Clojurians
#hoplon
<
2016-03-29
>

This page is not created by, affiliated with, or supported by Slack Technologies, Inc.

levitanong05:03:06

Anyone here have any ideas for using attrs as a formula cell in the example below? (defelem thing [{:keys [a b c] :as attrs} _])

dm308:03:15

@levitanong: what do you mean?

levitanong08:03:20

@dm3: in here, a, b, and c are cells.`attrs` is not a cell.

levitanong08:03:08

(cell= attrs) results in nested cells

levitanong08:03:51

(cell= [a b c]) of course returns what i want.

dm308:03:19

defelem+ makes attrs a cell, but then you must always destructure it in order to use

levitanong08:03:41

oooooh! thanks @dm3! This is exactly what I’ve been looking for

dm308:03:37

on the difference between defelem and defelem+

dm308:03:26

although it doesn't highlight the difference in attrs treatment

micha09:03:12

@levitanong: actually with defelem+ you can't have :as attrs at all

micha09:03:52

defelem+ only binds to specific attributes that you declare beforehand

dm309:03:31

I tried to express that, probably not clear enough :simple_smile:

micha09:03:27

the jquery autocomplete is an interesting situation

dm309:03:51

I'd think doing something like (and (.-parentNode elem) (.removeChild this elem)) is sensible

dm309:03:04

of course you might have even crazier onremove behaviour of nodes changing parents, etc

dm309:03:23

not sure if it's OK to just draw a line somewhere

thedavidmeister10:03:57

if i have #{[1] [2] [3]} what is the nicest way to get something like [1 2 3]?

dm310:03:27

(comp sort flatten)?

dm310:03:48

sorry :simple_smile: doesn't work

dm310:03:12

(comp sort flatten vec)

dm310:03:36

set doesn't get flattened properly

thedavidmeister10:03:21

sometimes it’s best to ask :simple_smile:

micha11:03:15

there is also sorted-set

micha11:03:38

(reduce into (sorted-set) #{[1] [3] [2]})

alandipert13:03:44

i usually avoid flatten because it works recursively, ie it flattens all depths

alandipert13:03:07

i tend to use (mapcat identity #{[1] [2] [3]}) or (apply concat #{[1] [2] [3]})

thedavidmeister13:03:56

do you know if datascript ships with something?

thedavidmeister13:03:16

seems pretty common to want to grab a list of ids/values that match some criteria

dm313:03:28

you can use a pattern in a query

dm313:03:49

(q '[:find [?e ...] :in ...])

dm313:03:04

will return a list of things instead of a set of tuples

thedavidmeister13:03:09

'[ :find ?list-id :where [_ :item/list-id ?list-id]]

alandipert13:03:15

another thing to look at is the "pull" syntax

thedavidmeister13:03:26

which gives me #{[1] [2] [3]}

dm313:03:41

do '[:find [?list-id ...] ...]

thedavidmeister13:03:48

so how do i ask for just [1 2 3]?

dm313:03:52

you'd be better served by (d/datoms :avet :item/list-id) I think

dm313:03:03

faster than a query

dm313:03:25

although you need to declare :item/list-id as indexed in the schema for it to get into the AVET index

thedavidmeister13:03:54

i made it :db.type/ref

thedavidmeister13:03:01

should that do it?

dm313:03:07

don't know

thedavidmeister13:03:08

#object[datascript.btset.Iter]

dm313:03:17

you get a seq of datoms

dm313:03:24

so try (map :v ...)

dm313:03:58

you just avoid the query parsing step and do a binary search directly on the index

thedavidmeister13:03:24

aaah yeah, so i just get all the ids

thedavidmeister13:03:07

then i convert to a set to get rid of dupes, then back to a vector, which is what i want

thedavidmeister13:03:10

actually i think that helps with something else i was doing too

thedavidmeister13:03:46

(j/cell=
                    (->
                      (d/q '[ :find ?list-id
                              :where [_ :item/list-id ?list-id]]
                            state/conn)
                      sort
                      flatten
                      vec))

thedavidmeister13:03:06

(j/cell= (->>
          (d/datoms state/conn :avet :item/list-id)
          (map :v)
          set
          vec))

thedavidmeister13:03:01

thanks all :simple_smile:

thedavidmeister13:03:22

@alandipert: also, i figured out a way to do sort-of event delegation

thedavidmeister13:03:25

that’s obvious in hindsight

thedavidmeister13:03:07

you can just let the event bubble out of the tpl, then catch the relevant info in a cell, and have a cell= inside your tpl reference that cell

thedavidmeister14:03:42

aaah, i found a way to make something else simpler with the datoms thing

dm314:03:15

@micha - I've updated the #106 issue some more: https://github.com/hoplon/hoplon/issues/106

micha14:03:31

ah yeah that's great

micha14:03:39

@dm3: thanks for digging in

dm314:03:16

not sure if this issue only concerns reload scenario

dm314:03:51

hm, 2 issues actually - first one seems generic

micha14:03:04

when autocomplete calls .removeChild though, that shouldn't really cause a problem

micha14:03:14

that should remove the child from the atom

dm314:03:37

what atom?

dm314:03:55

the problem is merge-kids has already decided on the seq of elements to remove

dm314:03:04

when autocomplete destroy detaches one of the elements

micha14:03:01

that's the issue with jquery though

micha14:03:08

it's making an async call

micha14:03:13

and not checking

micha14:03:24

hoplon is doing it synchronously

micha14:03:37

so it knows that the children exist

micha14:03:52

the jquery thing is assuming that nothing will mess with the ul

dm314:03:59

but the error happens in Hoplon, not jQuery

micha14:03:06

while it feels free to throw dom elements all over the place with no reservations

micha14:03:18

i don't see how that can be

micha14:03:39

is onremove handler applied synchronously?

micha14:03:14

that would be very unusual

dm314:03:08

well, that's how I debugged it at least

dm314:03:16

and the stacktrace is from merge-kids

dm314:03:27

not from jQuery

micha14:03:00

the jquery callback will call .removeChild

micha14:03:04

which will call merge-kids

micha14:03:35

are you totally sure that .removeChild wasn't called from in the onremove callback?

micha14:03:00

i mean the one that caused the error

micha14:03:08

yeah it would have to be

dm314:03:54

yeah, the thing that triggers is not the removeChild itself

dm314:03:58

but

(when-not *preserve-event-handlers*
                                        (.remove (js/jQuery n))))))

dm314:03:59

this one

micha14:03:08

.remove calls .removeChild, i would think, no?

micha14:03:41

i mean it does

micha14:03:47

i'm pretty sure

dm314:03:54

it does, but it also triggers the remove handlers

dm314:03:00

in the jQuery UI

micha14:03:09

right but those handlers run after the loop is finished

micha14:03:18

that's my theory

dm314:03:19

not according to my Chrome browser

micha14:03:26

you're positive?

micha14:03:35

that would be extremely weird design

micha14:03:46

for exactly this reason

micha14:03:04

in js you need to have callbacks happen asynchronously

dm314:03:24

you are probably right

dm314:03:39

and synthetic events should happen asynchronously

micha14:03:07

that's a terrible design

micha14:03:26

we can't really support it i don't think

micha14:03:32

because it affects everything

micha14:03:42

like you can't loop over anything now

micha14:03:52

without guards all over the place

micha14:03:12

if you put a try/catch in that critical section then the v8 compiler can't optimize the entire function

micha14:03:36

also it's not clear what should happen there

micha14:03:47

like if something sneakily injects code like that

micha14:03:54

that modifies the dom out from under you

micha14:03:14

maybe that really should be an error, because it will cause tons of problems in your application if you do that kind of programming

micha14:03:26

i don't see a sane way for hoplon to pave over this

dm314:03:52

it seems like the remove handler should be wrapped in like a setTimeout(0)

dm314:03:08

or what's the proper way to make it async

micha14:03:19

yeah exactly

dm314:03:30

what about the second issue?

dm314:03:33

with with-init! and reload

micha14:03:23

you can use (with-timeout 0 ...) there

micha14:03:57

the reason is just to have the body evaluated after the element is in the dom

micha14:03:07

and when you construct it it's not in the dom yet of course

micha14:03:21

so the with-timeout should work fine in both cases

dm314:03:46

with-init! seems to be the proper thing semantically

dm314:03:54

only I didn't look at how it works

micha14:03:19

reload isn't exactly init though

micha14:03:30

so i think it's doing the right thing

micha14:03:47

live reload is imho mostly a hack

micha14:03:55

it's trying to patch mutable state

micha14:03:13

which is not going to be a sound, consistent thing with clean semantics

micha14:03:31

i actually just reload the page when the code updates

micha14:03:49

then the semantic is clear

micha14:03:05

you can use the storage atom to maintain state between reloads

micha14:03:11

wrap a cell in that

micha14:03:21

and it will maintain your application state

dm314:03:19

yeah, well :disappointed: it's a nice hack. Some people call it a development tool :simple_smile:

dm314:03:20

thanks :simple_smile:

micha14:03:09

it's just kind of a rabbithole

micha14:03:28

with no real solution

micha14:03:38

at least not in a dynamic language

micha14:03:53

maybe in erlang you can do it correctly

micha14:03:08

but in lisp hot-patching code is a very sketchy affair

micha14:03:47

and you can see in clojure, the most powerful abstractions are the most problematic

alandipert14:03:49

erlang does it by not allowing it

alandipert14:03:57

which is not really doing it lol

micha14:03:58

like protocols for instance

micha15:03:12

@dm3: i added a check for parent the other day btw

micha15:03:15

just to see

micha15:03:29

and it prevented the error, but the reloaded autocomplete didn't work then

dm315:03:36

yes, because of the second issue

micha15:03:00

which issue is that?

dm315:03:15

with-init! -> with-timeout

dm315:03:21

then it works

micha15:03:52

interesting

micha15:03:41

this is where immutable data is working against us

micha15:03:37

we should benchmark the -parentNode test

levitanong17:03:45

Always so exciting when I see someone typing :stuck_out_tongue:

flyboarder17:03:09

https://github.com/hoplon/hoplon/wiki/Template-Macros summarized yesterdays explanation of cell conditionals, feel free to expand past my limited knowledge :stuck_out_tongue:

flyboarder17:03:39

@levitanong: agreed :thumbsup:

levitanong17:03:34

@flyboarder: Nice! I think that maybe they should be called Template Macros instead, as, people might think these have more to do with javelin

levitanong17:03:54

Just added a list of available template macros for reference

levitanong17:03:16

Also clarified that cache doesn’t mean the template gets outdated at all

levitanong17:03:26

simply that they aren’t rebuilt

levitanong17:03:28

i think we should delete this page

levitanong17:03:34

Ballin’, amirite?

dm317:03:47

@micha - awesome, although I'll try to come up with some benchmark there

dm317:03:24

maybe just testing for (when (.-parentNode n) ..) would be enough as it's a corner case

micha17:03:31

i was thinking that maybe i was overcomplicating things, since removeChild happens relatively rarely in hoplon world

dm317:03:39

and if the same element got reattached somewhere else, tough luck

micha17:03:47

so maybe performance there isn't so critical

dm317:03:11

then skipping jQuery (.remove) may be fine, if someone was smart enough to remove the element on remove

dm317:03:22

it's probably jQuery itself

micha17:03:37

that code isn't specifically for the jquery remove thing though

micha17:03:51

it's for any case where someone sneaks in and yanks stuff out of the dom

micha17:03:06

not calling .remove on it would be a possible memory leak

micha17:03:20

but i'm not sure that .remove is always safe to call?

micha17:03:58

oh wait, i see what you mean now

micha17:03:02

ok cool yes

micha17:03:30

we had the problem specifically because we called .remove on it, right?

micha17:03:44

which triggered the jquery ui thing to do its nonsense

dm317:03:12

yes, (.remove el1) triggered (.remove el2), then (.removeChild this el2) failed

micha17:03:21

i see it now

micha17:03:37

so we definitely don't want to call .remove then if the thing isn't in the right parent

flyboarder18:03:14

im not sure it is always safe, im seeing issues where semantic ui hides things outside of the dom (for animating), not working when used in hoplon-land

micha18:03:51

ah that's a good point

micha18:03:18

i think we might want to get rid of that thing that calls .remove

micha18:03:41

if you're doing cell= children you can call that yourself

micha18:03:57

since you're already managing memory manually

micha18:03:26

yeah i think i'm going to remove that whole :preserve-evant-handlers business

dm319:03:53

what was it supposed to solve?

micha19:03:03

jquery memory leak

dm319:03:10

like with the Autocomplete - I'd have to call it somewhere myself

micha19:03:25

only if you are going to remove it from the dom

micha19:03:32

which with loop-tpl you wouldn't do

micha19:03:43

people want to do (cell= (map foo bar))

micha19:03:52

that's why we added the .remove thing

dm319:03:25

seems that removing it is the right decision

dm319:03:06

thinking how that would interact on reload

dm319:03:03

jquery stuff would leak handlers

dm319:03:50

maybe attach a .remove manually in cases where this becomes a problem

micha19:03:15

yeah i thin kthat's the best solution

micha19:03:30

the user should manually add the .remove if they need it

micha19:03:37

or use loop-tpl or whatever

jjttjj21:03:49

Hey guys just wanna say thanks for all the work on hoplon and documentation these days! One thing: on the API documentation page, the section on Destructuring Syntax seems awkard to me: https://github.com/hoplon/hoplon/wiki/API-Documentation#destructuring-syntax Isn't the defelem destructuring just normal clojure destructuring except it allow key/value pairs or a map as the first "arg"? That section seems to imply that there's a bigger difference. I could polish it up a bit

micha21:03:40

@jjttjj: yeah the defelem defines a function of two args, first one being a map and the second arg being a seq

micha21:03:05

and destructuring works as usual, via the normal destructuring stuff in cljs

micha21:03:31

the difference is in how you call it

micha21:03:45

like for defelem the following are equivalent:

micha21:03:07

(foo :bar 100 :baz 200 "hi" "three")

jjttjj21:03:16

yup, the leading key value pairs or a map are the same

micha21:03:25

(foo {:bar 100 :baz 200} ["hi" "three"])

micha21:03:44

(foo :bar 100 {:baz 200} "hi" ["three"])

jjttjj21:03:46

that section in the wiki just gave me pause, just wanted to verify before i screw with it a little :simple_smile: