Fork me on GitHub
#hoplon
<
2016-03-28
>
dm308:03:24

There's defelem and defelem+ in the current master

dm308:03:53

which I'm not exactly sure how and when to use properly

dm308:03:06

seems like defelem+ accepts cells with kids and attrs that will get propagated into DOM through modified appendChild, etc

dm308:03:35

there's also a static macro

dm308:03:03

which doesn't seem like it works at all

dm308:03:03

I think would be good to cleanup the API and have docstrings for 6.0.0

dm308:03:36

@micha if you could explain ^ I could work on docstrings

dm308:03:13

also what's the use of hoplon.core/sexp macro?

dm313:03:06

@thedavidmeister: have you read my today's ramblings? simple_smile

thedavidmeister13:03:57

@dm3: yeah i saw you mention it ^^ so i put an issue up, because i’ve wondered what they are before

micha14:03:51

defelem+ is an experimental thing

micha14:03:55

it lets you do this:

micha14:03:19

(defc items ["one" "two" "three"])
(defc colors (cycle ["red" "blue"]))
(defc= color (first colors))

(with-interval 1000 (swap! colors rest))
(with-timeout 1000 (swap! items conj "four"))

(defelem+ foo
  [{:keys [color]} kids]
  (ul
    :css (cell= {:color color})
    (loop-tpl :bindings [kid kids]
      (li kid))))
      
(html
  (body
    (div
      (h1 "hello world")
      (foo
        :color color
        (p "this is the beginning")
        (loop-tpl :bindings [item items]
          (p :text item))
        (p "this is the end"))))) 

micha14:03:35

notice the loop-tpl in the arguments to foo at the bottom there

micha14:03:18

when you make a defelem+ you basically implement appendChild specially for that element

micha14:03:36

in this case, if you do (foo "hello") you will get this html:

micha14:03:55

<ul>
  <li>hello</li>
</ul>

micha14:03:59

if you do this

micha14:03:19

(def f1 (foo "hello"))

(f1 "there")

micha14:03:22

you will get

micha14:03:40

<ul>
  <li>hello</li>
  <li>there</li>
</ul>

micha14:03:52

notice the li wrapping of "there"

dm314:03:56

so what would be the advantage of defelem over defelem+?

micha14:03:08

compare to this

micha14:03:38

(defelem foo
  [attr kids]
  (ul
    (map li kids)))

micha14:03:43

in that case if you do

micha14:03:04

(def f1 (foo "hello"))

(f1 "there")

micha14:03:11

you will end up with

micha14:03:26

<ul>
  <li>hello</li>
  there
</ul>

micha14:03:38

do you see why?

micha14:03:06

defelem is just some syntax sugar over defn

micha14:03:21

defelem+ is not defn really

micha14:03:20

when you invoke a dom element as a function, hoplon implements that as appendChild

dm314:03:27

wonder what happens when you do

(defelem+ foo
  [attrs kids]
  (ul
    (cell= (map li kids)))

micha14:03:38

that won't work either

micha14:03:50

because with defelem+ attr and kids are cells

micha14:03:59

that's the key really

micha14:03:10

so you don't use map inside the body there

micha14:03:14

you use loop-tpl

dm314:03:23

yeah, I see

micha14:03:27

that would work

micha14:03:34

but you know, the issues with memory

dm314:03:53

I see how defelem+ is more powerful

micha14:03:56

also attrs isn't allowed with defelem+

micha14:03:02

you need to destructure

micha14:03:13

that's how you declare the attributes that you are interested in

dm314:03:28

and if you want to pass the attrs through?

micha14:03:46

that happens automatically

micha14:03:59

all the ones you don't declare in {:keys [...]}

dm314:03:22

does that also work for defelem? I should have tried ages ago!

micha14:03:36

no, it can't really

micha14:03:51

actually maybe it could

micha14:03:16

but with defelem+ the arguments have a different meaning

micha14:03:33

like (defelem+ foo [{:keys [color]} kids] ...)

micha14:03:09

the kids argument is a cell that will change if you do (.appendChild f1 (div "asdf"))

micha14:03:26

so the appendChild is handled internally in the defelem+ body

micha14:03:38

via loop-tpl or whatever, some cell relationship you make in there

micha14:03:06

kids represents the children of that element for all time, basically

micha14:03:14

not just when the element is being constructed

micha14:03:48

this is another step toward the ultimate goal of being alble to make custom elements that are equal to native elements in every way

micha14:03:52

behavior wise

dm314:03:00

it's just bugging me

dm314:03:17

that defelem and defelem+ are quite different, despite being named very similarly

micha14:03:40

yeah there wasn't a name we liked

micha14:03:15

they're both to create element constructors though

dm314:03:46

so what's missing from your goal of "being alble to make custom elements that are equal to native elements in every way"?

micha14:03:14

defelem+ is like almost there

micha14:03:39

there are issues with two-way binding

micha14:03:58

because of the way it makes the cells for the arglist bindings

micha14:03:05

(defelem text-input
  [{:keys [state] :as attr} _]
  (let [attr (dissoc attr :state)]
    (input :value state :keyup #(reset! state @%))))

micha14:03:27

that provides 2-way data binding of the state cell attribute

micha14:03:13

(defc state "hello")

(html
  (body
    (text-input :state state :type "text")))

micha14:03:31

you can change the value of the input by swapping the state cell

dm314:03:38

yes, using this pattern quite a lot

micha14:03:40

or you can type in the input and that updates the state cell

micha14:03:54

this is not so straightforward with defelem+

micha14:03:34

but then again when you have 2-way data binding, you only want to establish that at construction time

micha14:03:43

it makes no sense to do like

micha14:03:59

(def i (text-input :state state))

micha14:03:01

and then later

micha14:03:09

(i :state foop)

micha14:03:32

because then it would have the :value attribute bound to two different cells

micha14:03:37

which seems nonsensical

micha14:03:56

so when you do 2-way databinding you set that up once only

micha14:03:01

when you construct the element

micha14:03:06

so defelem works fine there

micha14:03:23

so maybe having both is good

dm314:03:55

do you use defelem+ a lot in your code?

micha14:03:54

haven't really had time to work with it yet

dm314:03:26

ok, so it seems like for 6.0.0 it could be marked as experimental

micha14:03:35

yeah it should be

dm314:03:39

thanks for the explanation!

dm314:03:47

what about static and sexp macros?

micha14:03:51

i think there are probably subtle issues with it that need to be worked out

micha14:03:13

the sexp macro is mainly for interop with other languages that transpile to hoplon

micha14:03:26

like i have a haml implementation that does that

micha14:03:37

it's useful to have a way to create sexps explicitly

micha14:03:27

static is an experiment that i don't think was a success

micha14:03:51

static is for defining forms that only appear in the prerendered dom

micha14:03:02

like they're prerendered and cached

micha14:03:20

so when hoplon boots up it reuses the prerendered element rather than creating a new one

dm314:03:23

hm, very specific usecase, could mark it experimental too

dm314:03:48

doing a small presentation on Hoplon next month in Zurich Clojure meetup

dm314:03:59

so would like to get docs in order

micha14:03:05

yeah i'll go in and separate out the experiments

micha14:03:16

in the .clj and .cljs files

micha14:03:25

and maybe make alpha14

dm314:03:00

could you also take a look at https://github.com/hoplon/hoplon/issues/106 if you have the time?

micha14:03:18

sure thing

micha15:03:44

@levitanong: how's d3 coming?

micha15:03:26

@dm3: i added some docstrings etc

micha15:03:31

reorganized a bit

micha15:03:47

also renamed the things to all be *-tpl

micha15:03:52

like if-tpl, cond-tpl etc

vmarcinko16:03:17

haven't been checking out hoplon for some time now... So quick question - hoplon prefers things to be set up in static way, but these *-tpl forms are basically adding dynamics, right?

micha16:03:51

they're still using static allocation, objects are cached

micha16:03:04

but they are removed from and reinserted into the dom

micha16:03:26

like for example:

micha16:03:44

(if-tpl pred?
  (div "hello")
  (div "world"))

micha16:03:52

there will be only one div in the DOM

micha16:03:02

depending on whether the pred? cell is true or false

micha16:03:13

but the divs are constructed only once

micha16:03:55

(if-tpl pred?
  (do (.log js/console "making div 1") (div "one"))
  (do (.log js/console "making div 2") (div "two")))

micha16:03:09

if you then do

micha16:03:24

(with-interval 1000 (swap! pred? not))

micha16:03:41

you will have in the DOM the two divs alternating every second

micha16:03:57

but the console will only show two lines of output

micha16:03:05

because the elements are constructed only once

micha16:03:26

and lazily, so "making div 2" will not be logged until the pred? cell is false

micha16:03:46

that's the static allocation part

micha16:03:17

is that waht you were asking?

flyboarder16:03:01

@micha: that is an amazing summary, should cell conditionals get their own wiki page?

micha16:03:28

sure, that sounds like a great idea

micha16:03:39

the concept is slippery to explain

flyboarder16:03:01

ill throw that on my todo list for today

micha16:03:57

seems like a similar concept in some ways

micha16:03:36

the idea of having a pool of things, some of which are cached and not "visible" at the moment

micha16:03:49

with a pointer pointing to the end of the visible things

micha16:03:56

that's sort of like what loop-tpl does

micha16:03:48

except for the part that each item in the pool updates according to the mad hatter tea party "everyone move down one" type of thing

micha16:03:18

like the table is set with the same dishes, but the people move one space to the left or whatever

micha16:03:38

the dishes being the allocated dom elements, and the people being the data

flyboarder16:03:39

right sorta like array pop push

flyboarder16:03:31

where the data just cycles through the positions

micha16:03:47

right, and the positions update their state accordingly

micha16:03:08

like same place setting, different diner sitting at that place at the table

micha16:03:28

> It is important to note that vector elements not in the active region are still considered part of the vector.

micha16:03:32

from that common lisp page

micha16:03:01

> An implication of this rule is that vector elements outside the active region may not be garbage-collected.

micha16:03:07

there is a similarity there

micha16:03:08

i think an animated gif is called for

micha16:03:22

that would be the best way to explain it probably

micha16:03:27

with a little animation

levitanong16:03:47

@micha d3 interop is seamless now. Working beautifully! Thanks man :D

micha16:03:00

@levitanong: wow that's great news!

levitanong16:03:40

Allows me to use more of the built-in d3 stuff. Such a time saver!

micha16:03:54

yeah it's crucial

micha16:03:58

i bet you can build some cool hoplon elements to encapsulate the d3 stuff

micha16:03:40

like (bar-chart :data (cell= ...) :x-axis ... :y-axis ...) and things like that

levitanong17:03:35

Yup! Scales are currently what I'm interested in. Instead of specifying all the arguments in one go, d3 has you call functions to set the parameters. Pretty weird.

levitanong17:03:08

I'm thinking of making a defrecord

micha17:03:22

should be more or less straightforward to hook cell watches up to the hooks that make d3 rerender, right?

micha17:03:38

that's pretty sweet

levitanong17:03:49

Right now I've been using do-watch a lot

micha17:03:55

yeah totally

levitanong17:03:55

And in cases where I want to watch multiple things for one thing, then I just (do-watch (cell= [a b c]) ...)

levitanong17:03:26

Many approaches one could take. Could componentize everything, each encapsulated in a with-let and do-watch, or have one huge do-watch doing more d3-like stuff

flyboarder17:03:43

@micha im working with firebase promises and im finding that chained promises are not seeing cell changes

micha17:03:22

@flyboarder: how do you mean?

flyboarder17:03:16

well for example lets say i have something like...

flyboarder17:03:09

.then fires once the previous promise is complete

flyboarder17:03:30

but after that threading macro if i print user it is there

flyboarder17:03:15

my problem being i need to create more promises which are not getting the updated values

micha17:03:47

ah that's a threading issue

micha17:03:06

and you put printfs to verify the order in which the then callbacks run?

micha17:03:32

depending on what type of promise implementation they provide, there may not be any guarantees of the order

micha17:03:46

most of the js promise implementations are bogus

flyboarder17:03:08

it’s supposed to be A+ compliant but who knows

micha17:03:30

if you put a prn in each of the .then callbacks

micha17:03:38

you can at least see which order they're running in

flyboarder17:03:02

i will double check that

flyboarder17:03:05

i think i might be better off going to the callback signatures instead

micha17:03:59

they must be firing in the opposite order

flyboarder17:03:51

@micha ok i figured it out, so it seems if i intercept the promises with a callback the callback cannot see the cell changes, but other promises can

micha17:03:04

did you verify the order?

flyboarder17:03:13

and a simple test works

flyboarder17:03:12

i imagine the callback is set when the promise is constructed so at that time the cell has not been modified by the promise yet

micha17:03:39

but the body of the callback isn't evaluated until the .then fires

micha17:03:45

so i don't thin kthat's it

flyboarder17:03:49

hm, not sure, some trickery at play tho 😛