untangled

NEW CHANNEL: #fulcro
cjmurphy 2017-04-03T00:59:51.420798Z

If you were to write a web application from scratch, would you just use the new forms way? i.e. just no reason not to - a higher/better abstraction is always a good thing.

adambros 2017-04-03T05:26:33.856969Z

@cjmurphy if you can model it as a form, i cant see a reason not to, but that’s a reasonably sized if also better is subjective, and higher isnt necessarily better (not that i disagree wrt untangled forms)

adambros 2017-04-03T05:27:24.861385Z

although the more we use it, the more we can justify work towards improving it especially if we get an db adapter layer for the commit, plug-n-play with server persistance is going to be pretty nice

adambros 2017-04-03T05:29:44.873614Z

which btw is something im tinkering with

cjmurphy 2017-04-03T05:41:12.938897Z

If the application has some idea of a user committing work, then it ought to be modelled as a form?? The whole web idea of what a form is is something that I don't fully understand I don't think. I wouldn't know whether to use a form or not if was writing a 'raw' web application. In the old days (pre-web, pre-Java, pre-Delphi, even-pre-Windows) there was a brief period when a huge number of applications used to be Oracle Forms applications. I think of a 'form' in that context - something ubiquitous to be used for programming applications, now SPAs. It was quite a productive way of programming. Ranting a bit 😜

adambros 2017-04-03T05:44:27.957296Z

it does feel like it could get really productive

adambros 2017-04-03T05:46:21.968126Z

and it feels validating (pun intended) that at one point developing with forms felt productive

tony.kay 2017-04-03T05:47:37.974922Z

The form support is a good match any time you have one or more persisted entities you'd like to allow a user to edit. But it is centered on the idea that the data will be presented in form entry fields

tony.kay 2017-04-03T05:48:44.981193Z

With management between a pristine and dirty state

cjmurphy 2017-04-03T05:52:00.000533Z

I guess my thinking is that's there's no difference between a form entry field and any other field - always use form entry fields even when only displaying. The difference doesn't make much sense - its a 'web thing'.

tony.kay 2017-04-03T05:52:00.000552Z

But the presentation is customizable and also easily generated...so you could end up with forms-based dev on certain types of apps

tony.kay 2017-04-03T05:52:53.006047Z

Also easy to make click-to-edit things

cjmurphy 2017-04-03T06:04:58.082908Z

@adambros: some of the reasons was productive: no mouse, no layout problems, no CSS (of course). There was field and record (i.e. record on the screen) block and whole form validation events (known as triggers). But not just validation, also triggers for leaving fields and that sort of thing - your whole app logic existed in well thought out triggers. So when the mouse came along these triggers could have (??) abstracted out (i.e. application programmer s/not have to care) things like whether the user is using the mouse or the keyboard to visit fields.

urbank 2017-04-03T08:02:50.164248Z

So an idea... just of the top of my head. Would it be a good idea to have a mechanism where if an ident could not be resolved (the entity is not longer in the table), the ident would just be deleted. So if an ident that doesn't have an entry in a table is encountered, it gets removed.

gardnervickers 2017-04-03T12:57:37.879985Z

@urbank you could just walk through the app db and drop idents you encounter, kinda like how the tempid resolution function works.

gardnervickers 2017-04-03T13:00:18.925524Z

I think adding in a global GC type utility for cleaning up old idents is a bit overkill though, at least for our case. Especially considering the create function for an entity usually lays out all the paths a new ident gets placed, it's trivial to make that a constant def and just do the reverse in the delete mutation.

urbank 2017-04-03T14:46:45.086827Z

@gardnervickers Right, I suppose it's usually overkill.

urbank 2017-04-03T14:55:20.284129Z

Something else. Take the calendar component in untangled-ui. It has a representation in the app-db. So that works great in a case like this

urbank 2017-04-03T14:56:46.318073Z

But what if start and end date can actually be edited from multiple parts of the UI (kind of silly for this example, but you get the point)? Then :start-date and :end-date can no longer be just idents pointing to calendar components, correct?

urbank 2017-04-03T14:57:41.339187Z

Because they aren't solely represented by the calendar components, but just edited by them

tony.kay 2017-04-03T16:06:02.906209Z

@urbank The model won't really support that. The query engine would have to be changed to side-effect, and that isn't what you want as overhead while trying to process a UI refresh. Your mutations should keep state clean...in fact, you should more be focused on removing and adding idents. The tables can be easily GC'd based on a graph analysis...if you run into a case where it really matters. That is where your space is consumed anyhow. Dangling idents should be considered errors in your program logic, since you've basically created a "bad pointer" in the graph.

tony.kay 2017-04-03T16:06:42.920989Z

and your calendar example makes no sense to me. It is normalized data. Of course you can point to a calendar from more than one place. That is intentional and desired.

tony.kay 2017-04-03T16:06:52.924364Z

and control it from multiple places

tony.kay 2017-04-03T16:07:02.928169Z

but I would never expect you to delete a calendar component from app state

tony.kay 2017-04-03T16:07:52.946311Z

So, separate your logic from UI bits that should probably remain in app state from "entities" that have a persisted lifetime on a server. Those are the ones you care about...and yes, Untangled makes you do a bit more "view creation" in the sense of creating multiple pointers if you have multiple views.

tony.kay 2017-04-03T16:09:13.974300Z

This is an intentional trade-off. If you want to purge an item from the DB, then you app logic needs to be well-constructed around that entity...but that is all local reasoning (in the sense that you only have to reason about it in the places it is used). For example, your navigation mutations should be dealing with data lifecycle as you move from place to place in the UI. It is your data.

urbank 2017-04-03T16:15:10.097345Z

@tony.kay Yes, I have gotten hung up on the view creation a couple of times, but when I think about it the trade-off does make sense. For one, as you've previously stated, computing everything on every render has the potential of tanking the performance, at which point you basically have to do what untangled has you do anyway.

tony.kay 2017-04-03T16:16:39.128651Z

perhaps I don't get your point. I cannot use the same instance for both start and end...that's true

tony.kay 2017-04-03T16:16:55.133960Z

and if I reuse one somewhere else, it'll still have the last state, which may be desirable

tony.kay 2017-04-03T16:17:14.140244Z

if it isn't desirable, then I use a diff instance

urbank 2017-04-03T16:17:50.152819Z

So I wasn't talking about the calendar being pointed to from different parts of the app...

urbank 2017-04-03T16:18:51.173950Z

But rather that the calendar is only one view on some date

urbank 2017-04-03T16:19:30.187546Z

So the calendar isn't being pointed to from different places in the app, but the value that the calendar is editing is being edited by other components

urbank 2017-04-03T16:19:33.188924Z

if that makes sense

tony.kay 2017-04-03T16:21:26.227992Z

when I say "if used from somewhere else" I am thinking in terms of any UI that makes a read/write interface to that instance in the db. Could either be a component (that shared calendar's ident) or a component that uses a link query to "pull" the component from a table entry [{[:calendar/by-id :end-date] ['*]}]

tony.kay 2017-04-03T16:21:52.237049Z

I guess I'm not seeing the problem you're trying to point out

urbank 2017-04-03T16:23:20.266795Z

Could be because it's not actually a problem, and I'm just missing something πŸ™‚ I'll try to rephrase

urbank 2017-04-03T16:24:52.298160Z

So let's say the app-db stores a bunch of Persons

urbank 2017-04-03T16:25:38.314Z

Or just one Person to make it simpler

urbank 2017-04-03T16:26:02.322347Z

this Person has a :person/date-of-birth field

urbank 2017-04-03T16:27:03.343168Z

Then there are two components PersonViewAlpha and PersonViewBeta

urbank 2017-04-03T16:28:09.365528Z

They both display the Person entity from the database, but one uses CalendarAlpha and the other uses CalendarBeta

urbank 2017-04-03T16:28:52.380276Z

Both CalendarAlpha and CalendarBeta conceptually modify the same value :person/date-of-birth

urbank 2017-04-03T16:31:17.431125Z

But they also each have a representation in the app-db in separate tables. Person also has a separate representation. If there were only one Calendar for the Person the value of :person/date-of-birth could just be an ident pointing to that Calendar's data in the table

urbank 2017-04-03T16:33:21.473581Z

As is though, as far as I can see, updating :date-of-birth of a person involves also updating the value of CalendarAlpha and CalendarBeta.

urbank 2017-04-03T16:33:27.475437Z

Does this make any sense?

tony.kay 2017-04-03T16:38:57.585370Z

No...why would you have different representations of the same person in two different tables? Share the ident.

tony.kay 2017-04-03T16:39:25.594694Z

two views of the same data is most of the reason for the existence of a graph db format

tony.kay 2017-04-03T16:39:51.603067Z

date-of-birth for person A is a singular fact. It should never ever be a duplicate thing in your database

tony.kay 2017-04-03T16:39:59.605871Z

nor should any of the other facts about person A.

tony.kay 2017-04-03T16:40:13.610366Z

The idents are how you create views of that same data in different places in the UI

tony.kay 2017-04-03T16:40:35.618394Z

BUT

tony.kay 2017-04-03T16:40:53.624031Z

I think I do see your point: I've got a calendar with state, and you're wanting to overlay the entity on person

tony.kay 2017-04-03T16:40:56.625216Z

No...don't do that πŸ™‚

tony.kay 2017-04-03T16:41:15.631133Z

calendar has a callback for telling you when the date changes. It's internal date should not be used for a bday in an entity

tony.kay 2017-04-03T16:41:32.636982Z

you should hook up a callback to calendar, get the value when it is selected, and then transact it into person

tony.kay 2017-04-03T16:41:39.639184Z

the two should not be complected

tony.kay 2017-04-03T16:42:30.656096Z

Calendar should be treated as an opaque component that provides a date through a callback, not that represents a date in a person entity

tony.kay 2017-04-03T16:42:39.659277Z

That's a great questions, and it should be clearer in the docs

tony.kay 2017-04-03T16:42:47.661700Z

I didn't think about that possible misunderstanding

tony.kay 2017-04-03T16:43:11.669610Z

Also, the forms support makes it even more interesting...because you might choose to render a date as a calendar in an opaque way

tony.kay 2017-04-03T16:43:21.672884Z

for the form field

tony.kay 2017-04-03T16:43:33.677093Z

but that form field would need to update the date field in another entity

tony.kay 2017-04-03T16:45:25.713974Z

such a custom date field support would require you to either use component local state, or manually compose in the calendar widget for use by the calendar rendering...though the form support for updating could be linked behind the scenes....you'd still have to make the user add a query for the calendar on the form that was allowing that custom editor for the date. Composition can get interesting in this way when you're trying to keep things clean

tony.kay 2017-04-03T16:49:18.791397Z

I definitely need to make a video or something about composition when you have a container that is acting sort of like a pass-through for opaque children that are doing something more complex. Another good example is something like a tabbed interface, where you might want a component that controls tabs as a stand-alone component, but which cannot possibly know (in advance) the queries of the component that will appear in the tabs. This is another case where you would not model it in Untangled in that way (the tabs would not be a parent in the UI). Instead, the tab bar itself would be a sibling component that has a callback to tell you what tab is selected, and your "router" component would act on that to switch between your UI elements in the "pane".

urbank 2017-04-03T16:55:00.907258Z

Looking forward to the the video or something πŸ™‚

tony.kay 2017-04-03T16:58:27.979694Z

not sure when I'll have time, but hopefully that is clear. Feel free to ask questions

urbank 2017-04-03T17:00:05.014405Z

Aha, I see now about the calendar... it's suppose to modify the entity by the callback. However I'm still unclear on how it solves the particular issue with two views of the Person. For example

urbank 2017-04-03T17:01:47.052296Z

So one calendar per :person/date-of-birth is now solved... you just use the callback, and the opaque data of the calendar is always in sync with Person

urbank 2017-04-03T17:03:31.089127Z

But then you add another component that also edits :person/date-of-birth,

urbank 2017-04-03T17:04:51.116091Z

now when this component modifies the :person/date-of-birth via callback, don't you also have to update the calendar (the first component which was editing Person)?

urbank 2017-04-03T17:05:25.128027Z

otherwise, when you switch back to the calendar, it will display the previous value of :person/date-of-birth

tony.kay 2017-04-03T17:09:10.202130Z

Yes, you're understanding correctly

tony.kay 2017-04-03T17:09:41.212794Z

and yes, if you choose to use a different component that will need the date, then you'll have to push the date into it, which kinda sucks from a normalization standpoint.

tony.kay 2017-04-03T17:10:20.225525Z

for that matter, when you "click" to edit the date, that is the point at which the calendar would need to have its date set.

tony.kay 2017-04-03T17:10:40.232249Z

since the calendar has "what date is being edited" and is different from the storage of the bday itself.

tony.kay 2017-04-03T17:11:07.240768Z

But this IS what you want, since someone fiddling with the calendar (but not hitting save, cancel...for example) should not have changed the bday

tony.kay 2017-04-03T17:11:24.246561Z

and now we're back to why form support exists

tony.kay 2017-04-03T17:12:01.259210Z

you want separation of state: the state that is current, and the state that is pristine...and an ability to control the lifecycle of the edit itself

tony.kay 2017-04-03T17:12:43.273143Z

Being able to decide: as they edit fields, I send to the server...or, once the whole form is changed and they click save. Both valid...and you need to know what it was (to undo) and what it will be (to commit)

tony.kay 2017-04-03T17:12:55.277123Z

this implies a copy

tony.kay 2017-04-03T17:13:13.282674Z

without form support, you're in this circumstance of managing the copy through minutiae

tony.kay 2017-04-03T17:13:47.294556Z

which is exactly the problem you have in mutable javascript or any other way of doing it. Editing makes you want to have copies πŸ™‚

urbank 2017-04-03T17:17:26.366810Z

Right, makes sense. I'll have to think about it a bit. You've been most helpful, as always πŸ™‚

tony.kay 2017-04-03T17:18:56.397150Z

welcome

wilkerlucio 2017-04-03T18:47:59.229843Z

hello people, I just published a new article on how to write an intermediate/advanced parser for Om.next, with the stuff I have learned so far in this new world, I hope it can help some of you: https://medium.com/@wilkerlucio/implementing-custom-om-next-parsers-f20ca6db1664

1
jonystorm 2017-04-03T23:49:36.839461Z

Hi, I’m new with om.next and untangled.. I was following the tutorial on https://github.com/untangled-web/untangled-todomvc I get to get it all up and running until the point of sending a support request, but when I go to the support.html with the id provided on console I get this error Uncaught Error: :support-request is not ISeqable I’m running it JVM_OPTS="-Ddev -Dtest -Dsupport" lein run -m clojure.main script/figwheel.cljtrying different configs but I keep getting this error

adambros 2017-04-03T23:52:35.865723Z

@jonystorm what’s the url you are hitting? and can you give the full stacktrace?

jonystorm 2017-04-03T23:53:55.877471Z

The url for my list is <http://localhost:3000/dev.html?list=MyList> the url for support is <http://localhost:3000/support.html?id=1001>

core.cljs:1120 Uncaught Error: :support-request is not ISeqable
    at Object.cljs$core$seq [as seq] (core.cljs:1120)
    at Object.cljs$core$first [as first] (core.cljs:1129)
    at cljs$core$ffirst (core.cljs:1640)
    at untangled$client$impl$data_fetch$data_query_key (data_fetch.cljs?rel=1491257248840:285)
    at untangled$client$impl$data_fetch$data_path (data_fetch.cljs?rel=1491257248840:291)
    at data_fetch.cljs?rel=1491257248840:48
    at data_fetch.cljs?rel=1491257248840:52
    at core.cljs:4265
    at Function.cljs.core.swap_BANG_.cljs$core$IFn$_invoke$arity$2 (core.cljs:4265)
    at cljs$core$swap_BANG_ (core.cljs:4258)
This is all the error I have

tony.kay 2017-04-03T23:53:56.877642Z

@jonystorm wrong console

tony.kay 2017-04-03T23:54:05.878958Z

look at the server console for the support request ID

tony.kay 2017-04-03T23:54:14.880067Z

not the browser console

tony.kay 2017-04-03T23:54:23.881569Z

we need to make that bold/underlined/caps in the docs 😜

tony.kay 2017-04-03T23:56:43.902135Z

Make sure you don't restart the server between making and using the request. The implementation caches them in RAM for the demo

tony.kay 2017-04-03T23:57:04.905240Z

that error is a bad error...it basically means it didn't find the request you supplied

tony.kay 2017-04-03T23:57:33.909610Z

we should fix that error message

tony.kay 2017-04-03T23:58:34.918817Z

oh, ok. Yeah, that should work

tony.kay 2017-04-03T23:59:49.930032Z

on the develop branch or master? The develop branch has had some recent code cleanup, and isn't officially released. I doubt it is broken, but if you can't get it to work, you might try the master branch