Fork me on GitHub
#hoplon
<
2018-01-21
>
thedavidmeister00:01:25

@chromalchemy i think you need to profile what's actually going on

thedavidmeister00:01:50

i'm assuming you're calling filter or remove under the hood in those fns

thedavidmeister00:01:21

which just walk the sequence and apply their predicate to each item, which should be fast enough

thedavidmeister00:01:24

you could maybe get some minor speed benefits doing a single pass with a combined test, e.g. transducers

thedavidmeister00:01:47

but not 5-10s worth i wouldn't have thought...

thedavidmeister00:01:44

datascript/datomic/datalog is an example of something more generalised for querying the state of your items

thedavidmeister00:01:03

@chromalchemy if you're rearranging list items and you have internal state for your items then you'll have a much easier time managing that state with a keyed list https://reactjs.org/docs/lists-and-keys.html

thedavidmeister01:01:06

even if you carefully wrap all your own state in shared scopes that you manage properly, there's still browser state like focus, values of inputs, cursor positions, selections, etc. (much of which JS cannot even see) that can get mixed up if you can't lock a specific DOM element to a keyed item

chromalchemy01:01:54

I have a UUID for each item. I've been using this to query the list and get/set/test map vals. I was assuming that this was not great, that constantly scanning the list for item id's was inefficient, and that I should be using the implicit index in the vector. But I'm seeing maybe I was on the right track?

chromalchemy01:01:25

I see that the key does not necessarily have to be a UUID, just locally unique.

chromalchemy01:01:24

Am I correct that since my "items" each have an unique :id, then it is a "keyed list"?

thedavidmeister01:01:41

@chromalchemy in the keyed-for-tpl you provide a key fn

thedavidmeister01:01:51

if you don't provide one then identity is used

thedavidmeister01:01:16

which would really only make sense if your items never changed, but you wanted to rearrange them

thedavidmeister01:01:38

the tests use keywords as an example of that, so a list like [:a :b :c :d] the key for an item is itself

thedavidmeister01:01:48

for your case, :id would be your keyfn

thedavidmeister01:01:48

so the work needed is something like

thedavidmeister01:01:13

- map keyfn to the list

thedavidmeister01:01:42

- walk this list of keys and lookup to see if we already have an el in the cache or we need a new one

thedavidmeister01:01:55

- return a new list of els based on the key lookups

thedavidmeister01:01:23

- call merge-kids to actually put the els in the DOM because there's no guarantee they line up with the last iteration

thedavidmeister01:01:03

building els and calling merge-kids at the end is probably the slowest parts of that

thedavidmeister01:01:13

but its all pretty new!

thedavidmeister01:01:53

in the future, merge-kids could potentially even become "key aware" and do smarter/faster diffs based on that

chromalchemy01:01:19

Ok thanks, I'll try to walk through, digest that process.

chromalchemy01:01:03

I'm a bit confused about keyfn though. What does it return? Or does it apply the key/val to each list item?

thedavidmeister01:01:18

given an item, return its key

thedavidmeister01:01:31

or its "identity" in a sense

thedavidmeister01:01:43

you're just telling hoplon what about the item makes it unique

thedavidmeister01:01:54

if you have a uuid that's easy, just return that

chromalchemy01:01:57

The id value itself right? Not simply the keyword the value is on?

thedavidmeister01:01:12

ah well keywords are fns

chromalchemy01:01:38

(keyfn myitem) => 23

thedavidmeister01:01:40

so :id is both the key and the function to lookup the key 😛

chromalchemy01:01:28

Ok, just wanted to make sure, lol

thedavidmeister01:01:45

yup, but let's say you needed to combine two things

thedavidmeister01:01:02

like um :id and :type or something

thedavidmeister01:01:11

you could do (juxt :id :type) for your keyfn

thedavidmeister01:01:02

like how a primary key in a db can be a combination of a few fields

chromalchemy01:01:00

So basically the keyfn is the "query" on the list?

thedavidmeister01:01:32

uh, it is, but specifically it's the query to tell hoplon how to line things up internally

thedavidmeister01:01:10

say i had [{:a 1 :b 2} {:a 3 :b 4}] and i passed that to the loop

thedavidmeister01:01:42

the loop needs to build an el for each item

thedavidmeister01:01:15

but it also needs to know how to retrieve that same el next time it gets a new version of the list

thedavidmeister01:01:23

so if the next iteration of the list looks like

thedavidmeister01:01:09

if the keyfn is :a then we re-use the first el from before and remove the second el

thedavidmeister01:01:25

if the keyfn is :b we need a brand new el and remove all the other els

thedavidmeister01:01:37

if the keyfn is (juxt [:a :b]) then we need a new el, and we'll never reuse this new one until we see a new item with both :a 1 and :b 5

thedavidmeister01:01:24

if the keyfn is identity (the default) then we'll only reuse the el if we see a new item that is = to the entirety of the current item

chromalchemy01:01:31

ok. And this is all in the context of keyed-for-tpl? And "re-use" means the same dom node, but it can have updated values (outside it's key)?

thedavidmeister01:01:55

yeah, same DOM node new values

thedavidmeister01:01:51

the important difference between a normal for-tpl is that the mapping between items and DOM nodes is based on the "identity/key" of the item rather than the position in the list

thedavidmeister01:01:10

which is crucial for re-ordering, because if you think about it, the whole concept of "reorder" is grounded in the idea that we have some "unique things" that can be reordered in the first place 😛

chromalchemy01:01:02

True, sounds obvious in retrospect. So the keyfn returns an item's unique value. And the keyed-for-tpl tests for equality of that value across updates to better manage (diff) the dom.

thedavidmeister01:01:38

but currently it's more about ensuring sensible state downstream than efficiency

thedavidmeister01:01:53

although i'm sure we could get improvements on the diffing later too 🙂

chromalchemy01:01:05

With the normal for-tpl, I would filter the list outside of the loop, then feed the filtered results in. But my understanding is that each iteration of any outside filtering, generates a whole new list in memory under the hood. Even if you only add/remove one thing? There's no sharing, and potentially a memory overload if you have too many filterings going on.

thedavidmeister01:01:28

depends how you do it

thedavidmeister01:01:44

subvec for example uses structural sharing, so it's O(1)

thedavidmeister01:01:58

but it's not really a filter per se

thedavidmeister02:01:06

calling filter yes, that will make a new list each time

thedavidmeister02:01:37

actually tbh i'm not even 100% sure of that, lol

chromalchemy02:01:41

I've been using Specter a lot, so not sure what's going on under it's hood.

thedavidmeister02:01:54

part of clojure's immutability is they do stuff "under the hood" to make things faster

thedavidmeister02:01:12

even if filter currently makes a new list, it might not in a future version...

thedavidmeister02:01:34

i'd be surprised if the making of a new list was causing problems

chromalchemy02:01:37

Like changing [:a :b :c] to [:a :c :b] results in 2 lists in memory?

thedavidmeister02:01:58

i dunno, i think "it depends"

thedavidmeister02:01:37

i think you have to research structural sharing in clojure to find out

chromalchemy02:01:47

Hmm. Ok I assumed some clojure immutable magic was going on. It only just looks like I'm duplicating all over the place.

thedavidmeister02:01:10

i think there's a lot of magic making it safer than it looks 😛

thedavidmeister02:01:25

regardless, you can use transducers if you're worried about this

thedavidmeister02:01:56

i highly recommend profiling before you go down this road though, it sounds like a micro optimisation to me

thedavidmeister02:01:37

e.g. i'd expect the process of walking the list and applying a predicate to every item to decide what to keep is going to be riskier for CPU than the mechanical process of building the resultant list

thedavidmeister02:01:50

even if you only remove one item, filter still needs to check every item

chromalchemy02:01:22

What do you think of the cascading formula cells to filter a list, (per above meta-code), before feeding to for-tpl. If I change the params around I get a lot of different versions of the list. But it seemed like you didn't think this would necessarily lead to a memory explosion in the for-tpl.

thedavidmeister02:01:46

i do things like that reasonably regularly

thedavidmeister02:01:59

i also use tools that have a query language regularly

thedavidmeister02:01:51

inside the for-tpl (at least my proposed versions) i don't think there's much you can "blow up"

thedavidmeister02:01:41

the for-tpl only sees the final output of your prior manipulations, and then builds item cells based on the key/position of the items list, and maps them to DOM elements based on the key/position

thedavidmeister02:01:31

this is where i got to last night with a simplified for-tpl:

thedavidmeister02:01:34

(defn loop-tpl*
 [items tpl]
 (let [els (cell [])
       itemsv (cell= (vec items))
       items-count (cell= (count items))]
  (do-watch items-count
   (fn [_ n]
    (when (< (count @els) n)
     (doseq [i (range (count @els) n)]
      (swap! els assoc i (tpl (cell= (get itemsv i nil))))))))
  (cell= (subvec els 0 items-count))))

thedavidmeister02:01:51

there's not really a lot to break there...

thedavidmeister02:01:05

and walking a list and applying a pred to filter is O(n) so it's not going to "blow up", just get linearly slower as you add more stuff to the list

chromalchemy02:01:04

"i'd expect the process of walking the list and applying a predicate to every item to decide what to keep is going to be riskier for CPU than the mechanical process of building the resultant list. Even if you only remove one item, filter still needs to check every item" I assume this is what's bogging me down more than anything. How can I avoid this constant walking (event to just pick out a single item with a given UUID)? That's why I was thinking I needed to use the implicit vector index. But the react article seemed to warn against that?

chromalchemy02:01:32

ps. I haven't had a chance to get into Datascript yet 😛

thedavidmeister02:01:37

well you can't is the short answer

chromalchemy02:01:40

but looking forward to it

thedavidmeister02:01:51

because if you have any downstream local state

thedavidmeister02:01:04

you'll just need to do the same calculations there, but probably more than once

thedavidmeister02:01:38

using my example from the issue i raised

thedavidmeister02:01:41

(let [state (cell {})] ; <-- note the additional state _outside_ the defn
 (defn my-item
  [item]
  (let [k (cell= ...) ; <-- get an id from item
          expand? (j/cell= (get state k) (partial swap! state assoc id))] ; <-- e.g. javelin readme
   ...)))

(for-tpl [x my-list] (my-item x))

thedavidmeister02:01:14

we're now just applying keyfn ad-hoc and using it to lookup values from a state cell per DOM element

thedavidmeister02:01:41

instead of doing it once up front and letting the DOM elements downstream take a consistent view of an item without additional accounting

thedavidmeister02:01:34

essentially there's no such thing as "changing a list" in clojure, as lists are immutable values

thedavidmeister02:01:15

you make a new list, and if there's any relationship you want to preserve between list A and list B then you need to make that explicit

thedavidmeister02:01:22

which is going to involve a walk somewhere

thedavidmeister02:01:29

might as well make sure it's only 1 walk and not 50

chromalchemy02:01:05

Yes, ok. I'm in the 50 walks camp right now, lulled by the idea that by mutating a named cell I have a "consistent" list.

thedavidmeister02:01:04

you can use a for-tpl and get some (probably minor) speed benefits if:

thedavidmeister02:01:27

- your downstream DOM elements are totally read-only, not even a11y browser interactions are allowed, like focus states

thedavidmeister02:01:56

- your downstream DOM elements have no internal state that references the identity/key of an item

chromalchemy02:01:41

I don't understand that last one. Can you give an example?

thedavidmeister02:01:59

^^ like the expand? above

thedavidmeister02:01:22

like "read more" with a popdown showing more info

thedavidmeister02:01:36

you want that to be tied to an item, not a DOM element

thedavidmeister02:01:30

if i click "read more" on item 1, then reverse my list, after rearranging i want the last item to be expanded, not the first

thedavidmeister02:01:00

i guess i'm just saying the same thing twice 😛 which is that you've got internal state of elements to worry about, which could either be cljs/cell state or browser internal state

chromalchemy02:01:20

I really don't want to worry about browser internal state if can avoid it. So by "item" you mean the item map, presumable contained in a cell. So If I have a "read more" link, I walk that cell for the "more" data, to feed to the view (which might create a new dom element)...?

thedavidmeister02:01:45

ah so for the purpose of what we're talking about here there is very little difference between a keyed and non-keyed for-tpl

thedavidmeister02:01:00

a (cell [ ... ]) goes in, gets turned into [(cell= ... ) (cell= ... ) (cell= ... )] or {k1 (cell= ... ) k2 (cell= ... ) k3 (cell= ... )}

thedavidmeister02:01:00

actually that's not quite right lol

thedavidmeister02:01:09

hmmm oversimplified too much 😛

thedavidmeister02:01:26

it's actually more like

thedavidmeister02:01:29

(cell [ ... ]) goes in, then we check to see if there is an appropriate DOM element for each item, if there is not then we make one like (my-el (j/cell= (get items k nil)))

thedavidmeister02:01:30

when you change the items list, we just check to see if new individual item cell/DOM mappings need to be made

thedavidmeister02:01:56

if you provide a sensible key to keyed-for-tpl you won't need to worry about browser state getting out of sync with your uuids

thedavidmeister02:01:49

my point is just that even though there's some internal stuff going on to make that happen, you can't make it faster by avoiding it because either A. you'll have to do equal or greater work downstream or B. you will have subtle bugs caused by stale/inconsistent state

chromalchemy02:01:24

I think I'm doing what you talked about earlier w/ respect to downstream identity:

(defc state 1)

(defc items [{:id 1} {:id 2}])

(for-tpl [x @items]
   (elem :color (cell= (when (= (:id @x) @state) :red))

thedavidmeister02:01:18

(defc items [{:id 1} {:id 2}])
(keyed-for-tpl nil :id [x items]
  (elem :colour (cell= (when (= (:id x) 1) :red)))))

thedavidmeister02:01:08

^^ looks like this with a key

thedavidmeister02:01:32

ignore the first nil in keyed-for-tpl for now 😛

thedavidmeister02:01:29

but what's also important, and is hard to see from this example, is that often these things are in different parts of your codebase

thedavidmeister02:01:39

your for-tpl might be in some ns calling elem from another ns, and elem might also be called from multiple different places, so it makes it tedious to track all this state across multiple places and contexts

thedavidmeister02:01:51

you really don't want your elem to be "reaching out" to global state in order to get the job done 😕

chromalchemy02:01:18

I think I see. But how do you change the 1 (state value) in your example?

thedavidmeister02:01:50

you can make it local to elem safely

thedavidmeister02:01:22

(defn my-item
 [item]
 (let [expand? (cell false)]
  ...))

(keyed-for-tpl nil :id [x my-list] (my-item x))

thedavidmeister02:01:31

(let [state (cell {})] ; <-- note the additional state _outside_ the defn
 (defn my-item
  [item]
  (let [k (cell= (:id item)) ; <-- get an id from item
          expand? (j/cell= (get state k) (partial swap! state assoc id))] ; <-- e.g. javelin readme
   ...)))

(for-tpl [x my-list] (my-item x))

thedavidmeister02:01:30

the boilerplate in the latter needs to be repeated for every single cell that i want to represent state for a given item

chromalchemy02:01:53

I think I'll have to play about a bit to fully appreciate all this. Right now it's still a bit fuzzy cause

(defc items [{:id 1} {:id 2}])

(for-tpl [x items]
  (elem :colour (cell= (when (= (:id x) 1) :red))))) 

looks almost the same as 

(keyed-for-tpl nil :id [x items]
  (elem :colour (cell= (when (= (:id x) 1) :red)))))
But I understand that this example is overly stripped down.

thedavidmeister02:01:40

well actually your example works in both for-tpl and keyed-tpl 😛

thedavidmeister02:01:58

because your cell attached to :colour is using part of the value of item

thedavidmeister02:01:25

expand? is different because you can't read it from item

thedavidmeister02:01:45

it is related to item but it isn't part of that data structure

thedavidmeister02:01:45

(defn elem [item]
 (let [toggle? (cell false)]
   (div
    :color (cell= (if toggle? :red :blue))
    :click #(swap! toggle? not))))

(defc items [{:id 1} {:id 2}])
(keyed-for-tpl nil :id [x items]
  (elem x))

thedavidmeister03:01:26

when you click the elem it swaps between red and blue

thedavidmeister03:01:04

so if i click :id 2 it goes red, if i then reverse the list so that :id 2 is at the start of the list, do i want the first DOM element or the second one to be red?

thedavidmeister03:01:27

for-tpl would keep the second element red, so :id 1 would be red now

thedavidmeister03:01:48

keyed-for-tpl would make sure the item with :id 2 is red, so the first element would be red now

chromalchemy03:01:18

I think I see now. I think in that situation I would have tried to create a permenant {:id 1 :toggled? true} attribute to get/set/test. But that seemed verbose and redundant and more walking..😬 Using that local state looks a lot more elegant.

thedavidmeister03:01:49

yes of course you can keep injecting all this state into item

thedavidmeister03:01:12

but you're going to end up with dozens or hundreds of very similar looking keys quite fast >.<

thedavidmeister03:01:13

and none of those extra keys really add value to item tbh

thedavidmeister03:01:57

toggled? has nothing to do with the fundamental definition of the item, it has to do with the internal state of a particular DOM element that is rendering that item in a certain way

chromalchemy03:01:27

Yes, that's the feeling I was getting. My semantic data was getting littered with view-state complexity! I want to compute the view stuff implicitly, but was searching for an efficient means.

chromalchemy03:01:34

Well thank you for taking the time to explain it in depth. This will help me tremendously. My app is all about filtering and sorting a list, and this clarifies where I need to head, and free's me from some gloom about feeling I was plodding down a dead end path.

chromalchemy03:01:04

I am currently trying to update to Hoplon 7.1 but getting some .spec errors. Do I need 7.1 to use the keyed-for-tpl? or could I plug it into 7.0.3?

thedavidmeister03:01:42

actually keyed-for-tpl is not available in anything yet 😛 waiting on @flyboarder to push a snapshot when he's available 🙂

thedavidmeister03:01:59

but it would be in 7.2-SNAPSHOT getting ready for the 7.3 release

thedavidmeister03:01:38

what errors are you getting from spec?

chromalchemy03:01:52

I got the one about serializing the error message, but I was able to follow your trick of inserting (prn) code into boot.cljs

thedavidmeister03:01:23

ah yeah, well that error just hides another error

chromalchemy03:01:07

now I'm getting stuff like

{ :cause 
     "Call to clojure.core/defn did not conform to spec:\nIn: [1 0] val: {:keys [url open-new?], :or {:open-new? false}, :as attr} fails spec: :clojure.core.specs.alpha/arg-list at: [:args :bs :arity-n :bodies :args] predicate: vector?\r\nIn: [1 0] val: ({:keys [url open-new?], :or {:open-new? false}, :as attr} kids) fails spec: :clojure.core.specs.alpha/arg-list at: [:args :bs :arity-1 :args] predicate: (cat :args (* :clojure.core.specs.alpha/binding-form) :varargs (? (cat :amp #{(quote &)} :form :clojure.core.specs.alpha/binding-form))),  Extra input\r\n"
  :data 
     {:clojure.spec.alpha/problems , 
        ( 
          {:path [:args :bs :arity-1 :args], :reason "Extra input", :pred (clojure.spec.alpha/cat :args (clojure.spec.alpha/* :clojure.core.specs.alpha/binding-form) :varargs (clojure.spec.alpha/? (clojure.spec.alpha/cat :amp #{(quote &)} :form :clojure.core.specs.alpha/binding-form))), :val ({:keys [url open-new?], :or {:open-new? false}, :as attr} kids), :via [:clojure.core.specs.alpha/defn-args :clojure.core.specs.alpha/args+body :clojure.core.specs.alpha/arg-list :clojure.core.specs.alpha/arg-list], :in [1 0]}
          
          {:path [:args :bs :arity-n :bodies :args], :pred clojure.core/vector?, :val {:keys [url open-new?], :or {:open-new? false}, :as attr}, :via [:clojure.core.specs.alpha/defn-args :clojure.core.specs.alpha/args+body :clojure.core.specs.alpha/args+body :clojure.core.specs.alpha/args+body :clojure.core.specs.alpha/arg-list :clojure.core.specs.alpha/arg-list], :in [1 0]})
      :clojure.spec.alpha/spec #object[clojure.spec.alpha$regex_spec_impl$reify__2436 0x13d54144 "clojure.spec.alpha$regex_spec_impl$reify__2436@13d54144"], :clojure.spec.alpha/value , 
        (link [{:keys [url open-new?], :or {:open-new? false}, :as attr} kids] (elem :td :underline :tc :blue :m :pointer :click (fn* [] (.open js/window url (if (not open-new?) "_self"))) (dissoc attr :url :open-new?) kids))
      :clojure.spec.alpha/args 
        (link [{:keys [url open-new?], :or {:open-new? false}, :as attr} kids] (elem :td :underline :tc :blue :m :pointer :click (fn* [] (.open js/window url (if (not open-new?) "_self"))) (dissoc attr :url :open-new?) kids))}
  :via
    [{:type clojure.lang.ExceptionInfo
      :message 
            "src\\elements\\misc.cljs [line 90, col 1] Call to clojure.core/defn did not conform to spec:\nIn: [1 0] val: {:keys [url open-new?], :or {:open-new? false}, :as attr} fails spec: :clojure.core.specs.alpha/arg-list at: [:args :bs :arity-n :bodies :args] predicate: vector?\r\nIn: [1 0] val: ({:keys [url open-new?], :or {:open-new? false}, :as attr} kids) fails spec: :clojure.core.specs.alpha/arg-list at: [:args :bs :arity-1 :args] predicate: (cat :args (* :clojure.core.specs.alpha/binding-form) :varargs (? (cat :amp #{(quote &)} :form :clojure.core.specs.alpha/binding-form))),  Extra input\r\n"}]}
But haven't had a chance to dig into it yet.

thedavidmeister03:01:26

{:keys [url open-new?], :or {:open-new? false}, :as attr}

thedavidmeister03:01:32

Call to clojure.core/defn did not conform to spec

thedavidmeister03:01:58

yeah looks like that's clojure itself complaining about some of your syntax

thedavidmeister03:01:16

you probably just got bumped up a few versions of cljs to support 7.1

chromalchemy03:01:43

Yeah, I think so. Hopefully I wont hit a wall with it 😅

chromalchemy03:01:56

My current codebase is in Hoplon.UI, which hasn't been updated in a while. I'm not sure If i can update to 7.2 if there are breaking changes, at least until a major refactoring. Can I cut and paste the keyed-for-tple stuff on top of a 7.1 base. Or does it depend on 7.2 internals?

thedavidmeister03:01:59

well if you get stuck let us know

thedavidmeister03:01:14

yeah you can paste it in

thedavidmeister03:01:29

the only thing is that it is so new that it will probably evolve a bit

thedavidmeister03:01:41

so don't get too attached to the version you paste

thedavidmeister03:01:55

in theory there aren't breaking changes in 7.2, it was a big refactor internally but the idea was to keep the external APIs the same or extended with new things only

chromalchemy03:01:47

Sure. I think anything might help me out right now. Especially just learning how to orient to that keyed internal state you so eloquently demonstrated.

chromalchemy03:01:54

Ok I'll give it a try

chromalchemy03:01:07

What about using a 7.0.3 base, just in case?

thedavidmeister03:01:34

ah actually hmmm

thedavidmeister03:01:47

it won't totally work without the new version of merge-kids

thedavidmeister03:01:54

i forgot about that

thedavidmeister03:01:37

since for-tpl never re-arranges anything, hoplon doesn't actually support re-arranging >.<

thedavidmeister03:01:53

i had to add that support while i was working on keyed-for-tpl

thedavidmeister03:01:20

which also sucks for me because it means i can't use it either until the snapshot goes up, lol

thedavidmeister03:01:03

on that note i think i'm going to go get a ☕

thedavidmeister03:01:21

don't stress too much about 7.1 -> 7.2 though, i don't see that being a major hurdle at all

chromalchemy03:01:15

Ha. Lifestyle of the bleeding edge! I'll probably refactor and catch up to 7.2/7.3 asap either way. Thank you for the help, and all your recent work!!!

thedavidmeister03:01:37

np, glad to help

thedavidmeister03:01:05

and also it's great if other ppl start using what i'm writing so i can get feedback and make sure its rock solid

chromalchemy03:01:17

Sure. I'll test and give some feedback as soon as I'm able. Later

thedavidmeister03:01:24

:thumbsup: catch

flyboarder03:01:29

@thedavidmeister I’ll get it in snapshot soon I promise

flyboarder03:01:47

Nice explanation! This is a great enhancement!

thedavidmeister04:01:30

@flyboarder hah, all good, i've got plenty of other things to keep me busy 😉

thedavidmeister04:01:11

i think this also fits well with the "easy interop with 3rd party libs" principle, as they will often have their own internal state that hoplon shouldn't damage or need to know about - imagine a WYSIWYG editor attached to an item, we don't want to be thrashing a setData() method on every reorder, that could be very expensive as it would internally trigger a parse/render cycle on the editor for every item

thedavidmeister04:01:41

@chromalchemy https://www.youtube.com/watch?v=6mTbuzafcII <-- good watching if you're concerned about lots of list rebuilding

thedavidmeister04:01:06

you're basically doing what he describes at around 6:00

ntzanos21:01:01

Hi all. I am new to hoplon as well as clojure(script) so please excuse my naive question

ntzanos21:01:53

I am trying to get the value of a plain text input field and create dynamically a number of text input fields based on the number I will get. I am not sure how to get this number.

ntzanos23:01:05

Fixed 🙂

thedavidmeister23:01:20

(let [c (cell 0)]
 (input :value c :input #(reset! c @%))
 (for-tpl [i (cell= (range (int c)))]
  (input)))

thedavidmeister23:01:30

@corelon ^^ something like this