I'm trying to port from v0.dev to electric. So far, I have it to the point where an AI can reliably give me the hiccup representation of this page that I'm trying to create. There are enough examples for AI to do that. However, then translating from hiccup into the functions that are called in electric UI is its own challenge. Anybody got a great mechanical program to run this translation? Mostly just needs props and dom/div and that would get me to where I can annotate with e/ tools
I donβt have a tool for this but if you want to write one then you might find hickory to be a useful intermediate format. Itβs more verbose than hiccup but easier to parse because itβs explicit. The library also has the facility to read HTML into hiccup or hickory. https://github.com/clj-commons/hickory
Why does my usage of Form! break? Seems like I should be able to pass a top-level element (dom/tr) like in the https://electric.hyperfiddle.net/tutorial/form_explainer:
(e/defn Entry [id account amount]
(e/client
(forms/Form!
(dom/tr
(e/amb
(dom/td)
(dom/td (Checkbox! false :entry/id id))
(dom/td (dom/text (:account/ident account)))
(dom/td (dom/text amount))
(dom/td)))
:commit (fn [dirty] [dirty nil]))))
does each element in e/amb have to return an Input thingy? I tried that too, but no change. Also tried Checkbox! vs Checkbox and Input! vs Input. If I take out Form! and e/amb and use Checkbox and Input, it works.what is "break", please provide a complete issue report
your problem may be in the caller, i cannot say because you have not told me what the problem even is
making minimal example
OK, it seems that if I pass (dom/td (dom/text "some text")) as one of the e/amb arguments inside a Form!, it works, but if I pass an empty (dom/td), the form will render but on commit it throws "Cannot read properties of null (reading ...
To reproduce, uncomment the empty (dom/td):
(e/defn Testing []
(e/client
(let [!state (atom {:checked? false :text ""})
{:as state :keys [text checked?]} (e/watch !state)
edits (forms/Form!
(dom/tr
(e/amb
(dom/td (dom/text 123.45)) ; this works
;(dom/td) ; this breaks if you uncomment it
(dom/td (Checkbox! :checked? checked?))
(dom/td (Input! :text text))))
:commit (fn [dirty]
(let [prediction nil]
[(merge state dirty) prediction]))
:debug true)]
(e/for [edit edits]
(let [[t data _guess] edit]
(case (do
(reset! !state data)
true)
(t))))
(dom/text "State:" (pr-str state)))))maybe because dom/text calls e/drain, whereas dom/td uses element*? (dom/td (dom/text)) seems to work for me.
^ @dustingetz example above.
please print (e/as-vec edits) so we can confirm there's an unwanted nil
If I insert a (prn 'edits (e/as-vec edits)) before the (e/for [edit edits] ...), it throws before it can print:
EXCEPTION
L851:13 electric-starter-app.main/Testing
function TypeError() { [native code] }: Cannot read properties of null (reading 'cljs$core$IFn$_invoke$arity$0')comment out the e/for, the prn and the e/for are concurrent and racing, they're running in backwards order
I don't see an unexpected nil with the empty (dom/td) uncommented:
edits "[[#object[G__47080] {:checked? false, :text \"arstarst\"} nil]]"
(the nil at end is prediction = nil)^ from: (prn 'edits (pr-str (e/as-vec edits)))
ok, it's not what i thought it was but i can reproduce the problem
workaround with (dom/dt (e/amb) #_(dom/text "str1"))
(dom/td) does return nil (because it puts children inside an implicit do and (do) evaluates to nil. We plan to change that to an implicit (e/amb) but it's potentially breaking and we aren't ready to do it yet
nonetheless, the Form! probably should not crash if a nil sneaks in
i also don't understand {0 nil} in the tutorial debug display, though the form is producing correct transactions
is Form! meant to work in an e/for? Not sure if the workaround applies here:
(e/defn FormThing []
(e/client
(dom/tr (dom/td (dom/text "test row")))
;; this works:
(dom/tr
(dom/td
(forms/Form!
(Input! :text "hi")
:commit (fn [dirty] [dirty nil])
:debug true)))
; but this doesn't:
(forms/Form!
(dom/tr (dom/td (Input! :text "hi")))
:commit (fn [dirty] [dirty nil])
:debug true)))
(e/defn Testing2 []
(e/client
(dom/div
(dom/table
(e/for [x (e/diff-by identity (range 3))]
(FormThing))))))i would appreciate if you just eliminate the word "works" "breaks" from your vocabulary and never say it again
so "works" here just means it renders (for me), and "breaks" means it doesn't render for me
...and it's because Form! mounts a <form> element under <table> where the <tr> should be and that doesn't show up in my browser, so it's not "broken," but it makes Form! tricky to use for the columns in a table row.
Before reading the source for Form!, I had assumed Form! was a conceptual wrapper for inputs, not an actual DOM form element.
Would you be less annoyed if I had said, "it's not showing"? I'll try to be precise but I don't think the animosity is warranted β it's not an attack π€·ββοΈ
after reading this three times i think you are saying there is a css layout issue? Try display:contents on the <form>
unfortunately, display:contents has no effect because my browser has a user agent rule for any form under a table:
:-webkit-any(table, thead, tbody, tfoot, tr) > form:-internal-is-html {
display: none !important;
}
...and I can't seem to override it, but display: table-row would be the right thing if the <form> element functioned as a <tr>. if I change the <form> element to <tr> in the console after rendering, it shows up.don't use table then
yeah, I'll switch to divs and set display:table-row if I want that layout.
it would be convenient if there was a logical (right word?) State! helper that dit not mount anything but just tracked state and handled commit/discard.
what I'm really after is an easy way to collect all nested changes for components under an e/for, where each e/for element has its own row ID, so that I can collect the changes and bulk submit tem to the DB, i.e. only track changes and not inject any DOM elements, but I can probably bubble this up from the subcomponents.
i dont understand that, you're still making me work really hard to parse your meaning. i think you should spend more time working through the todo list examples in the tutorial
if you have a question, please ask a very clear question, it should end in a question mark
I revisited the todos example and it looks like Service is doing what I'm trying to describe: all the todo events (or effects?) seem to "bubble up" up and the (e/Apply Effect args) triggers the handler for each effect, create/edit/toggle, if I understand correctly?
I need to spend more time with todos β there's a lot going on in there... π
yes the service handles requests concurrently, Form! has the ability to merge a set of individual effect requests (such as field level edits) into a single atomic effect request for the batch - this is what the :commit prop does
Do I understand correctly that in v3, for a component to return a stream of values to its caller (e.g. dom/On "click" events), a flow must appear in the tail position of the component, i.e. as the last expr? Or is there another way for events to flow up the chain I'm not aware of?
children values flow out of dom container elements via either implicit do or explict e/amb