Fork me on GitHub
#untangled
<
2017-04-03
>
cjmurphy00:04:51

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.

adambros05:04:33

@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)

adambros05:04:24

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

adambros05:04:44

which btw is something im tinkering with

cjmurphy05:04:12

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 😜

adambros05:04:27

it does feel like it could get really productive

adambros05:04:21

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

tony.kay05:04:37

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.kay05:04:44

With management between a pristine and dirty state

cjmurphy05:04:00

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.kay05:04:00

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

tony.kay05:04:53

Also easy to make click-to-edit things

cjmurphy06:04:58

@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.

urbank08:04:50

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.

gardnervickers12:04:37

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

gardnervickers13:04:18

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.

urbank14:04:45

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

urbank14:04:20

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

urbank14:04:46

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?

urbank14:04:41

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

tony.kay16:04:02

@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.kay16:04:42

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.kay16:04:52

and control it from multiple places

tony.kay16:04:02

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

tony.kay16:04:52

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.kay16:04:13

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.

urbank16:04:10

@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.kay16:04:39

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

tony.kay16:04:55

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

tony.kay16:04:14

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

urbank16:04:50

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

urbank16:04:51

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

urbank16:04:30

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

urbank16:04:33

if that makes sense

tony.kay16:04:26

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.kay16:04:52

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

urbank16:04:20

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

urbank16:04:52

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

urbank16:04:38

Or just one Person to make it simpler

urbank16:04:02

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

urbank16:04:03

Then there are two components PersonViewAlpha and PersonViewBeta

urbank16:04:09

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

urbank16:04:52

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

urbank16:04:17

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

urbank16:04:21

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.

urbank16:04:27

Does this make any sense?

tony.kay16:04:57

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

tony.kay16:04:25

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

tony.kay16:04:51

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

tony.kay16:04:59

nor should any of the other facts about person A.

tony.kay16:04:13

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

tony.kay16:04:53

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.kay16:04:56

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

tony.kay16:04:15

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.kay16:04:32

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

tony.kay16:04:39

the two should not be complected

tony.kay16:04:30

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.kay16:04:39

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

tony.kay16:04:47

I didn't think about that possible misunderstanding

tony.kay16:04:11

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.kay16:04:21

for the form field

tony.kay16:04:33

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

tony.kay16:04:25

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.kay16:04:18

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".

urbank16:04:00

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

tony.kay16:04:27

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

urbank17:04:05

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

urbank17:04:47

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

urbank17:04:31

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

urbank17:04:51

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)?

urbank17:04:25

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

tony.kay17:04:10

Yes, you're understanding correctly

tony.kay17:04:41

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.kay17:04:20

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.kay17:04:40

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

tony.kay17:04:07

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.kay17:04:24

and now we're back to why form support exists

tony.kay17:04:01

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.kay17:04:43

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.kay17:04:55

this implies a copy

tony.kay17:04:13

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

tony.kay17:04:47

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

urbank17:04:26

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

wilkerlucio18:04:59

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

jonystorm23:04:36

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

adambros23:04:35

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

jonystorm23:04:55

The url for my list is the url for support is

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.kay23:04:56

@jonystorm wrong console

tony.kay23:04:05

look at the server console for the support request ID

tony.kay23:04:14

not the browser console

tony.kay23:04:23

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

tony.kay23:04:43

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

tony.kay23:04:04

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

tony.kay23:04:33

we should fix that error message

tony.kay23:04:34

oh, ok. Yeah, that should work

tony.kay23:04:49

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