Fork me on GitHub
#fulcro
<
2020-04-13
>
tony.kay03:04:58

If the code is clean and it’s just a dependeny and setting, I’d rather it all be in one @mdhaney.

daniel.spaniel16:04:07

Is there a way to use when-mocking such that you mock 2 calls to same method ? i want to check params for first call and second call to see that the variables are different

Jakub Holý (HolyJak)19:04:56

Hi! Could you please clarify how does it relate to Fulcro? What is "when-mocking"?

tony.kay19:04:25

you can put a call count in the arrow, and when-mocking will treat it as a “script”….no count means “greedy”, explicit count means exactly that many calls.

tony.kay19:04:35

@U0522TWDA part of fulcro-spec, some testing tools that make writing tests more specification-like, and a bit easier to read…but inside it is all still clojure.test, so you can mix and match.

👍 4
👀 4
daniel.spaniel19:04:48

oo .. that is good idea @U0CKQ19AQ .. to use call count like that .. thanks 🙂

folcon18:04:27

Is there a good example of using component local state? I’m sure I found it earlier, but now I can’t seem to figure out where I saw it? I basically want to create a component that when given a list, walks the list one item at a time, where the user can hit next/previous… So basically pagination… Sorry, just so used to doing this with hiccup, so this is unfamiliar…

Jakub Holý (HolyJak)19:04:32

The book has a section on pagination. Doesn't that work for you?

folcon09:04:02

Not quite… I’m currently rewatching the videos…

dvingo18:04:32

(comp/set-state! this {:password (evt/target-value %)

dvingo18:04:07

and initLocalState if you want to set a start value http://book.fulcrologic.com/#_react_lifecycle_methods

folcon19:04:41

Ok, I feel like I’m completely doing this wrong… I’m trying to figure out how this works with a mock dataset like this:

[{:account/teams
    [:team/id :team/name
     {:team/project
      [:project/id :project/name
       {:project/tasks [:task/id :task/name]}]}
     {:team/people
      [:person/id :person/name
       {:person/tasks [:task/id :task/name]}]}]}]
So accounts have teams, teams have projects and people, people can have tasks, but only the ones that belong to projects their team is working on. Creating this query generates the data I need, so all I need to do is take all the tasks and then show them as options against the appropriate person. I’ve got no idea how to structure my UI so that the queries generate the correct request? I just keep getting the error get-query calls must be inside a join, but if I make a join the query request is wrong?
[{:account/teams
    [:team/id :team/name
     {[:component/id :team-member-task-selector]
      {:team/people
       [:person/id :person/name
        {:person/tasks [:task/id :task/name]}]}}]}]
Now I’ve been watching the subforms video (https://www.youtube.com/watch?v=qWe7jVqUOys), but that seems to assume that I can do a global request for tasks? But tasks are dependent on the project… Should I be writing some sort of custom resolver that understands this: [:component/id :team-member-task-selector]?

Jakub Holý (HolyJak)19:04:51

Normally you don't write the body of a join manually, you get it from a component so that it includes important Metadata such as an ident for normalization, ie {:project/tasks (comp/get-query Task)} (made up example)

mdhaney19:04:50

It’s hard to say without seeing the code, but it sounds like you maybe don’t fully grasp idents and normalization and how to compose queries? That trips up a lot of people new to Fulcro. Maybe revisit the normalization chapter in the docs, or post your component code and we can try to help out.

folcon19:04:24

So I wrote the initial join manually, to work out the data shape, but I’m trying to generate the correct UI queries in my components:

{:query [:team/id :team/name
           {[:component/id :team-member-task-selector]
            {:team/people (comp/get-query PersonEntry)}}]}

folcon20:04:18

Here:

(defsc TaskItem [this {:task/keys [id name] :as props}]
  {:query [:task/id :task/name]
   :ident :task/id}
  (div :.item
    (dom/h6 name)
    (p (pr-str props))))

(def ui-task-item (comp/factory TaskItem {:keyfn :task/id}))

(defsc Project [this {:project/keys [id name tasks] :as props}]
  {:query [:project/id :project/name {:project/tasks (comp/get-query TaskItem)}]
   :ident :project/id}
  (div
    (dom/h5 name)
    (p (pr-str props))
    (map ui-task-item tasks)))

(def ui-project (comp/factory Project {:keyfn :project/id}))

(defsc PersonEntry [this {:person/keys [id name tasks] :as props}]
  {:query [:person/id :person/name :person/tasks]
   :ident :person/id}
  (div :.ui.segment
    (dom/label name)
    (p (pr-str props))
    (map ui-task-item tasks)))

(def ui-person-entry (comp/factory PersonEntry {:keyfn :person/id}))

(defsc TeamListItem [this {:team/keys [id name #_people #_project] :as props}]
  {:query [:team/id :team/name {[:component/id :team-member-task-selector] {:team/people (comp/get-query DataEntry)}} #_{:team/project (comp/get-query Project)}]
   :ident :team/id}
  (div
    (dom/h4 name)
    ;(p (pr-str props))
    #_#_
    (ui-project project)

    (map ui-person-entry people)))

(def ui-team-list-item (comp/factory TeamListItem {:keyfn :team/id}))

(defsc TeamList [this {:account/keys [teams] :as props}]
  {:query         [{:account/teams (comp/get-query TeamListItem)}]
   :initial-state {:account/teams {}}
   :ident         (fn [] [:component/id :teams])
   :route-segment ["teams"]}
  (div :.ui.container.segment
    (h3 "Teams")
    (div :.ui.list (map ui-team-list-item teams))))

folcon20:04:40

I’ve commented out bits that weren’t working… The component is called [:component/id :team-member-task-selector] Because I’d like to feed it a team and page through the team members, but I’ll work that out =)… What I’m unclear on is how exactly do I specify how to get the query I want?

Jakub Holý (HolyJak)20:04:05

This join {[:component/id :team-member-task-selector] {:team/people (comp/get-query PersonEntry)}}]} looks strange. Normally you have a query for attributes mixed with simple joins such as {:team/people (comp/get-query PersonEntry)} but here you have 2-level join including a hard-coded ident of the singleton component [:component/id :team-member-task-selector]. (the code of which I haven't seen in this thread?). So you have a TeamListItem, which each corresponds to a unique entity, including a singleton query?! I do not grasp what you are trying to achieve. But if team-member-task-selector indeed is a singleton, it should correspond to a top-level key in the DB and be accessed via a Link Query http://book.fulcrologic.com/#_link_queries

folcon20:04:47

So this is what the original UI Code looked like:

(defsc TaskItem [this {:task/keys [id name] :as props}]
  {:query [:task/id :task/name]
   :ident :task/id}
  (div :.item
    (dom/h6 name)
    (p (pr-str props))))

(def ui-task-item (comp/factory TaskItem {:keyfn :task/id}))

(defsc Project [this {:project/keys [id name tasks] :as props}]
  {:query [:project/id :project/name {:project/tasks (comp/get-query TaskItem)}]
   :ident :project/id}
  (div
    (dom/h5 name)
    (p (pr-str props))
    (map ui-task-item tasks)))

(def ui-project (comp/factory Project {:keyfn :project/id}))

(defsc PersonEntry [this {:person/keys [id name tasks] :as props}]
  {:query [:person/id :person/name {:person/tasks (comp/get-query Project)}]
   :ident :person/id}
  (div :.ui.segment
    (dom/label name)
    (p (pr-str props))
    (map ui-task-item tasks)))

(def ui-person-entry (comp/factory PersonEntry {:keyfn :person/id}))

(defsc TeamMemberTaskSelector [this {:team/keys [id name people project] :as props}]
  {:query [:team/id :team/name {:team/people (comp/get-query PersonEntry)} {:team/project (comp/get-query Project)}]
   :ident :team/id}
  (div
    (dom/h4 name)
    ;(p (pr-str props))
    ;#_#_
    (ui-project project)

    (map ui-person-entry people)))

(def ui-team-member-task-selector (comp/factory TeamMemberTaskSelector {:keyfn :team/id}))

(defsc TeamList [this {:account/keys [teams] :as props}]
  {:query         [{:account/teams (comp/get-query TeamMemberTaskSelector)}]
   :initial-state {:account/teams {}}
   :ident         (fn [] [:component/id :teams])
   :route-segment ["teams"]}
  (div :.ui.container.segment
    (h3 "Teams")
    (div :.ui.list (map ui-team-member-task-selector teams)))
With the query:
[{:account/teams
    [:team/id
     :team/name
     {:team/people
      [:person/id
       :person/name
       {:person/tasks [:project/id :project/name {:project/tasks [:task/id :task/name]}]}]}
     {:team/project
      [:project/id :project/name {:project/tasks [:task/id :task/name]}]}]}]
I’m trying to get:
[:account/teams :team/people :person/tasks] to join with [:account/teams :team/project :project/tasks]
So that when you look at a person, you see the project tasks which they could be assigned to.

👍 4
folcon20:04:13

Sorry, I thought the way to do it was inside some component, and was trying to work out how to coerce the query…

folcon20:04:43

I’ll check out that section of the book =)…

folcon20:04:40

Would I use a link query to pull all the :project/tasks and then filter to the ones that match in the UI?

Jakub Holý (HolyJak)20:04:39

I will think about it. But perhaps simpler is to make a resolver that, given a person, returns the tasks assignable to her? If that is inefficient then returning all project tasks and filtering in the UI might be an option. But I am no expert (yet :))

folcon20:04:29

Hmm, that may be the way to do it =)… Thanks, Appreciate the help!

folcon21:04:18

Just found the section on lazy loading tabs, I think I can repurpose this! =)…

tony.kay19:04:53

I’m working hard on getting better Hooks support. There’s some really cool stuff you’re about to be able to do using React hooks 🙂

8
🔥 36