This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-09-16
Channels
- # alda (1)
- # aws-lambda (1)
- # bangalore-clj (1)
- # beginners (70)
- # boot (24)
- # cider (1)
- # cljs-dev (167)
- # cljsjs (8)
- # cljsrn (17)
- # clojure (224)
- # clojure-android (7)
- # clojure-austin (8)
- # clojure-russia (17)
- # clojure-spec (120)
- # clojure-uk (46)
- # clojurescript (68)
- # community-development (198)
- # conf-proposals (1)
- # core-async (7)
- # cursive (6)
- # datomic (27)
- # dirac (19)
- # events (9)
- # hoplon (2)
- # jobs (1)
- # luminus (9)
- # off-topic (1)
- # om (281)
- # om-next (5)
- # onyx (50)
- # pedestal (1)
- # re-frame (19)
- # reagent (11)
- # ring-swagger (14)
- # slack-help (2)
- # spacemacs (1)
- # untangled (72)
- # yada (30)
I’ve been using Om.Next for awhile, and I have many places in the app where I use ui
components with no queries. So if I am have props that being pulled by parent component and passing them directly to a factory of child, and inside render of the child grabbing props with (om/props this)
, are there any benefits of having query that explicitly states what that component needs?
@ag yes, especially if you use path optimization. You get the added benefit of not having to rerender the entire app from the root each time that the app state is modified. For larger apps, it also means you don't need a root component with a gigantic query. You can reason locally within the component to understand what data it needs without having to think quite as much about how to get the data there
hmm ok. what’s the best way to deal with situation when I have a component that needs to render a few children but not depending on data, it would use the same data but should be rendering things differently, right now I’m doing something like this, which I guess (although works) still totally wacky:
(ui-filter-popover {:children [ui-filter-text ui-filter-date]})
…
(ui-filter-popover {:children [(ui-checkbox-group types)]})
basically I'm passing children as propsI’ve added a library that will work with Om Next and Untangled to co-locate localized CSS on reusable components. It does not need to hack defui
, can refresh during development, and automatically gives you composition and minimal CSS for the components you use. It is similar to the library om-css from Antonio, but it doesn’t require you use modified dom or defui components, and does not need to output a file.
https://github.com/untangled-web/om-css
@tony.kay Excellent library. Looking forward to playing around with it. Always wanted a way to do exactly that and had thought of writing it myself many times. Glad someone else beat me to it.
@tony.kay Cool! Any word on how this could work with https://github.com/postcss/autoprefixer?
@tony.kay om-css from Antonio does refresh?
@mitchelkuijpers: yeah you can use it with Figwheel
@anmonteiro I use it with boot 🙂
And it doesnt refresh?
Yes it does
With some hackery
Oh I thought you were asking if it refreshed
Well yeah it does, and @tony.kay is aware of it
No is misread tony.kay his post, I thought it stated that your’s does not
I made om-css reloading working by doing a bit of a hack hehe:
(sift :move {#"^public\/js\/main\.outout\.css$" "public/css/next.css”})
Awesome
Oh it might get prefixed by Boot-cljs automatically
Bummer
Yeah that is the problem, i don’t really care
I first used the
:css-output-to "resources/public/main.css”
but that triggers double reloadsor it would reload 4 or 5 times 😛
because boot is watching the resources folder
@mitchelkuijpers: probably worth submitting a PR to the readme explaining how to work with Boot
@anmonteiro I will do that tonight
Thanks!
@mitchelkuijpers The version from @anmonteiro uses overridden defui and DOM functions to ensure that the syntax is easy, and that a CSS file is (re)generated as you evolve the CSS (which figwheel) can hot reload. I actually worked with him via PM on the design of mine to address some issues I had (I didn't want to hack defui and DOM macros/functions). The resulting library has some nice facets as a result.
@tony.kay Cool! I am sorry I misread the post a bit
@levitanong The library doesn't really care what you use for CSS. In fact, you could just co-locate strings and avoid some of the helper functions. I added helpers for garden, but we could just as easily add helpers to combine anything that can make CSS.
I do wonder if the compose to root constraint is nice because I am currently using the other om-css, but I would be interested to see what are exactly the benefits of composing to root
What I mean to say I would not mind to be proven wrong
Another cool thing: you can use React component lifecycle (e.g. component will mount) to inject component styles into the DOM..meaning a library of widgets could inject their styles with no extra config!
So you don’t generate a css file?
@mitchelkuijpers which addresses the compose to root issue...not actually necessary
Oh? cool
nope, you either manually include a (dom/style) element in your tree, or use upsert-style
to inject a style element into the DOM body
Aha ok
and since it is all in code, users of components could "theme" or otherwise configure the CSS through API hooks of the component in question
Thanks @tony.kay I will try it out, but I wont switch anytime soon prob because we are already pretty invested in the other om-csss
Cool, that is a great thing. I have stolen a lot from untangled
(and learned)
Oh, and a working Untangled Recipe in the cookbook is now up on github. @denik See my comment above to levitanong
@tony.kay I’m not sure you understood @levitanong’s question
I think it’s also something I mentioned to you yesterday
the problem being that a lot of people now process their CSS, either with Less/SASS and/or PostCSS that adds vendor prefixing to CSS rules
this is why it was a hard requirement for om-css to generate a CSS file
so that it could be included as part of the “CSS build process"
I really like the idea behind your newfound approach to colocate CSS with Om Next
however, I think there’s still space for a solution that combines the best of both worlds
but my response stands: whatever library you want to use to generate the actual CSS doesn't matter. The library is more about the idea of how to inject it and compose it.
Ah, you're right, I didn't understand he was asking about postcss external css parsing tools
I just haven’t had enough time to look at what exists out there in the React world, such as js-css, aphrodite and stuff
we could probably learn a lot from those
it would be straightforward to have a pre-processor work on the garden data structures (or the resulting css strings?) and do a similar thing. Not aware of existing libs though.
right, but my point was to find something that could work with existing tools
not to reinvent all of them 🙂
especially here: https://github.com/untangled-web/om-css/blob/d9483a34b674095d24ad6d47ff0b9eb0fdeada6e/src/om_css/core.cljc#L122
since it specifically looks for #js maps and specifically rewrites entries in those from .class to .className
I would imagine it would be a minor change to make it work (or supply a macro for sablano). It's just a walker with a transform in specter, so any nested data structure can be scanned (in fact the tests use more raw data to make it easier to run the test in untangled-spec)
But the macro is for convenience, as the docs state. You can just get the localized names directly from local-class
It also looks like garden does support some auto-prefixing, just FYI: https://github.com/noprompt/garden/wiki/Compiler
@dnolen I’ve been thinking about an optimization to reconcile!
. I think it’s the case today that if a number of components are queued for re-render, all of them will run through the update loop. It does seem to me, however, that this loop is quite naïve. I think we have enough information to know whenever we have already updated a subtree from the top, in which case the components that are below in that subtree could just be removed from the render queue.
thoughts?
@peeja :elide-paths
is a parser option that will skip adding Om path metadata to the results of parsing
you can expect things to break if you set it to true
🙂
@peeja yes, definitely! I had never thought about it
we should probably make that the default as well when invoking it from om.next.server
@dnolen ^ agree?
oh actually: https://github.com/omcljs/om/blob/master/src/main/om/next/server.clj#L9
it’s already there
@anmonteiro Is there an example app that uses Compassus with a backend? I feel like I'm misunderstanding how it's supposed to work. Specifically, I don't understand why all the queries are keyed by route. It seems to me like the parser and especially the remote should be ignorant of the routes.
@peeja you can probably look in the Compassus test suite
nothing open source with a server that I know of
the remote can definitely be unaware of the routes
and I think that’s the default, actually
you’d need to use compassus-merge
otherwise
https://github.com/compassus/compassus/blob/master/src/test/compassus/tests.cljc#L252-L309
hopefully these should guide you
I should definitely make an example fullstack application that showcases Compassus
It's more that I would have expected each page's query to run against the root, rather than at the route key, and because it's nested under the route key I'm having trouble raising idents to the top for my remote
@levitanong One more comment on auto-prefixing. Not only does garden support adding vendor prefixes to tags, it doesn't need to do the same checking as postcss. It can just emit them wherever you tell it to, even if the browser in question doesn't need them. The cool thing about generating this stuff client side is the the download size isn't affected at all, since the CSS is being generated client-side. Vendor prefix the heck out of things, and don't worry about size.
@peeja do you have anything sizeable working with Compassus server-side that you can open source?
happy to link to it in the absence of a fullstack example
@peeja yeah the vector within a vector is no problem at all
instead of using query->ast
try using om.next.impl.parser/expr->ast
should solve that one
hrm, I think I’ve solved that one before, let me look around
@peeja have you tried tweaking merge-sends
?
Compassus's read
for [:default ::route-data]
for instance does (parser/expr->ast (first ret))
, but my ret
will have multiple values and I want them all
^ reconciler option
it’s how you merge the sends your parser returns with the ones that are already queued by the reconciler
@peeja I’m not sure you’ll have more than one
@peeja again, not sure that’s the case
the query that Compassus passes to the user-parser is a single join: [{route the-query}]
I didn’t get what you meant then, sorry
I was thinking there might be a bug in Compassus there
@peeja well it will be at the top-level, right?
because I can't imagine a use case for the page's reads being nested under a key for the route
Or, to put it another way, I can't imagine a page having a query that isn't solely idents (with joins)
which I thought wouldn't be a problem, because I thought idents automatically moved to the top of the query, but apparently I have to do that myself
That is, db->tree
will pretend they're at the root of the query, but that doesn't help when I need to pass the query on to my remote
sorry I didn’t follow since the double negative 🙂
My page queries are all made up of idents, and I can't imagine why they would have anything else
gotcha now
I'm spiking on recreating this page with Om Next: https://circleci.com/projects
So the page needs the current user's orgs: that's {[:app/current-user '_] [:user/organizations]}
and it needs more info on whichever org is selected, which right now looks like {[:organization/by-vcs-type-and-name {:vcs-type "github", :login "circleci"}] [:organization/login]}
(pardon the fact that the ident there uses a map as its value, which I know is technically illegal)
(and eventually that will actually query via some kind of routing data, so it'll be {[:app/routing-data '_] […]]
)
because at this level it's only interested in global stuff, and only subcomponents will be interested in relative data
I think it all makes sense at a first sight
so what’s the problem specifically?
currently just remoting?
Exactly. Because it's on a page, my query is
[{:app/projects [{[:app/current-user _] [:user/organizations]}
{[:organization/by-vcs-type-and-name {:vcs-type "github" :login "circleci"}]
[:organization/login]}]}]
@peeja so app/projects
isn’t the route?
I mean, I guess I can deal with it in the send
, but I thought it would be more correct to do it in the parser
I need to turn this into a call to get the current user's orgs, and a call to get the login (etc) of the selected org
@peeja OK so there are different problems here
let’s tackle each one at a time
1. the RESTful call could be solved by having a multimethod in the send
, which IIRC you already have
then it makes total sense to return the route to dispatch on that multimethod
so Compassus will give that whole query to the send method if I’m understanding correctly
and it should
what are the UI-only concerns that send
knows about?
from the moment that you have a REST API, those are not UI-only concerns anymore
it’s not a problem of your frontend, it’s just a problem of REST’s out-of-band-ness and coupling
But it doesn't matter which page I'm on. Any page might declare it's interested in the current user's orgs
@peeja isn’t that what I’m saying? Wouldn’t that all be great if only you weren’t communicating with REST endpoints?
If I were hitting a single queried API, I wouldn't want that to know about :app/projects
either
exactly
if you were hitting a single queried API, you could just send what’s beneath :app/projects
in the join
or any other route key, for that matter
without having to dispatch based on the route
@peeja I would probably do it in send
because it’s easier
but it would be simpler to do it in the parser
That's what I meant by https://clojurians.slack.com/archives/om/p1474043584001211
oh well
¯\(ツ)/¯
so to do it in the send
function, you probably already have everything hooked up
to do it in the parser you’ll have to mess with merge-sends
if I understand correctly
Anyway, that's still really helpful. You've been super generous with your time, as usual, and I greatly appreciate it.
@peeja no problem
try this for merge-sends
:
(defn custom-merge-sends [a b]
(merge-with #(into %1 (mapcat identity) %2) a b))
and report back!
@peeja pinging, not sure you saw this ^
Ah, haha, that doesn't work because merge-with
ing {}
with anything won't use the into
and won't use the mapcat
😛
oh shoot
OK let me revise that
(defn custom-merge-sends [a b]
(let [a (cond->> a
(not (every? (set (keys a)) (keys b)))
(merge (zipmap (keys b) (repeat []))))]
(merge-with #(into %1 (mapcat identity) %2) a b)))
I did
(fn [sends new-sends]
(let [unwrapped-new-sends
(into {} (map #(vector (key %) (first (val %)))) new-sends)]
(merge-with into sends unwrapped-new-sends)))
I still feel pretty weird about returning an invalid query from the parser, but at least this works
@peeja glad it’s working
the problem is just the recursive parsing
I created a gist with my version above so I don’t forget it if I ever end up needing it 🙂 https://gist.github.com/anmonteiro/1dea0b30ebcc195875dc5d0b102f69c8
I could actually change the query on the way in to the parser, and skip the route keys entirely
Using datomic here, so there’s a strong incentive to produce a valid query in the parser
@jfntn the approach @peeja & I came up with will send a valid query to the remote
Right now I'm playing with wrapping the parser itself, so that while my read
returns something funny, my parser returns the correct send
I'm wrapping my read and mutate's in "middleware" so I can edit or validate inputs/outputs. I also have some parser middlewares. I don't know how common this is
not for this specific usecase, but it's a common thing I do to solve problems around parser/reads/mutates
db->tree
(om.next/db->tree query some-data app-state-db)
Given a query expression, some data in the default database format, some application state data in the default database format, denormalize it. This will replace all ident link nodes with their actual data recursively. This is useful in parse in order to avoid manually joining in nested relationships.
I'm really hazy on the distinction between some-data and app-state-db. How are they different? (I'm not doubting they are)
@grzm: this function is internally called denormalize*
. some-data
is data that contains references / links to entities app-state-db
is all the app’s data, stored in a normal form. this function recursively replaces those links/references with the actual data using query
and the data in app-state-db
.
@env the app-state-db contains all off the state, though, right? I don't understand what some-data could add.
@grzm, some-data
defines the tree of data to recurse over and the shape of the output, while app-state-db
is the source of data in which to look up links. in simple cases these, could be the same, but structuring the params in this way allows for the case where the desired output is different than the full db tree, or perhaps not part of the db tree at all (but still using links to data that is in the app-state)
Because query
isn't a query against the root of the state, it's a query against (get st key)
yup. In that case I don't see what the purpose of the app-state-db is. All of the data pertinent to the query should be in the some-data portion.
If (get st key)
contains links, those links are dereferenced from the top of the app state
Which is because, once one of these reads can have multiple joins/keys, Compassus has the same problem I'm trying to solve.
If I query for a singleton ident—`[:foo _]`—I should expect it to show up in the props as :foo
, and if I query for a non-singleton ident—`[:foo 5]`—I should expect it to show up as the full ident, [:foo 5]
. Is that right?
If I understand correctly, the components that re-read are determined by the merge
function's :keys
value
It looks like my component which queries for [:app/current-user _]
is being indexed by the key :app/current-user
, but not [:app/current-user _]
, which means that when my send comes back it doesn't cause it to re-read.
Judging by this, I'm guessing the indexing is intentional, and my send is acting funky: https://github.com/omcljs/om/blob/master/src/main/om/next.cljc#L1774
@peeja the actual issue is that Links were never meant to be sent server-side 🙂
so I’m misunderstanding your issue
I get some orgs via the API, and then I call (cb (rewrite {:app/current-user {:user/organizations (vec orgs)}}) query)
is rewrite
some process-roots
thing?
that might be where the problem is?
that’s a tough one to answer thouroughly
what I would say is that the callback probably doesn’t expect a link
(rewrite {[:app/current-user '_] {:user/organizations (vec orgs)}})
, I get
{:app/projects
{[:app/current-user _]
{:user/organizations
[{:organization/name "circleci"}
...]}}}
With (rewrite {:app/current-user {:user/organizations (vec orgs)}})
, I get
{:app/current-user
{:user/organizations
[{:organization/name "circleci"}
...
:app/projects {[:app/current-user _] nil}}
so the thing is that :app/current-user
is supposed to be a top-level key in your app state
so you want to merge that in a top level key, not under :app/projects
my other question is why the send function is even getting the link?
they’re not really meant to be there
I would expect current-user
to be only a pointer in your app state
right? a pointer to an actual [:user/by-id id]
or something
I’m probably not thinking clearly about all the solutions (a bit tired, we can probably revisit this tomorrow)
but one would be to modify the AST in your parser
such that it includes the actual user ID