This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-05-31
Channels
- # admin-announcements (4)
- # alda (3)
- # aws (1)
- # beginners (2)
- # boot (33)
- # braid-chat (4)
- # braveandtrue (20)
- # cider (52)
- # cljs-dev (13)
- # cljsrn (55)
- # clojure (111)
- # clojure-belgium (4)
- # clojure-brasil (6)
- # clojure-dusseldorf (1)
- # clojure-greece (116)
- # clojure-mexico (1)
- # clojure-nl (3)
- # clojure-russia (56)
- # clojure-spec (72)
- # clojure-uk (13)
- # clojurescript (66)
- # community-development (2)
- # component (24)
- # core-async (1)
- # cursive (19)
- # datomic (27)
- # devcards (5)
- # emacs (1)
- # funcool (34)
- # hoplon (313)
- # jobs (1)
- # lein-figwheel (11)
- # luminus (5)
- # mount (30)
- # off-topic (63)
- # om (375)
- # onyx (67)
- # perun (8)
- # proton (1)
- # reagent (4)
- # rum (1)
- # specter (55)
- # spirituality-ethics (7)
- # test-check (2)
- # untangled (34)
- # yada (20)
Where can i find examples of components talking to components - in another word , a large app example
@lewix: not sure if this is big enough for what you want but there is https://github.com/staltz/flux-challenge/tree/master/submissions/mynomoto that I did sometime ago.
we would like to deploy a standalone hoplon frontend to S3 but the source files are also dumped into the target/
dir.
what's the idiomatic way to exclude them?
(since we are not putting the source maps into production either, there is no use for the source files.
or should we just deploy the source proudly? :)
@lewix: if you want to see the my latest thinking with respect to organization of large applications i have some examples
if you go to http://app.adzerk.com
that login screen's source is here: https://gist.github.com/micha/032367265649dee391b09f6cd456d3ea
the workflow/form-machine
on L32 is a state machine that mediates interaction with the database backend
like validation (backend only for now), displaying contextual error messages, things like that
also the "i forgot my password" thing works the right way, and doesn't make you retype your email address 🙂
we use macros to set dynamic vars, and those vars become the refault values for custom element (that's the hoplon way to say "component") attributes
custom elements from the form namespace are aware of these dynamic vars and use them if present to configure themselves
the form/with-field
macro makes bindings for cells associated with a specific field in the form
then for example the custom element form/validation-message
, which is nested in both the form and the form field contexts
that custom element uses context bindings from both of those contexts to configure itself
but you also don't need to explicitly pass contexts through all the intermediate elements
so i could wrap that validation-message
custom element in any number of other elements that have no need to know or care about the bindings related to the form stuff
it looks like it couldn't be, becaus ethere is basically no configuration in there, so how does it know?
with some individual things here and there, like the logic you can see in that example around the "i forgot my password" state
the idea is that you can abstract away the reusable aspects while still exposing all the knobs you need to customize for a specific implementation
and the default configuration is derived from contexts which are set at a higher level than the UI elements
using dynamic extent instead of passing context in a map through the component tree also has the advantage of separation of concerns
and good factoring and separation of concerns allows you to build more powerful abstractions and eliminate boilerplate and hard to follow logic
the form
namespace is here btw: https://gist.github.com/micha/7a734317c1fbf8c93661e38788c44a82
the deeper layers of the application get progressively less elegant, but that's fine because they will all be factored out into separate libraries and require very little maintenance
the part that really needs to be elegant and easy to understand is the application layer, where we assemble the actual appliaction
using all the general reusable components developed in the lower level libraries and namespaces
we're pretty much at the point with that application now that adding new features hardly ever involves mucking around in lower level code
the validation-field
custom element definition, for example: https://gist.github.com/micha/7a734317c1fbf8c93661e38788c44a82#file-form-cljs-hl-L198-L209
or the submit-primary
element, which knows when a request is in flight to the backend and shows Working...
and disables itself until the request completes
the cool thing is that it manages its own state, and it doesn't need to ahev everything passed to it via arguments
so that element can be created by another piece of code that doesn't know about anything
when you need to pass info directly down as parameters it becomes difficult to make higher-order components
because you need to tell the higher order thing how to pass stuff down to the things it creates
"don't know, don't care" is i think the key to separation of concerns, which is key to developing general-purpose higher-order functions, which is the key to making maintainable applications 🙂
@micha Ln 233 has attr before you merge with attr
@micha how are you binding the dynamic vars? with-bindings
? I was looking into this last week and got rather turned around by the different macros for binding dynamic vars
@flyboarder: the macros are just wrappers for the binding
cljs macro
probably should have just used binding
there, but with my wrapper macros i have the option to include default bindings
you can accomplish a similar default binding situation when you define the dynamic vars too, so not sure if the wrappers really provide much value overall
https://gist.github.com/micha/7a734317c1fbf8c93661e38788c44a82#file-form-cljs-hl-L233-L234
@flyboarder: here is the clj file with the form macros: https://gist.github.com/micha/c266cec4c2c36c576445
so it would seem that this method is pretty much the same as passing in the state, with the exception you don't actually have to call the :attribute
, does this change how cells are expected to behave?
otherwise every function would need to know how to pass things down to any function they call
(insert my app here)
which means your head quickly explodes trying to understand how things are related to each other
so when would passing in state be appropriate then? this still feels similar to a custom component with a let
and then a child component using some var in the let
the let
is for components that are not related abstractions, but are related in the specific instance in which they aer assembled at the application layer
where there is nothing reusable there, because it's specific to a particular screen or workflow
ok that makes sense bindings => related abstractions
let => related instances
at the application layer you don't form new abstractions, you simply assemble more general things there into an instance of a particular thing
makes sense imo, using it in forms makes more sense now too
as forms are a very general thing that applications do
right since they can be different at any time
right and thats where you get to take advantage of the code reuse
handling paginated state and managing how to get that data from the backend in a clean no boilerplate way was a huge step toward maintainability
yeah I can already think of instances in my app where that would have saved a bunch of time and pass around way less
the only tricky/messy part is that you need to convert dynamic bindings into lexical ones in the "consumer" if there will be async callbacks thta need those values
that's how you'd normally do things in clojure when you use dynamic vars like this in an asynchronous program
(defelem submit-primary
[{:keys [state text active-text] :or {text "Save" active-text "Working..."} :as attr} kids]
(let [state (or state *form-loading*)
attr (dissoc attr :state :text :active-text)]
[(button/primary
(merge {:type "submit" :toggle (cell= (= 0 state))} attr)
(or (seq kids) text))
(button/primary
attr
(merge {:disabled true :toggle (cell= (not= 0 state))} attr)
(or (seq kids) active-text))]))
yeah thats what I was thinking of doing
which would still allow local state to be used instead
thats awesome!
great pattern
seems so clean when you think about it
i've been trying to factor it out into something that can be crystallized into a library
yeah thats a fine line between a library and the language
i've also been trying to figure out how to make this into a more rigorous state machine thing, but don't know how to do it: https://gist.github.com/micha/622a62d6a52ff419e5ac
want to factor that into a library too, but it would be too specific in its current form
yeah but much of that is form specific
although submit
and validate
are probably pretty common for state systems
interesting challenge
@micha what about when using loop-tpl
? for example right now I have this (not the best) https://gist.github.com/flyboarder/8a5055b212785818ebfa53ddeb05e2f2
current research is leading toward a type of macro that goes along with the context macros to automate that binding process
or alternatively perhaps a macro for defining dynamic vars such that they can be discovered at runtime, like clojure has
how come this loop-tpl
gets executed when my :bindings
is set to null?
it doesnt appear but it forced me to do LN21
or is that just the formula cell doing that?
yeah if I do :class [severity]
I get a dose not support name:
also i'm not sure how that loop-tpl is working ,shouldn't it evaluate to just a single element?
yeah compact-message
formatting error from copy paste
yeah the vector works but not when severity = nil
which happens when the cell is reset so it doesnt show anything but it throws an error
i believe it converts vectors to maps, i copied the same part from core and put it in semui-hl
doesnt that convert it
right so as long as my vector doesnt contain nil it works
otherwise it works like usual :class [:class1 :class2]
but it does 😛
thanks to zipmap it gets {:class1 :class1 :class2 :class2}
so as long as kvs isnt nil
hahaha sanity restored
until we discover force 5
like known forces in physics, gravity electromagnetism, weak/strong atomic bonds
they believe they might have inadvertently experienced the result a 5th force, other 95% of the universe here we come!
http://futurism.com/physicists-think-they-might-have-just-detected-a-fifth-force-of-nature/
@micha I think a simple fix would be to change (seq kvs)
to (seq (or kvs [:class false]))
wait that wont work
it needs to be in the clmap
so maybe change clmap if to cond and check for nil
its in ->map tho
ah ok so it’s becuase im wrapping it in the vector
that change worked
does the same thing on removing them, throws the same error on remove
Uncaught Error: Doesn't support name:
like so? (cell= {severity severity})
but the severity is my class
severity is a value that is the class name
so right now i dont know what the class name is as it comes from the db
i guess ill stick with (cell= (when severity [severity]))
once it sets a class that way i don't think it will ever unset it, no matter how severity
changes
well severity should never change it’s the severity of the error that was saved to db
it’s just a key with vectors of errors
well this is my db structure:
oh I see what you mean just use the known class names since there are only a handfull
im sure you understand it more than I so 😛
right
so would (cell= (when severity [severity]))
cause memory leaks or just static content if severity were to change
ill have to test this
hmmm so i cant change keys in my db anyway, it would be the same as removing it and adding it under the new name
and if you change the value of severity
to a different class, then the element will have both classes
so (cell= (when severity [severity]))
does hide things tho without throwing the error
well what is in loop-tpl doesnt show
which is weird that it throws the error, without it
but it’s calling name on nil once the :bindings have become nil, why is it trying to add the class to the element that it is hiding?
so the attributes are updated even tho the elements are not being displayed
because the cell changes
that's a crucial thing because otherwise you'd need to manually manage how to "catch up" the element's state when it goes back in the dom
that's the kind of thing you need to do with react, they have a ton of protocols you need to implement basically
which is kinda what im doing with the when
beacuse there is still no guarantee that severity
isn't nil when it's in the dom or not
unless i change it to a map
so is this really a bug then? or simply a poor usage pattern
ok i think im tracking now, so the class is still present while it’s hidden, im just not seeing it because 1) it doesnt change for me 2) either exists or is deleted in my instance
it’s never in another state which could then have both classes
i think if you have a situation where severity
is "foo"
and then later severity
is "bar"
you will see both classes on that element
right
just wouldnt happen in my instance but it could if anyone else is using a vector in a loop-tpl like that
gotcha