Fork me on GitHub
#fulcro
<
2018-10-15
>
currentoor00:10:49

@hmaurer that is the standard procedure, what seems odd about it?

hmaurer00:10:48

@currentoor I might be missing something but it seems strange to have to imperatively load data before transitioning to a route. It seems like “what data should be loaded and when” is something the route itself should care about, not the call-points which transition to it

currentoor00:10:10

maybe i’m missing something but it sounds like both are the same, user clicks a button and triggers mutation foo, whose implementation does two things calls set-route to update to the new route and calls df/load-action to trigger a load

currentoor00:10:45

so then foo is your route change mechanism from now on

currentoor00:10:45

of course instead of foo you make name that sounds like a route transition in your app

Daniel Hines14:10:26

I'm starting a green field hobby project, and was really thinking about using Datomic, but seeing the new tools for graphql, I'm really tempted to use graph.cool, write some graphql schema, run a command on their cli to deploy, and be done. Do you guys have any thoughts on Datomic vs the growing number of graphql solutions out there for a fulcro backend?

wilkerlucio14:10:48

we are open to all of then! and actually, using Graph.cool + Pathom is a great way to go, @tony.kay recently add some docs to pathom about how to do exactly that: https://wilkerlucio.github.io/pathom/DevelopersGuide.html#_simple_graphql

wilkerlucio14:10:35

if you just use the connect integration using the graph.cool you can get full access to the GraphQL + custom resolvers and mutations using anything (or graph.cool itself, but you can also mix in other REST things or even other GraphQL endpoints)

tony.kay14:10:33

I like Datomic’s facility to scale reads, but I’m sometimes put off by the write throughput. I’ve heard people say they end up in the “free tier” with AWS and Datomic because when there are few writes, the reads “don’t happen” because of aggressive caching. I’d just carefully consider what your I/O is likely to be, and if it is write-heavy I’d make sure to model/test well.

Daniel Hines15:10:10

Thanks for the advice/cautions. I'm really eager to learn and use Datomic, but I think I'll be able to ship it faster with graph.cool, so I'll give that a shot. If it were a more critical application, I think I'd be more inclined to invest in Datomic more, but it's a pretty simple thing that nobody is going to depend on.

magra16:10:23

Hi, I have fuclro.ui.form-state/dirty-fields return {:before nil, :after nil} in delta. This is the nesting {:delta {[:contact/by-id #fulcro/tempid["52214238-2efb-495b-a7af-b55cb71e430a"]] {:contact/person {:before nil, :after [[:person/by-id #fulcro/tempid["b414fb0f-5a22-46e4-a64b-c6c7e92f62d2"]]]}}, [:person/by-id #fulcro/tempid["b414fb0f-5a22-46e4-a64b-c6c7e92f62d2"]] {:person/lastname {:before nil, :after "Fleißig"}, :person/firstname {:before nil, :after "Gustav"}, :person/middlename {:before nil, :after nil}}}}. Am I doing something wrong here?

tony.kay16:10:32

@magra not sure I see the problem…it is just telling you that field wasn’t set before, and it still isn’t set. If there is a tempid in the form, then all fields are sent (since the server can’t possibly know any of them).

magra16:10:39

@tony.kay ok. I just thought delta is changes. Would you put nil into datomic or an empty string in this case?

tony.kay16:10:16

I wouldn’t store a missing attr in datomic

tony.kay16:10:53

unless you have a requirement that middle name is always defined, even if just "", but that is a local app schema concern

magra16:10:45

Good. thats how I did it so far. So I will filter it out then.

tony.kay16:10:41

Yeah. Form state can’t know if an “unchanged value” in a “new entity” is important or not..so it doesn’t make a judgement call….if it has a tempid it just assumes you need to know about every field on the server, in case your client-side init code gave something a default value, etc.

magra16:10:48

Thank you!

hmaurer20:10:58

@tony.kay Hi! Quick question on the websocket layer you recommended me the other day. How would you handle authentication? Would you have a mutation that attaches some state on the server to a specific websocket connection?

hmaurer21:10:57

@tony.kay by the way, there is something I wanted to add to the discussion we had last time. You asked me how I found the first steps with Fulcro, etc. I mentioned that some parts confused me, as the way Fulcro worked was somewhat different from what I had been used to until now. However, I forgot to mention that this is also what caught my interest. There are many librairies out there in the JS land that all attempt to solve the same problem (writing UIs), and while they look different on the surface they often all boil down to the same things. Fulcro seemed different in its approach, which is precisely why I am getting into it

hmaurer21:10:40

So yes, on the one hand it does throw you off a little bit at first, but on the other hand that’s a sign that there is something of value to be learned there

👍 4
tony.kay21:10:02

@hmaurer There are a couple things I recommend on websockets. I’m actually working on security around that right now. Doing some work to make sure your websocket cannot be hijacked is a good idea (I will try to write something on this in the guide soon), but in terms of auth, it is no different than any other app. You probably want sessions and cookies, so that a browser page reload doesn’t lose the auth…but that means you have to make sure CSRF is in place and working properly.

tony.kay21:10:51

Then in Sente you’ll have the request that was used to “build” the websocket, and that will have the session info in it

hmaurer21:10:16

oh, so the request will be given on every message received on the socket?

tony.kay21:10:27

If you log them in after the websocket is established, ou can use sente’s reconnect function to re-connect to update that embedded request

tony.kay21:10:44

yeah, but the request is cached, which is why you need the reconnect if you log them in after they’ve connected

tony.kay21:10:23

e.g. you could use normal (non-websocket) stuff to log them in, then establish websocket, but in Fulcro it’s easier to just hook them up via websockets, and then do auth

hmaurer21:10:02

so you would have, i.e., an authenticate mutation with Fulcro that would return a token, then set that token on a cookie (or else) and trigger a re-connect

tony.kay21:10:06

let me see if I can find some logic I can share from my apps…

hmaurer21:10:40

@tony.kay don’t worry about it now; I won’t be implementing auth with websocket for at least a month and a half, and by then you might have documentation on it 🙂

hmaurer21:10:47

I was just curious

tony.kay21:10:23

#?(:clj
   (defn login* [main-connection email password]
     (on-error "Login" FAILED
       (prom/mlet [c            main-connection
                   db           (d/mdb c)
                   account-info (account/validate-user db email password)]
         (cond
           (prom/context? account-info) (session/failed-session :server-down)
           (map? account-info) (session/valid-session account-info)
           :else (session/failed-session :failed))))))

#?(:clj
   (s/fdef login*
     :args (s/cat :connection any? :email :account/email :password :account/password)
     :ret ::session/session))

#?(:cljs
   (defmutation login
     "Mutation: Try logging in with the given `email` and `password`."
     [{:keys [email password]}]
     (action [{:keys [state]}]
       (swap! state set-status* :authenticating))
     (remote [{:keys [ast state]}]
       (-> ast
         (m/returning @state session/ServerSession)
         (m/with-target [::session/session])))
     (refresh [_]
       [::session/session]))
   :clj
   (defmutation login
     "Mutation: Try logging in. Params: `email` and `password`"
     [{:keys [email password]}]
     (action [{:keys [main-connection]}]
       (let [session (login* main-connection email password)]
         (server/augment-response session
           (fn [response]
             (assoc response :session session)))))))
and then I’ve got pathom on the server and have a “session” query resolver…so the client (on reload) can first do a query on session…if it gets something back, then the UI just looks logged in.
#?(:clj
   (let [empty-session {::status :none}]
     (defresolver `session
       {:com.wsscode.pathom.connect/output [::session]}
       (fn [env _]
         (let [session (-> env :request :session)]
           (timbre/debug "Looking up session " session)
           (if session
             session
             empty-session))))))

hmaurer21:10:23

By the way, I have another question which is more relevant short term. I asked it here yesterday and got a reply but I’m curious to hear your perspective. I found the story around data-loading for routes a bit counter-intuitive. Unless I am mistaken it seems that the go-to way with Fulcro is to load the data before transitioning to a route, but this seems to push the responsibility to the caller (whatever is triggering the transition) and not on the route itself. There might be multiple ways to transition to a route (URL change, etc)

hmaurer21:10:54

It seems to make more sense to me to explicitly say “that route requires this data to be loaded”, and to trigger that load whenever we attempt to transition to that route, however we do it

hmaurer21:10:17

thanks for the code above! taking a look now

tony.kay21:10:14

The response you got was correct. In more detail, here’s why: The existing defrouter is meant to be there to make it so you don’t have to manually write the (sometimes confusing) union queries that make things faster…it’s the optimization part of routing for the UI. People want all sorts of behaviors tied to UI routing, and me supplying “just one way” for those is a terrible option. Maybe you want to auto-load data, but someone else wants more fine-grained control and HTML5 this-and-that…The routing is minimal because it is meant for you to build something to your liking on top of

tony.kay21:10:25

(including an add-on OSS library if you so choose 😉 )

tony.kay21:10:45

I don’t consider the complex parts of routing to be an internal Fulcro issue, in other words

tony.kay21:10:54

I’d rather see add-on libs

hmaurer21:10:10

Could anyone point me as to why this happens? :thinking_face:

hmaurer21:10:20

(the null pointer exception)

currentoor22:10:17

@hmaurer did you require the app.client namespace?

currentoor22:10:50

i mean are you able to require it?

tony.kay22:10:05

@hmaurer you really should be using Fulcro Inspect for Chrome 🙂

tony.kay22:10:47

The error you’re seeing is because you overflowed Figwheels communoication buffer for the REPL, I think

hmaurer22:10:51

@currentoor ah yep, requiring it did the trick. But why was I able to see the atom but not able to dereference it?

tony.kay22:10:06

I know you can overflow the REPL buffer too

currentoor22:10:12

:man-shrugging: lol

hmaurer22:10:33

@tony.kay ah, will do, thanks 🙂

hmaurer22:10:40

The errors in the clojurescript repl are really godawful 😞

tony.kay22:10:30

Inspect makes it all better 🙂

tony.kay22:10:38

well, almost

tony.kay22:10:18

some of the clj(s) error messages are about the worst thing ever…it’s slowly improving

hmaurer22:10:22

Trying inspect now; looks great 🙂

currentoor22:10:58

@hmaurer you’re using the chrome extension right?

hmaurer22:10:20

messages like this: https://puu.sh/BLEta/968496295d.png (I know it has nothing to do with Fulcro, but… 😛 )

hmaurer22:10:55

a very cryptic way of saying “wrong function name”

currentoor22:10:50

might just be me, but “undeclared use of var …” seems clear no?

hmaurer22:10:15

mmh yeah I guess

hmaurer22:10:23

@currentoor sorry but yet another silly issue… I am getting this: https://puu.sh/BLEYw/0816942cdd.png

hmaurer22:10:49

I did require css injection (`[fulcro-css.css-injection :as css-injection]`), and I am on Fulcro 2.6.0-RC9

hmaurer22:10:43

In fact in the cljs repl I can see this function just fine:

dev:cljs.user=> fulcro-css.css-injection/upsert-css
#object[fulcro_css$css_injection$upsert_css]

currentoor22:10:54

well for one can you bump up to Fulcro 2.6.7

currentoor22:10:05

i think the template needs to be updated

currentoor22:10:15

try changing line 33 to (js/console.log ccs-injection/upsert-css)

currentoor22:10:23

and see what it logs to

currentoor23:10:14

you might have some stale stuff sitting around, you could also try lein clean and restart your build

hmaurer23:10:26

just restarted the build, the error is gone (and upgraded to 2.6.7), now getting:

Auto-include was used for CSS, but the component had no query! No CSS Found.
but i can probably figure that one out with the doc. Thanks! 🙂

hmaurer23:10:39

Well, another question: > By default it will automatically use the query to find all children (even through unions) that have CSS. (http://book.fulcrologic.com/#CSSInjection) What if a child component has no data dependency, and is therefor not part of the query? I must use css-include in such cases?

currentoor23:10:37

i’m actually not sure about this

currentoor23:10:55

@tony.kay can answer when he’s around

tony.kay23:10:44

Yes, auto-include only follows queries

tony.kay23:10:01

so you’d have to do manual includes there

tony.kay23:10:40

I honestly don’t know if you can mix and match..don’t remember…hopefully I said in the docs? 🙂

tony.kay23:10:01

e.g. will auto-include follow :css-include on things it finds

hmaurer23:10:55

Alright, one another thing (sorry…). (css-injection/upsert-css "my-css" Root) throws the following error:

Auto-include was used for CSS, but the component had no query! No CSS Found.
However, (css-injection/style-element {:component this}) in my Root component works just fine

hmaurer23:10:11

I have a query on Root of course:

(defsc Root [this props]
  {:query [{:person (prim/get-query Person)}]
   :initial-state (fn [params] {:person (prim/get-initial-state Person {})})}

tony.kay23:10:14

wrong params

tony.kay23:10:26

new upsert-css uses same args as style-element

hmaurer23:10:41

ah, so {:component}

tony.kay23:10:42

{:component Root}

hmaurer23:10:56

yep that works, thanks 🙂

tony.kay23:10:01

I changed it in the new ns so you could specify all the options

tony.kay23:10:13

are the docs wrong?

hmaurer23:10:22

You do say in the doc “They take the exact same options map as the style-element.“; my bad

hmaurer23:10:06

I only read “Fuclro 2.6+: Use the fulcro-css.css-injection/upsert-css or fulcro-css.css-injection/style-element to embed the CSS.” and assumed the function just changed namespace but used the same params

tony.kay23:10:15

cool, thanks for checking

hmaurer23:10:06

Can CSS dynamically depend on props?

hmaurer23:10:25

Or you would have fixed css but add classes to elements dynamically based on props?

tony.kay23:10:59

CSS can depend on global state (e.g. atoms). See book.

hmaurer23:10:13

Yup I saw the section on global state and the theme example

tony.kay23:10:36

The overhead of having props involved in CSS in the render loop would be overkill I think

tony.kay23:10:22

cool vs good 🙂

hmaurer23:10:23

Yep I think that makes sense; I was just wondering because the popular CSS-in-JS solutions (styled-element, emotion, etc) all allow props involved in CSS

hmaurer23:10:09

But I agree it’s overkill

hmaurer23:10:54

I actually ran into issues on a project I was working on this summer because I had CSS dynamically depending on props and that generated an ungodly amount of classes; I had to move that styling logic to inline styles :face_with_rolling_eyes:

hmaurer23:10:31

Big thumbs up to whoever wrote the Chrome extension by the way; the state inspector is super slick

tony.kay23:10:55

Anyone lurking have security experience? I think I’ve found a security hole in Sente, and want more eyes on it.

hmaurer23:10:23

am I correct in understanding that there is no built-in rollback procedure for optimistic updates if the server fails?

hmaurer23:10:29

ah nevermind, this seems to be touched on later in the book!