Fork me on GitHub
#om
<
2016-05-12
>
dimiter00:05:19

Figured it out

(defn test-ajax [state owner input]
  (go
    (let [access-token (get-in state state/user-access-token-path)
          api-result (<! (ajax/managed-ajax
                           :get
                           (api-url (str "/api/v3/users?per_page=5&search=" input))
                           :headers {"Authorization" (str "Bearer " access-token)}))]
      (om/update-state! owner :options (fn [v] (distinct (concat v (:resp api-result))))))))

mattyulrich02:05:23

Thanks to some help from @cjmurphy I was able to start to get some things together on this gist: https://gist.github.com/mattyulrich/8524ce8f20606e644770205021875639

mattyulrich02:05:52

I’m facing a new problem, when my data has a single element, the om/db->tree function appropriately pulls out the requisite element.

mattyulrich02:05:59

But when my data is a vector (eg. from gist - :classes or the :students data within the :selected-class), I end up with a vector of empty elements..

mattyulrich03:05:25

:selected-class resolves to {:class-id 1, :class-name Learning Om, :teacher {:user-id 456, :user-name Fred}, :students [{} {}]}

mattyulrich03:05:49

:classes resolves to [{:teacher {}, :students {}} {:teacher {}, :students {}}]

cjmurphy03:05:26

Not sure sure about:

(query [this]
    [(om/get-query App-Login-Button)])

mattyulrich03:05:45

Yeah - sorry, I did that to pass the :current-user straight from the App-Content (root):

(header-component current-user)

mattyulrich03:05:31

While those semantics may be funny, that bit seems to be working fine.

cjmurphy03:05:23

Also it doesn't look like everything is coming in from the root.

cjmurphy03:05:45

Every single query has to be in the root as a join.

cjmurphy03:05:47

(there is an exception to this - when a different component needs the same data, I believe).

cjmurphy03:05:12

Passing the props straight through is usually a bad idea:

(app-class-list-component (om/props this))

cjmurphy03:05:38

I know I was guilty of that one for a long time 😛

cjmurphy03:05:08

Another thing I do to stay out of trouble is put idents in everything. In the app state I just have a single number for say a panel or whatever.

cjmurphy03:05:31

I just looked through the Kanban demo and every single time a component has a query it also has an ident. So it is a good rule. Without idents Om Next won't know what to do with joins.

mattyulrich03:05:15

Ok.. that’s something worth investigating..

cjmurphy03:05:55

And you need joins to separate out your queries/components. It is obvious to do a join in 1:M case. But joins are good for 1:1 as well.

mattyulrich03:05:41

I did find that kanban demo very instructive; it certainly got me where I am now; thanks for that.

mattyulrich03:05:12

So - I just put some output from my REPL in a gist to demonstrate the specific issue: https://gist.github.com/mattyulrich/95cea7cbbf60520cbdc8e6a7ffcb9fd9

mattyulrich03:05:39

Do you think this type of result from the om/db->tree call at the end is likely due to missing idents and joins?

cjmurphy03:05:51

Is the problem that no students are coming out when there should be?

mattyulrich03:05:11

It resolves teacher just fine but not the students

mattyulrich03:05:26

But teacher is a single user, students is a vector of users

cjmurphy03:05:45

I don't have much experience of not using idents for everything, and not having state always in default db format.

cjmurphy03:05:07

I would guess that db->tree can work fine just on the data, as long as it is in default db format.

cjmurphy03:05:39

But idents used for getting it into default db format in the first place.

mattyulrich03:05:14

Ok, and that format is the one I get from the tree->db call; right?

cjmurphy03:05:52

Yes - but Om Next does that for you automatically usually.

mattyulrich03:05:26

Yeah - I’m not explicitly doing it in my code; I just did it at the REPL there to demonstrate how my current set of Idents/Queries is restructuring the initial app state.

cjmurphy03:05:10

Are there all idents in your state i.e. is it fully normalized? Perhaps it is because your state is all 'data' - your components are not also making it into the state.

cjmurphy03:05:28

Just looked - looks like it is in default db format just fine

mattyulrich03:05:56

Except for the three main root components, :current-user, :selected-class and :classes

mattyulrich03:05:18

Those refer to the idents.

mattyulrich03:05:23

I thought I may have been missing something because it seems like my read functions can get the data for the single idents just fine, but the vector of idents is coming up empty… I wasn’t sure if there was something special I needed to indicate in my query semantics to say “this should be a vector”..

mattyulrich03:05:55

But, maybe I should just try and be more rigorous with Idents and Joins as you describe and see what happens.

mattyulrich03:05:07

I’ll give that a shot and see if I can get more color on what’s going on.. Thanks again for the help with this, I’ve just been trying to get my head around how this all works - quite a learning curve on it.

cjmurphy03:05:16

I don't like fact that :students is not coming in from the root, as a join query to Class-User.

mattyulrich03:05:17

Meaning you think it should be there?

cjmurphy03:05:31

Without it there student data that is poured into the root will be ignored.

mattyulrich03:05:43

Ok… so in the root app query (App-Content) you think there should be a “:students” join leveraging the Class-User query...

cjmurphy03:05:20

Yes - for an application to work.

cjmurphy03:05:48

Not necessarily to solve your db->tree problem.

cjmurphy03:05:38

Also I would be doing @my-reconciler.

cjmurphy03:05:21

Well just have a my-reconciler that REPL, whatever can use.

cjmurphy03:05:19

Yeah you've got that already 🙂

mattyulrich03:05:42

Ok - lemme see if I can try cleaning this up a bit more; add more Idents and push these queries up to the root...

cjmurphy03:05:26

Yeah - be interesting to isolate the cause. I can't see it at the moment.

mattyulrich04:05:52

In general, when trying to use Om.next, I’m finding myself more and more structuring my UI to conform to my data…

cjmurphy04:05:35

Yes me too - data is more than just data!

mattyulrich04:05:45

yeah - I can sometimes see shadows of how this is powerful, but it often seems like data & ui acrobatics to just get something to show up...

mattyulrich04:05:06

I keep telling myself that I just don’t understand, or that it’s just some confusion in syntax or semantics - but the more I figure out, the more I feel like I don’t understand...

mattyulrich04:05:17

Anyway, this little example app emerged out of trying to simplify a bigger and more complicated app to better understand - as such, it has gone through evolutions of confusion and understanding; I’ll take some time to make it more readable and a smaller problem to see if I can work up from these smaller building blocks.

mattyulrich04:05:14

I called you out at the beginning of this just to give credit for getting me where I am now; didn’t mean to rope you into further assistance. Thanks again for taking a look and providing some guidance and suggestions.

cjmurphy04:05:05

Some feeling of disconnect - like just learn the rule and worry about understanding it more deeply later. A nice book would be good - but it is early days yet. I really like it as your app starts to get bigger and bigger. Was at a pause so a bit of roping in good 🙂

mattyulrich11:05:31

FYI - I just figured out the problem above; I was wrapping my initial app state in an atom explicitly rather than passing it to the reconciler as a value. Once I took out the atom, everything worked.

mattyulrich11:05:57

Does this mean that when the reconciler :state is passed as an atom, it assumes the data is already in the db format and doesn’t internally restructure it?

anmonteiro11:05:07

@mattyulrich: from Om Next’s documentation:

:state - the application state. If not an atom the reconciler will normalize the data with the query supplied by the root component.
https://github.com/omcljs/om/wiki/Documentation-%28om.next%29#reconciler-1

mattyulrich11:05:02

Ok - yeah, missed that bit when setting up my app. Thanks.

mattyulrich11:05:38

I’m trying to get a ref to an ident (from my select) - I see get-ident - given a component, return its ident; is there a way that given the ident value (ie. “2”) and the ident key, I can lookup the ref for that ident?

anmonteiro12:05:41

@mattyulrich: not sure what you mean by the ref, but there’s ref->component, where given an ident it returns the component that matches that ident?

mattyulrich12:05:26

I’m probably going about this all wrong; trying to wire up a dom/select - in the onChange event, I want to mutate some state and set the ident associated with the selected option. I can get the “value” of that option from the event, but that’s currently just a number, the ident value.

anmonteiro12:05:30

@mattyulrich: so you might not want to rely on the event information for that. can’t you pass the ident of the thing you’re mutating to the transact! call?

mattyulrich12:05:38

Maybe I haven’t seen the right example yet; not sure the best way to use a dom/select in om.next.

mattyulrich12:05:23

I guess my real question is: how do I get the ident of my option out of the select onChange event?

mattyulrich12:05:45

Or is there a better event to be using?

anmonteiro14:05:49

@mattyulrich: I’m not quite sure that you’re options need to have idents

anmonteiro14:05:56

in any case, get-ident is a thing

anmonteiro14:05:06

if you’re calling transact! inside the option component

mattyulrich14:05:25

Well, maybe I structured it wrong. I have a select with a list of classes (option value: class-id), when I select one I want to transact the current selected-class to the one chosen in the option.

mattyulrich14:05:37

I’ll try and clean up what I have and update my gist.

ethangracer18:05:41

does shouldComponentUpdate return true if only computed data changes? I’m having a hard time deciphering the macro, but it in my experience it returns false if props and state are the same while computed is different. wondering if that’s on purpose or not

hueyp18:05:57

I think computed is just a prop

hueyp18:05:04

so it should be part of sCU

hueyp18:05:08

but that could have changed

anmonteiro19:05:21

@dnolen: I’ve been digging into the problem with instrumentation

anmonteiro19:05:01

The solution I initially thought about can’t work

anmonteiro19:05:13

because it creates infinite loops

anmonteiro19:05:25

which is pretty much something you know already, and the reason behind build* in om.core

anmonteiro20:05:25

my thinking now is that we need some kind of factory function that can’t be intercepted by instrument

anmonteiro20:05:26

@dnolen: or introducing another option to factory, a boolean instrument value, which we call with false inside the instrumentation function, to prevent loops

anmonteiro20:05:38

@dnolen: I’ve got it working like that, I’ll submit a PR to see if you agree with the implementation choice

ethangracer20:05:25

@hueyp: computed is just a prop, but when you call om/props it doesn’t actually give you the computed map

ethangracer20:05:52

so if the macro is checking om/props as opposed to React props, it might be missing it

ethangracer20:05:02

which, looking back at the macro, is exactly what’s happening — a call to om/props, and a check that the props are an instance of OmProps

ethangracer20:05:04

I think this is a bug

ethangracer20:05:12

albeit a minor one

anmonteiro20:05:39

@ethangracer: I’m pretty sure that computed props weigh in on the sCU decision

anmonteiro20:05:30

I wonder what’s the particular use case where this is causing you problems

anmonteiro20:05:45

my understanding would be that: - if you’re passing callbacks they won’t really be changing - any other stuff might influence rendering, so it’s a good idea for it to be a part of the decision

ethangracer20:05:35

@anmonteiro: did this change as of alpha-33? I’m on 32, and I’m not seeing the reshape-map where shouldComponentUpdate was previously defined

ethangracer20:05:55

nor am I seeing the shouldUpdate? function you referenced on 1729

ethangracer21:05:46

and yes, the callbacks aren’t changing. what I’m currently doing is passing a search string down through components to help filter what components are shown — nothing else is changing, but I still need a re-render triggered. for now I just overrode the function in my defui and have it return true...

anmonteiro21:05:00

@ethangracer: I don’t understand your question

anmonteiro21:05:27

reshape-map is only ever seen by the JVM in macro expansion

ethangracer21:05:59

right, isn’t that were the defui’s shouldComponentUpdate is defined?

ethangracer21:05:06

I understand it’s a macro, but it gets expanded when defining the component in cljs, doesn’t it?

anmonteiro21:05:06

yea, what defui does is create a component constructor and attach those functions to its prototype

ethangracer21:05:42

and it seems that the definition of shouldComponentUpdate in there may be missing computed

ethangracer21:05:09

ok I found should-update? — even there, computed is pulled out before the call to should-update?, but is never passed anywhere

ethangracer21:05:53

(when (mounted? c)
              (let [computed   (get-computed (props c))
                    next-props (om.next/computed (ui->props env c) computed)]
                (when (should-update? c next-props (get-state c))
                  (if-not (nil? next-props)
                    (update-component! c next-props)
                    (.forceUpdate c)))))

ethangracer21:05:19

nevermind, I see it now

ethangracer21:05:33

I don’t know, it seems not to work

anmonteiro21:05:34

@ethangracer: Ah, I know what you mean

anmonteiro21:05:04

and it’s related to the OmProps check

ethangracer21:05:18

yeah, what is that?

anmonteiro21:05:38

nested components receive that deftype instance

anmonteiro21:05:41

the top level component doesn't

anmonteiro21:05:49

it’s just a wrapper for Om’s props

anmonteiro21:05:39

thing is computed should be in there too

ethangracer21:05:41

I would think so too. All I know is that on alpha-32, if you set up a parent component to update it’s state based on text being entered in a field, and then you pass that data through computed to a child component, the child only re-renders on the first change to computed. then it stops

anmonteiro21:05:05

@ethangracer: happy to look at a minimal case

ethangracer21:05:12

could definitely be that I’m doing something else, but figured I’d throw it out there because my testing indicates otherwise

ethangracer21:05:20

yeah let me work one up, i’ll send it your way

ethangracer21:05:26

@anmonteiro: changing computed does seem to trigger a re-render for a simple app, just not confident that I’m recreating the same scenario. I’ll keep playing around and let you know if I figure it out

mattyulrich22:05:18

At line 140, how could I get the ident of my option? Or, is there a better way to approach this whole select thing?

mattyulrich22:05:35

I guess the big problem is how to I move between the synthetic events of the om/dom components and the om components?

mattyulrich22:05:49

Or should I just eschew those events in favor of another approach?