Fork me on GitHub
#om
<
2017-02-08
>
devth03:02:28

trying to force a component to re-render/read (remotely) from a sibling component that mutates. i know a mutation should include read keys of affected components: > The second argument is a query expression that includes mutation. The query expression should contain any additional keys which should be re-read following the mutations (e.g. :todos/list). These reads will schedule components which depend on the specified keys to re-render. the problem is: nothing happens. i've tried all sorts of combinations of queries. does it need to be the rooted absolute query? relative query? single key of a query?

baptiste-from-paris10:02:13

hey guys, little feedback welcome, at what point do you use db->tree to write your read function ?

baptiste-from-paris12:02:04

another one =>

(dom/input
               #js {:type "text"})
(dom/button
               #js {:onClick (fn [e]
                               (om/transact! this `[(add/person {:id ~id})]))}
               "Add Person")
how do I know in om/transact! the content of the input button ?

danielstockton12:02:19

You should pass it through as a parameter in the transact call.

cjmurphy12:02:34

And if the content of your button can change, then it should be in your app's state, and your add/person mutation has access to all the state.

baptiste-from-paris13:02:08

@danielstockton how to do it ? the use case is : 1) I enter some text in my input 2) I click on the button to submit the content

baptiste-from-paris13:02:58

from what I understand I have to get an onChange handler on my text input which modifies the app-state then once I click on my button, I pass the value to my mutation method

danielstockton13:02:19

Actually, you don't need to pass it through if you know the key on state that you're reading to get the value. The add/person parser method will be passed the app-state. You just need the input to transact values onto the app-state onChange. There is another way, which is to have the input update component local state and then pass that through in the transact! Using app-state might be better, for time travelling purposes...

baptiste-from-paris13:02:14

the update-state!has been made for local state right ?

danielstockton13:02:11

You can use a link (https://github.com/omcljs/om/wiki/Thinking-With-Links!) in this component query to get the current input value. Yep, update-state! is for local state.

baptiste-from-paris13:02:27

so why using componentDidUpdate to set local state instead of the ILocalState in the todoMVC of David Nolen

danielstockton13:02:11

ILocalState is for initializing state, componentDidUpdate is a hook that is run after updating props (not called on iniital render).

danielstockton13:02:14

Nothing specific to om, they're standard react lifecycle methods.

baptiste-from-paris14:02:41

is there any example of using ILocalState

danielstockton14:02:45

Object
(initLocalState [this]
   {:count 1})
This is actually how I init state.

baptiste-from-paris14:02:03

you don’t implement the protocol .

danielstockton14:02:39

There is: https://github.com/omcljs/om/blob/master/src/main/om/next.cljc#L645 but it's a protocol on the component, defui takes care of this for you.

baptiste-from-paris14:02:25

it’s all new for me...

baptiste-from-paris14:02:21

I still have this but i’ll take care of it later

Constructor is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component

danielstockton14:02:29

It's new to most people at the moment, that's why I keep looking things up in the source.

danielstockton14:02:51

To fix that, you need to set a :value on the input.

danielstockton14:02:05

Probably an empty "" at first and then it will read whatever you're setting on app-state or local-state.

baptiste-from-paris16:02:02

another question ^^ I have got my normalized data which looks like this =>

{:list/one [[:people/by-id 1] [:people/by-id 2] [:people/by-id 3]],
 :list/two [[:people/by-id 2] [:people/by-id 5] [:people/by-id 6]],
 :people/by-id {1 {:id 1, :name "John", :points 0},
                2 {:id 2, :name "Mary", :points 0, :age 27},
                3 {:id 3, :name "Bob", :points 0},
                5 {:id 5, :name "Gwen", :points 0},
                6 {:id 6, :name "Jeff", :points 0}},
 :om.next/tables #{:people/by-id}}
If I want to create a component that displays all people with only their name, I created this component
(defui PersonList
  static om/IQuery
  (query [this]
    '[{[:people/by-id _] [:name]}]))
Problem : query does not work with _ wildcard but it actually work if I do this =>
(defui PersonList
  static om/IQuery
  (query [this]
    '[{[:people/by-id 1] [:name]}]))

baptiste-from-paris16:02:39

can’t figure out how to do it

tmulvaney16:02:22

@baptiste-from-paris the likely reason the latter works and the former doesn't is you have a default read method which does something like (get-in state key)

baptiste-from-paris16:02:18

it makes sense, I don’t know how om is supposed to know that I want all the nested elements wihtout giving him a key 1 or 5

baptiste-from-paris17:02:25

I juste wanted to know if it was possible

tmulvaney17:02:46

yeah you'd have to write a custom read function for that

baptiste-from-paris17:02:21

I know how to do it with the read function but I hoped to do it with the db->tree helper

tyler17:02:45

Has anyone ever run into an issue with om.next and figwheel where clojurescript will compile the first time through when including [om.next :as om] but if figwheel is restarted compiliation will fail with a null pointer exception and the only way to resolve the issue is to stop figwheel, delete the public/js folder and start figwheel again?

baptiste-from-paris17:02:21

have you tried lein clean ?

peeja17:02:19

@baptiste-from-paris _ isn't a wildcard, so that's not going to do what you're trying to do

peeja17:02:21

When db->tree reads [:foo 1], it reads (get-in state [:foo 1])

peeja17:02:43

When db->tree reads [:foo _], it reads (get state :foo)

baptiste-from-paris17:02:19

right I find that but does it means that my query suxxx ?

peeja17:02:45

I wouldn't use those words…but yes. 🙂

baptiste-from-paris17:02:06

is it possible anyway ?

peeja17:02:28

There isn't a built-in way to get "every person that's in the app state", because that's normally not useful

peeja17:02:33

Which people do you want?

peeja17:02:46

Your query should express your complete intention

peeja17:02:25

If you really want "all people in the system", you might define a key that means that, such as :all-people, or maybe :app/all-people

baptiste-from-paris17:02:42

ok, that’s what I thought

baptiste-from-paris17:02:02

but it’s a duplicate of my people/by-id

peeja17:02:14

If your app is backed by a server, "all people" may include people your app state doesn't know about

peeja17:02:25

and you want to make sure you ask the server about it

peeja17:02:44

Note, though: you don't want to add a new key to your app state to do this

baptiste-from-paris17:02:54

it’s for learning purpose 😉

peeja17:02:56

Because, as you've just said, that would just be duplicate data

peeja17:02:29

(Right, but conceptually, that's why asking about all the things that happen to be in the local app state isn't a common thing to do)

peeja17:02:46

You want to create a custom read for :app/all-people

baptiste-from-paris17:02:40

ok, let’s try it

peeja17:02:08

Something like:

(defmethod read :app/all-people
  [{:keys [state] :as env} key params]
  (let [st @state]
    (vals (get st :app/all-people))))

baptiste-from-paris17:02:22

so in my case (without a backend) I still have to duplicate data by hand right ?

peeja17:02:27

No, not at all!

peeja17:02:31

Just put that in your parser

peeja17:02:31

Then your query becomes [{[:app/all-people _] [:name]}]

peeja17:02:46

Oh, actually, I've done that a bit wrong

peeja17:02:13

That implementation will give you all the people, but it doesn't apply the subquery to each person

peeja17:02:30

I think it'll actually work in this case, though

baptiste-from-paris17:02:00

yep I’ve tried this one and it’s working with one little hint

baptiste-from-paris17:02:43

when I update on person rendering only applied on my Person component and PersonList is not rendered

baptiste-from-paris17:02:09

but it’s ok, thank’s for you help, this community is amazing 🙂!

peeja17:02:06

Off the top of my head, it should be more like:

(defmethod read :app/all-people
  [{:keys [state query] :as env} key params]
  (let [st @state]
    (map
     #(om/db->tree query % st)
     (vals (get st :app/all-people)))))
so you apply the joined query (`query`) to each person you find with db->tree.

peeja17:02:18

Happy to help! 😄

peeja17:02:24

Good luck!

peeja17:02:59

Question of my own, speaking of things like :person/by-id

peeja17:02:43

Is there a particular philosophy behind Om using idents like [:person/by-id 1] and not [:person/id 1], which would match Datomic lookup refs?

peeja17:02:52

The major reason that comes to mind is that the app state wouldn't match the spec for :person/id, because you'd have :person/id mapped to a map. But that doesn't feel like a terribly strong reason to me.

peeja18:02:09

I ask because my team is working on something Om-y and Datomic-y with Postgres, and considering using the equivalent of :person/id for the analog of this, and I wonder what the room thinks

baptiste-from-paris18:02:43

I really don’t know but I guess it’s more of a documentation thing; all docs have been made with person/by-*

baptiste-from-paris18:02:10

but that’s just a guess

baptiste-from-paris18:02:36

*a newbie point o view ^^

peeja18:02:31

Yeah, I haven't questioned it until now

anmonteiro18:02:31

@peeja there have been questions like that for a while, my take on it is that it was just David's choice when doing the tutorials that people took on because everything was so new to them

anmonteiro18:02:47

I don't think it has any semantics to it, and has made some people confused

anmonteiro18:02:04

so feel free to use whatever naming scheme you want

peeja18:02:08

Makes sense. Thanks!

matthavener18:02:33

has anyone run into an issue where importing om.next in the JVM and then building an om.next based project with cljsbuild.api with :advanced optimzations causes issues with static protocol implementations? Specifically, (om.next/iquery? SomeComponent) returns false. It looks like the closure compiler elides some of the internal protocol data on the SomeComponent class object

matthavener18:02:51

(not sure if I’m using the correct terms for cljs compiler internals)

anmonteiro18:02:50

@matthavener yes, Om Next 1.0.0-alpha47 requires you be on ClojureScript 1.9.293 at least

matthavener18:02:30

@anmonteiro yep, this is on alpha47 with 1.9.239

anmonteiro18:02:09

sorry do you mean 1.9.293? because there's also 1.9.229 and that's confusing

matthavener18:02:41

oh sorry, yes, 1.9.293 🙂

anmonteiro18:02:18

it doesn't make any sense to me what you're getting then

anmonteiro18:02:25

did you clean your build?

matthavener18:02:44

yeah, I’ll try to figure out a testcase

anmonteiro18:02:47

if you're still seeing issues after cleaning your build and rebuilding the project, can you put together a minimal repro I can look at?

anmonteiro20:02:11

check1  true
testcase.js:674 check2  true

matthavener20:02:53

hrm, must be something in my environment, it failed for @cmcfarlen as well

matthavener20:02:06

what java version are you using?

anmonteiro20:02:33

java version "1.8.0_112"

anmonteiro20:02:53

just looks like a caching issue

anmonteiro20:02:57

I don't know

matthavener20:02:35

well, we first saw the issue running builds inside docker.. which should be pretty clean 😕

anmonteiro20:02:42

I would say Cursive has some issues around caching too much, but I think @cmcfarlen uses Vim

anmonteiro20:02:16

right, and Docker should be pretty clean

anmonteiro20:02:30

your example didn't work right away for me, btw

anmonteiro20:02:02

I had to tweak user.clj

anmonteiro20:02:27

(removed the om.next require)

matthavener20:02:35

ah, that’s the line that breaks it 🙂

matthavener20:02:03

requiring om.next into the JVM before running cljsbuild/build is what triggers the issue

matthavener20:02:15

I definitely get “true true” if I remove the om.next require

anmonteiro20:02:20

right but I was having a completely different symptom

anmonteiro20:02:26

so I know what the issue is then

anmonteiro20:02:38

we use clojure.main/demunge but never require clojure.main

matthavener21:02:40

over my head, is clojure.main not implicitly required by the lang itself?

anmonteiro21:02:55

I don't know

matthavener21:02:38

well, I updated the repo with clojure.main for other’s convenience

anmonteiro21:02:47

@matthavener I can definitely repro your issue now

anmonteiro21:02:00

clojure.main stuff apart

matthavener21:02:11

sweet! It literally took me 2 days to minify so I was happy to get it small enough 😛

anmonteiro21:02:17

this is going to be hard to fix

anmonteiro21:02:29

my suggestion is you change your build step?

anmonteiro21:02:34

if that's something you can do

matthavener21:02:04

yeah, we may have to

matthavener21:02:42

its not blocking us anymore, I just needed ast->expr which I grabbed from om.next.impl.parser instead

anmonteiro21:02:09

@matthavener the problem seems to be the dynamic require

anmonteiro21:02:28

or not even that, hrm

matthavener21:02:50

yeah, I’m not sure. I know if its AOT’d then it seems OK

anmonteiro21:02:07

that makes sense

anmonteiro21:02:41

so the problem is how we make statics work in advanced compilation with the Google Closure Compiler

anmonteiro21:02:08

we need to replace those internal cljs.core functions before compiling anything Om Next

anmonteiro21:02:18

it's basically an ugly hack

anmonteiro21:02:33

and just like every ugly hack, it comes to bite you when you least expect 🙂

anmonteiro21:02:00

in this case, bite me, because I put it there

matthavener21:02:19

because its a dynamic require its not properly hooking into cljs.core early enough? or hooking in before it should?

anmonteiro21:02:31

something like that

anmonteiro21:02:20

but it shouldn't be a problem for most people

anmonteiro21:02:41

and you can work around it too

cmcfarlen21:02:51

We ran into it because we are running the cljs compiler from the same jvm as our server app

cmcfarlen21:02:57

Its all my fault

anmonteiro21:02:51

so the way you fix it is you guarantee that cljs.build.api is required before om.next

anmonteiro21:02:27

which may not be possible

matthavener21:02:00

we can totally do that

anmonteiro21:02:05

hrm nope, even that doesn't fix it for me here

anmonteiro21:02:08

but you can always try

cmcfarlen21:02:22

In our case, cljs.build.api is required before om.next

cmcfarlen21:02:32

But we can require om.next after running the build step

matthavener21:02:18

yeah, that should be possible

anmonteiro21:02:29

meanwhile there were some situations where clojure.main wasn't loaded somehow

cmcfarlen21:02:10

@anmonteiro The initial symptom of this problem was our compassus Wrapper class was treated as not having IQuery, so the wrapped factory wasn't in computed, but in props. Its feels strange that the factory is obtained differently in the Wrapper depending on if you have a query or not. I understand why it is so, but it is a bit jarring.

anmonteiro21:02:13

@cmcfarlen feel free to open an issue

anmonteiro21:02:18

we can probably change that behavior

anmonteiro21:02:02

might be hard if the wrapper component transacts

anmonteiro21:02:23

then factory won't be there

anmonteiro21:02:32

just something to think about

cmcfarlen21:02:10

I was thinking always have them in computed. I'll open an issue and we can discuss there

anmonteiro21:02:41

or having them in both places if not iquery?

anmonteiro21:02:03

^ not a breaking change, and it'll be in computed anyway

cmcfarlen21:02:20

yeah, that would work backward too