Fork me on GitHub
#fulcro
<
2021-04-15
>
Jakub Holý (HolyJak)14:04:40

In RAD, I'd like to have a report with checkbox controls to show/hide some columns but I see no simple way of doing that, other than creating my own table and row components. Correct?

Panel02:06:23

Digging this one up, would this require a PR to fulcro rad ?

tony.kay04:06:03

no PR required. RAD is designed to allow you to take over rendering and do whatever you want. If you wanted that particular behavior over and over, then you’d write a rendering plugin (like the semantic UI one, or extend the SUI one) and add those features to your UI plugin. The core RAD stuff would need no modifications if you just follow the existing patterns. The only pain I see is that you’d have to have a place in the component query for the data you’d want to hold onto (which columns are hidden). You could do that with hooks in the custom rendering, of course, but it might be nice to at least persist that to app state, and therefore you’d need a query inclusion on the top-level report.

Panel04:06:26

Thanks Tony, So for me the use case is to hide show/hide based on report controls and/or viewport width (ex: only display this field on desktop) I understand I can write my own ui code in the defsc-report but I would rather have it as a ro/show-columns option to use the auto gen render.

Panel05:06:08

I'll try the extend sui plugin route.

Panel10:06:37

Do we have any example of extending ui plug-in ?

Jakub Holý (HolyJak)10:06:30

@U03K8V573EC might have some tips here Personally, I just copy the relevant parts of the plugin to my project, modify, and change sui-controls to point to the modified copy. See e.g. here https://github.com/holyjak/fulcro-billing-app/blob/main/src/shared/billing_app/ui_utils/rad_controls.cljc#L23

Eric Dvorsak14:06:21

I copy namespaces and tune them, refer to my copy in controls map.

donavan14:04:01

I’m busy trying to track down a bug in my app and just want to check that what I’m trying to do is supported. I have a union component that has a to many recursive collection of itself. The union component uses the correct query when in isolation but when the union component is a child it doesn’t use the correct query when its type differs from its parent. It’s denormalised using the parent’s type query.

donavan16:04:50

This is a minimal failing test https://github.com/fulcrologic/fulcro/pull/473/files Is this supposed to work in theory?

donavan18:04:23

I see it’s actually deeper than that… reading the EQL docs it doesn’t seem to me, on first reading at least, that the semantics of recursive unions are spelt out explicitly… but how the AST is structured it seems to me that implicitly a recursive union was intended to only be of one type

donavan19:04:09

…no, I think there isn’t an implied semantics in the AST. I think it might be in fulcro…

donavan20:04:38

So I’ve written an ultra hacky hack that detects if the node we’re trying to normalise is a recursive union and uses the ‘correct’ query for the join-entity. It amounts to an extra cond branch in add-join!. With a little work it will probably just be a modification of the (and recursive? join-entity) branch. At first blush it might need an extra field in the EQL AST to keep the whole union in the children node but that would at most be just an extra key in :union-entry nodes if it’s truly needed. What’s not entirely clear to me though is what are the expected semantics of recursive union components and queries… is it up to Fulcro to decide? Is this worth pursuing, I’d be happy to keep going at the PR if there is a chance this will be used. Granted I haven’t thought too hard about the downsides, I do think this is really useful and I would like to make a case that it is the natural expression of recursive union components.

❤️ 3
tony.kay02:04:38

It has been a very long time since I looked at recursion in the context of unions (which I almost never use). I’m not sure if I ever planned to support the specific use-case you’re trying (honestly have no memory…4-5 yrs ago). We have pretty solid tests for db->tree, so you’re welcome to take a shot at implementing it.

hadils15:04:40

I am trying to use JS async/await functions from 3rd party libraries in Fulcro. I have tried using mutations and state machine to handle the asynchrony of the call, but without success. Does anyone have any experience solving this problem? I appreciate what you can share.

Jakub Holý (HolyJak)15:04:18

Async just returns a promise, no? Cannot you simply (.then promise #(transact this-component...))?

hadils16:04:44

The problem is that I am integrating an image picker with a UI component. So I am trying to pick an image then display it. The image picker is async.

Jakub Holý (HolyJak)16:04:19

What exactly does it do? Pick an image from the disk, upload, display? Or pick an image from a list of images? What do you get from the picker, an url?

hadils16:04:38

It picks an image from storage and returns a url, if one is picked.

hadils16:04:01

I can't put a Promise or a go block inside a render code block. I can't put it in the event handler of a state machine either.

Jakub Holý (HolyJak)17:04:19

I assume the picker is a React component that you nest in your fulcro component and that it expects that you pass it a callback it will call when the user has picked. Or?

hadils17:04:16

I don't seem to have a callback. The React example uses await I am sure I can convert that to a callback though.

hadils19:04:57

You can't have a Promise in the render body of a defsc. Any ideas?

hadils19:04:04

Nvm, I figured it out. Just save the promise and return nil. That works.

👍 3
hadils20:04:04

No @U0522TWDA. I did not solve it. Here is my code:

(defsc Photo [this {:ui/keys [photo] :as props}]
  {:ident         (fn [] [:component/id ::Photo])
   :query         (fn [] [:ui/photo])
   :initial-state (fn [photo] {:ui/photo photo})
   :route-segment ["photo"]}

  (log/debug "Photo props" props)
  (let [foo (-> (.requestMediaLibraryPermissionsAsync ImagePicker)
              (.then (fn [resp]
                       (let [granted? ^boolean (.-granted resp)]
                         (if granted?
                           (-> (.launchImageLibraryAsync ImagePicker)
                             (.then (fn [resp]
                                      (let [cancelled? ^boolean (.-cancelled resp)
                                            photo (.-uri resp)]
                                        (when-not cancelled?
                                          (log/debug "Photo uri=" photo)
                                          (n/ui-view {:style {:flex 1}}
                                            (n/ui-text {} "Hello there!!!!!!")
                                            (n/ui-image
                                              {:source {:uri photo}})
                                            (rne/ui-button {:title       "Accept"
                                                            :buttonStyle {:height 40}
                                                            :onPress     (fn [] (js/alert "You pressed the button!"))}))))))))))))]))
The code inside the .then does not render.

donavan20:04:19

Can you not separate out the ui and promise handling code? You might need some internal state to orchestrate? I don’t think what you’re trying to do will ever work the way you want it to.

donavan20:04:47

The Photo component’s render method (the body of the macro) needs to return react dom constructors. You’re not even using foo in the body

hadils20:04:12

Hi @U0VP19K6K. I have tried to separate the ui and promise code. I tried a transaction, and a statemachine without success. I don't use foo, because it is an object and React was complaining about it. I am stumped as to how to separate the concerns.

donavan20:04:03

Yeah, was just typing that promise then doesn’t return the result of the then method

donavan20:04:24

…as far as I remember, it’s been a while since I’ve used promises

hadils20:04:46

The problem is that the function returns immediately, then computes the result and saves it. I don't know how to deref a promise.

donavan20:04:23

Sorry @UGNMGFJG3 I have food about to arrive but…

donavan20:04:41

I would just keep some local state…

donavan20:04:12

and in the .then callback update that state with whatever is in resp (it looks like it’s a URL)

👍 3
donavan20:04:23

then always return DOM from the component

donavan20:04:34

and check whether you have a URL in the component’s local state

donavan20:04:15

if you do, do whatver you want like displaying the image, if not show a button saying ‘upload the image’

donavan20:04:58

I have to go sorry, will try check back later

donavan20:04:32

You may want to find a plain React tutorial on how to build an upload button… that might help you understand what you need to do?

Jakub Holý (HolyJak)21:04:55

As donovan says. Put the body into photo and inside the then just store the url so the photo component can see it, either in local state or in the Fulcro client db: do there something like (transact! this [(m/set-string :ui/img-url <the url>]))

hadils21:04:13

Thanks @U0522TWDA. How do I know when the uri is ready?

hadils22:04:21

I think the component will update when the transaction occurs.

3
Jakub Holý (HolyJak)06:04:14

I'd do this

(defsc Photo [this {:ui/keys [photo] :as props}]
  {:ident         (fn [] [:component/id ::Photo])
   :query         [:ui/photo]
   :initial-state {:ui/photo :params/photo}
   :route-segment ["photo"]}
  (log/debug "Photo props" props)
  (if photo
    (n/ui-view {:style {:flex 1}}
               (n/ui-text {} "Hello there!!!!!!")
               (n/ui-image {:source {:uri photo}})
               (rne/ui-button {:title       "Accept"
                               :buttonStyle {:height 40}
                               :onPress     (fn [] (js/alert "You pressed the button!"))}))
    (do
      (-> (.requestMediaLibraryPermissionsAsync ImagePicker)
          (.then (fn [resp]
                   (when (.-granted resp)
                     (.launchImageLibraryAsync ImagePicker))))
          (.then (fn [resp]
                   (when (and resp (not (.-cancelled resp)))
                     (m/set-string! this :ui/photo (.-uri resp))))))
      (dom/p "..."))))

hadils14:04:19

Thanks @U0522TWDA. I put the ImagePicker code inside. a mutation. That worked.

👍 3
mruzekw17:04:07

Hi all, I remember seeing a message here about using Fulcro via a React hook. Does anyone know where that’s documented? Thanks

Jakub Holý (HolyJak)19:04:30

What do you need? Use hook based instead of class based components? See the doc string of defsc and the Dev Guide

Jakub Holý (HolyJak)19:04:55

Using Fulcro without Fulcro components, via hooks, see the develop branch and the raw-components namespace

Jakub Holý (HolyJak)19:04:35

Search the code for its usages to find the examples.

mruzekw20:04:50

Thanks @U0522TWDA! I was thinking the second. Using Fulcro without Fulcro components

mruzekw20:04:04

I’ll check that namespace out, thanks

Jakub Holý (HolyJak)20:04:01

It was something with raw and components, not sure about the order :)

tony.kay02:04:29

See the composition cards in the fulcro-3.5 branch ( in progress). Those will be the more ideal items.

tony.kay02:04:55

not released, but relatively stable, so you use a SHA/git via deps

tony.kay02:04:42

I’ve actually been talking about these on this channel for the past few weeks.

donavan20:04:38

So I’ve written an ultra hacky hack that detects if the node we’re trying to normalise is a recursive union and uses the ‘correct’ query for the join-entity. It amounts to an extra cond branch in add-join!. With a little work it will probably just be a modification of the (and recursive? join-entity) branch. At first blush it might need an extra field in the EQL AST to keep the whole union in the children node but that would at most be just an extra key in :union-entry nodes if it’s truly needed. What’s not entirely clear to me though is what are the expected semantics of recursive union components and queries… is it up to Fulcro to decide? Is this worth pursuing, I’d be happy to keep going at the PR if there is a chance this will be used. Granted I haven’t thought too hard about the downsides, I do think this is really useful and I would like to make a case that it is the natural expression of recursive union components.

❤️ 3
thosmos20:04:32

I’m attempting to use ao/style :autocomplete with a RAD :string attribute, but I’m getting an error thrown Uncaught Error: Assert failed: (or (eql/ident? server-property-or-ident) (keyword? server-property-or-ident)) because the :autocomplete/search-key is nil in com.fulcrologic.rad.rendering.semantic-ui.autocomplete/AutocompleteField :initLocalState I’m setting that key in the form like:

fo/field-options {:worktime/task {:autocomplete/search-key :worktime/tasks}}
but it’s missing in the initLocalState call. I think I’m making progress on figuring this out, but maybe there’s a known problem.

thosmos21:04:20

I made a change that fixes the error above, so the remote call is happening, and the options are getting stored, but they’re still not showing up back in the dropdown in the browser. https://github.com/thosmos/fulcro-rad-semantic-ui/tree/bugfix/autocomplete

tony.kay05:04:06

Is the autocomplete used in the demo? I thought I used everything that was supported in the demo. Perhaps you are using the options incorrectly. I don’t remember off the top of my head, which is why I tried to use everything in the demo…could be I overlooked that one, but I know I’ve used autocomplete at some point.

thosmos20:04:40

ok, I’ll check the demo

thosmos20:04:04

It looks like the only :autocomplete in the demo is for an :enum type, not a :string type, and it looks like it only supports having the fo/field-options on the attribute, and not on the form itself. I’ll just use it as a starting point and make my own.

thosmos00:04:57

Putting the options on the attribute with fo/field-options eliminates the PR I made (I thought that field-options was only for forms), but the dropdown options are still not rendering into the UI, even though they’re where they should be at [::autocomplete-id id :ui/options] in the app DB. It appears that when AutocompleteFieldRoot renders, :ui/options is missing, so when it passes that as props to AutocompleteField , it never sees the :ui/options The problem seems to be here:

field              (get-in props [::autocomplete-id id])
which is null and gets passed to the instance like
(ui-autocomplete-field (assoc field
                         ::autocomplete-id id
                         :autocomplete/search-key search-key
                         :autocomplete/debounce-ms debounce-ms)

thosmos01:04:54

oooh, does this have something to do with which rendering strategy I’m using? :optimized-render! kf2/render!

thosmos03:04:45

:optimized-render! mroot/render! did it

thosmos01:04:24

Even though it’s working now, it brings up that I think there’s some confusing different places where field-options are located. for example, here’s the let from po/load-options!

[{:com.fulcrologic.rad.form/keys [field-options]} (comp/component-options form-class)
         field-options  (get field-options qualified-key)
         picker-options (merge attribute field-options)]  
It merges field-options from the form directly into the attribute’s map, rather than into ::form/field-options consistent with what my PR change did. In other words, I think the way the :autocomplete is coded in the demo is inconsistent, and the implementation of the :autocomplete component should be changed (breaking) to get its :autocomplete/keys from either ::form/field-options on a form, or directly from the attribute map, which is consistent with everywhere else.

tony.kay23:04:13

I don’t think there is a need to break it…just add the corrected locations as possibilities via or , where form overrides attribute, and the old location has the least precedence (or form-value correct-attr-location old-attr-location)