Fork me on GitHub
#om
<
2016-03-23
>
adamkowalski00:03:22

well I guess I should explain what it is I am doing

adamkowalski00:03:35

I want to make a component that can manage a web socket connection for me

adamkowalski00:03:26

that means that all you need to do is give the component some props (url, and a core async channel) and in the componentWillMount section of the component I set everything up and in componentWillUnmount I tear everything down

adamkowalski00:03:59

then there is a go loop that waits for messages on that channel, uses transit to encode the message and sends it over the web socket

adamkowalski00:03:43

and finally when the server responds the component will add a new transaction with a payload that is the response from the server, and automatically decodes the transit message

adamkowalski00:03:37

that way you don’t need to worry about setting up or maintaining a web socket connection. if you just drop this component into the render function its all taken care of for you

anmonteiro00:03:20

@adamkowalski: you don't need to call a transaction

anmonteiro00:03:55

just call om/merge!

anmonteiro00:03:20

then you don't need a specific parser to be in place

adamkowalski00:03:57

I thought about that, but I ended up making it so as a prop you also pass in a name for the transaction

adamkowalski00:03:17

then when the transaction will occur it will transact under the specified name with the decoded message as the payload

adamkowalski00:03:49

but I don’t want to force somebody to do something specific with the response of the server. maybe they will mutate something, or maybe call another transaction, or maybe nothing at all

anmonteiro00:03:57

I'm not sure that's the way to go about it

adamkowalski00:03:37

well maybe I am going about this the wrong way

adamkowalski00:03:03

but Ideally what I would have are completely isolated components that you can just include into your project and not have to worry about how they work

adamkowalski00:03:45

that way I can start to build up a catalog of reusable components I can share between projects

anmonteiro00:03:47

that's why I'm saying you want to decouple that from a parser

adamkowalski00:03:48

if I use the om/merge! function that would assume that the server is always returning something you want to put into your atom app state right?

adamkowalski00:03:02

or am I misunderstanding what that function does

anmonteiro00:03:08

I suppose, yes

adamkowalski00:03:48

well, don’t most projects define a mutate multimethod that dispatches on the key?

adamkowalski00:03:56

so that way I could just say that a requirement is to add a (defmethod mutate ‘name-your-provided-to-props [{state :state} _ {message :message}] ;; do something )

adamkowalski00:03:31

and that way I can just make a transaction and have the user of the component figure out how to deal with responses

wei03:03:53

newbie question. how would set the :open? prop to true from an external event?

wei03:03:56

(defn alert
  [data owner]
  (reify

    om/IInitState
    (init-state
     [_]
     {:open? false})

    om/IRenderState
    (render-state
     [_ {:keys [open?]}]
     (when (om/get-state owner :open?)
       (dom/div #js {:className "alert alert-info alert-bar"}
                (dom/span #js {} "message"))))))

cjmurphy05:03:08

@wei: sorry can't answer as that's Om Previous code. If it was Om Next - then you change app state with mutations by calling transact!, or possibly merge!.

cjmurphy05:03:35

My question. Here this is how you create the component: (def thing (om/factory Thing)), where Thing is a defui. Was that last sentence correct? Then how come (om/component? (thing)) is returning false?

cjmurphy05:03:10

Perhaps because I'm not giving it props? - no - giving it props doesn't help me. Next thought - maybe it needs to be mounted?

adamkowalski05:03:20

om/component? would return true if it was called inside of a defui component

cjmurphy05:03:42

So only works for this?

adamkowalski05:03:43

(defui MyComponent Object (render [this] (.log js/console (om/component? this))))

cjmurphy05:03:30

I was trying to create a component from another component.

adamkowalski05:03:51

care to elaborate

cjmurphy05:03:45

I've got a component which I want to be local state only, and am creating it the way normally created, from the parent. In the parent is where the button is that will add local state to this component. So I'm doing update-state from the parent, but in that call it says it is not a component, hence my question...

cjmurphy05:03:37

Doing this from the parent: (om/update-state! some-component update :squares conj (random-square))

cjmurphy05:03:04

But as you say it won't work. If I turn the two components into one I should be okay - there's no reason I can't do that, so will proceed that way.

adamkowalski05:03:34

another thing you could try is to use a core async channel

adamkowalski05:03:50

I have started to use them a lot for communication between different parts of the system

adamkowalski05:03:10

inside of the inner component have a go loop which takes messages of the channel and mutates the state

adamkowalski05:03:27

the inner component can have the channel passed to it through props

adamkowalski05:03:41

then the outer component can just do a put! channel message

adamkowalski05:03:16

either that, or I think you could use om computed to pass a callback down and that way you could get a reference to the this that you need

cjmurphy05:03:45

I do use them for stuff that's away from Om Next. Kind of loathe to use them for communication between components. I could pass the function down to the child using computed.

cjmurphy05:03:52

Thanks yes. A few ways around it...

cjmurphy06:03:04

@adamkowalski: A few ways around it - hmm - I think your channel idea may be the only one that will work for this situation. I wasn't able to put the two components together as one is the D3 thing from Tony Kay's Untangled tutorial.

urbanslug06:03:20

before switching an existing codebase to Om Next, I was ask. Will there be another Om Next? and will the changes be so radical?

cjmurphy07:03:34

@urbanslug: I think that ON is less likely to have flaws than Om Previous. Like say with closing over state using cursors. So the future changes won't need to be so radical. (I just picked that up from listening to talks, never used Om Previous). ON is more of a well though out integrated whole concept type of thing 😜

urbanslug07:03:58

@cjmurphy: That makes sense.

urbanslug07:03:13

Calling it ON raises your coolness level by a few points haha

cjmurphy07:03:47

Oh No ☺️

anmonteiro13:03:54

@dnolen: wrt. the advanced compilation stuff in protocol methods, it seems to me like a CLJS bug. However, the changes in PR #662 make the problem go away for now

anmonteiro13:03:29

I've put together a gist that I linked to in #C03S1L9DN with a minimal repro

dnolen13:03:37

@anmonteiro: mind opening a minor ClojureScript JIRA ticket so we don’t lose track of it?

dnolen13:03:43

I’ll add to my Friday queue

dnolen13:03:50

feel free to assign it to me

anmonteiro13:03:20

@dnolen: will do, I'll also put the minimal case inline

dnolen13:03:26

thanks much

anmonteiro13:03:54

@dnolen: one more thing, I think I forgot to add support to lists in path-meta

anmonteiro13:03:03

I think this is desirable, don't you think?

dnolen13:03:18

questionable - but I’m OK with making that work

anmonteiro13:03:31

should be a simple change

anmonteiro13:03:53

@dnolen: not a priority at all, but whenever you update Om's pom.xml I'd appreciate it if you could also update project.clj with the deps you end up using

henriqueqc17:03:31

I'm implementing a table component with an :order-by param. The render function shows the order-by param and changes it based on a user click event. Suppose the component query (simplified) is [({:data [:field1 :field2] {:order-by ?order-by}})], ?order-by is either :filed1 or :filed2. The problem that I'm having is that, if the order-by param changes let's say from :field1 to :field2 but the result of the query is the same (only one item for instance) this won't cause a re-render, and I'm unable to display the correct order by to the user.

anmonteiro17:03:29

@henriqueqc: sounds like a case for custom shouldComponentUpdate implementation

henriqueqc17:03:42

I'll look into it.

henriqueqc17:03:03

One solution that I came up was to add the param order-by to the query (and to the app state). In this case, on the click event, I update the params and transact a new value for order by thus changing the query result and causing a re-render.

henriqueqc17:03:09

But this causes another problem, if I want a default :order-by, I have to add it to both the params and the initial app state.

henriqueqc17:03:10

@anmonteiro: Returning true always, works because a render is scheduled after the the set-query! call. But I haven't found a way to compare the params to "next-params" as they are not present in either the nextProps or nextState.

anmonteiro17:03:34

@henriqueqc: there's a hack you can use, but it might change sooner or later

henriqueqc17:03:08

@anmonteiro: I'll keep it like that for the time being. Thanks!

henriqueqc17:03:53

As I understand right now from om-next there are at least 3 places to keep state, at the global atom, as a component local state or in the query using params. If I wanted to keep the order-by param in the global state atom, how could I keep it in sync with the query params? Looking at the @reconciler, om already saves the queries under the :om.next/queries key. Can I read/transact to that key from other components? Is this a thing?

thiagofm18:03:47

Is anybody unit testing their om next applications? How?

cjmurphy18:03:19

@henriqueqc: Just from listening in here my understanding is that :om.next/queries will not always be in kept in app state.

iwankaramazow18:03:39

@henriqueqc: I wouldn't count on it, I think @anmonteiro is trying to move queries out of the app-state.

iwankaramazow18:03:56

That's a good thing though (I think): keeping certain crucial things opaque isn't a bad thing

iwankaramazow18:03:39

@henriqueqc: aren't query params the right fit for your problem?

henriqueqc19:03:11

@iwankaramazow: I may be misunderstanding something, or using params the wrong way, but as it seems, when you have a piece of state as a query param, you are locked in the ui-tree only child components will be able to access/change the params via parent provided callbacks.

iwankaramazow19:03:23

@henriqueqc: you could always put a marker in your app-state :params-for-some-component, pass those down, and based on those do a set-query! or update-query! which updates the components params

iwankaramazow19:03:00

You read the thinking in Links tutorial?

iwankaramazow19:03:22

Gives you some ideas to escape the ui-tree prison

henriqueqc19:03:33

@iwankaramazow: I've implemented something like that, but it ends up convoluted. Say you want to have a initial value for the query-param, now you have to add it to both the IQueryParams and the initial app-state because on the initial load the param will come from the IQueryParams and after that it will come from the app-state. If your transact function does not depend on the previous value you could get away with just adding it to the IQueryParams. Am I missing something?

iwankaramazow19:03:41

@henriqueqc: you know there is (om.next/get-params component) ?

iwankaramazow19:03:04

Try sticking to IQueryParams as much as possible

thiagofm19:03:29

I'm having to create a bunch of similar reads, such as: (defmethod read :language [{:keys [state ast] :as env} k {:keys [query]}] {:value (get @state k)}) (defmethod read :repository_full_name [{:keys [state ast] :as env} k {:keys [query]}] {:value (get @state k)}) How to refactor them in something that can be shared?

anmonteiro19:03:22

@thiagofm: if there's only a common case, the simplest way would be to put them under the :default key

cjmurphy20:03:47

@thiagofm: You don't have to use multimethods. In some of the tutorials there's one read method and a case statement.

thiagofm20:03:54

Okay, but if I use only one read(and a couple of different queries), would Om next still know when it should rerender something because a query changed? (Just a guess, probably not true)

iwankaramazow20:03:06

@thiagofm: multi methods or a case statement isn't conceptually different for the reconciler

cjmurphy20:03:24

Reads are how ON gets to fill in the data for keys. It doesn't really matter how you implement the read. One thing you could try is putting println statements in the :default case, just to see what ON asks for.

thiagofm20:03:46

sounds good

adamkowalski20:03:46

@thiagofm: you should look into making a default read function which can just be (defmethod read :default [{state :state} key _] {:value (get @state key)})

adamkowalski20:03:16

and only if you have a read which is doing something non trivial should you make a new method for that edge case

thiagofm20:03:12

@adamkowalski: but then how would I specify it in the IQuery?

thiagofm20:03:41

Oh, this only happens when there's no matching read, right?

adamkowalski20:03:02

no, it will happen if no multimethod is defined for that key

adamkowalski20:03:06

which is exactly what you want

thiagofm20:03:40

That's what I meant, nice. Most of the reads in my applications are key/value reads

adamkowalski20:03:07

here david shows how you can have a default read method as well as a specialized one

adamkowalski20:03:54

What I would do is have (defmulti read om/dispatch) so that way you dispatch on key then (defmethod read :default) like i showed above

francoiswirion22:03:29

I just mistakenly posted in #C03RZGPG1, so I'm reposting my newbie questions here now.

francoiswirion22:03:09

What is the second argument to db->tree? I understand one wants to run a query against the database and get denormalized results, but what's the second argument and why is it usually the same as just the database again?

francoiswirion22:03:49

Is there a list of db->tree usage examples somewhere?

anmonteiro22:03:09

@francoiswirion: you can check Om's tests

francoiswirion23:03:36

@anmonteiro: thanks, will check them out. I see the api documentation of db->tree is a bit sparse. Is there appetite for having examples in the documentation? I could contribute some explanations if and when I figure this out.

seanirby23:03:21

anyone have insight into this error message?

core.cljs:264 Uncaught Error: No protocol method ICollection.-conj defined for type cljs.core/Keyword: :font-lock-comment-delimiter-face
It showed up as soon as I changed a components query from [:code-face] to [{:code-face (om/get-query Face)}]

thiagofm23:03:10

I think I either found a bug, or I am doing something wrong(probably?). I have in my root component: [{:repository (om/get-query code/Code)} {:code (om/get-query code/Code)} (nth (om/get-query code/Code) 2) (nth (om/get-query code/Code) 3)]) In my children: ['(:repository {:query ?language}) '(:code {:language ?language :repository-full-name ?repository-full-name}) '(:language) '(:repository-full-name)] For some reason, only the first query, :repository get the params(such as ?language). The second query in order doesn't get the params. If I do a get-query, I see the params there, but when I am in a reader, the params aren't there, they are the default value(nil, in my case).

thiagofm23:03:07

I've tried exchanging order between repository and code queries, and still the same thing happen. Code get both of its params, and repository gets none.

matthavener23:03:01

thiagofm: you can’t do (nth ..) (first ..) etc on a query to change it, the metadata gets messed up

thiagofm23:03:47

how do I specify it instead? I'm not having problems with this part of the query though

matthavener23:03:12

specify the root query? it should be a combination of the queries for children

matthavener23:03:12

if the root needs some of the same data as Code, then you probably want an ident for that data

thiagofm23:03:24

Root doesn't need anything, I just have to specify it there because otherwise things won't work

thiagofm23:03:02

All that I've wanted was, in my children, to query: ['(:repository {:query ?language}) '(:code {:language ?language :repository-full-name ?repository-full-name}) '(:language) '(:repository-full-name)]

matthavener23:03:16

hmm, i’m not sure the query language works with ‘(:language).. if you want :language without params just specify :language (no list wrapping)

matthavener23:03:20

that could be the issue

thiagofm23:03:00

language and repository-full-name are getting passed just fine

thiagofm23:03:12

my problem is with the repository & code queries

thiagofm23:03:54

which for some reason, the first one gets ?language, but the second doesn't get ?language and ?repository-full-name in the reader. If i swap their places, the first one gets it and the other doesn't

anmonteiro23:03:37

@seanirby: mind providing the full stack trace?

thiagofm23:03:01

Example get-query: [(:repository {:query "ruby"}) (:code {:language "ruby", :repository-full-name "rails/rails"}) (:language) (:repository-full-name)] (everything in place, perfectly!) Example output(ast, inside the reader) -- All query related things are nil: {:type :join, :dispatch-key :code, :key :code, :query [(:repository {:query nil}) (:code {:language nil, :repository-full-name nil}) (:language) (:repository-full-name)], :component haxlife.components.code/Code, :children [{:type :prop, :dispatch-key :repository, :key :repository, :params {:query nil}} {:type :prop, :dispatch-key :code, :key :code, :params {:language nil, :repository-full-name nil}} {:type :prop, :dispatch-key :language, :key :language, :params {}} {:type :prop, :dispatch-key :repository-full-name, :key :repository-full-name, :params {}}]}