Fork me on GitHub
#hoplon
<
2016-02-20
>
thedavidmeister13:02:53

if i’m doing a loop-tpl, is there a way to get at the next/previous items?

alandipert14:02:52

@thedavidmeister: one way is by assigning the elements in the template an id attribute based on some known string and the loop # (you can get this by using map-indexed)

alandipert14:02:24

that's kind of gross though because it involves dipping into global dom scope, what are you trying to do?

thedavidmeister14:02:53

@alandipert: just wanting to select the “next” element on keyup

micha14:02:29

you could also index the items in the seq the loop is iterating over

micha14:02:41

and have a cell to coordinate between them

micha14:02:06

i.e. perhaps each loop body could add a callback to a vector in an atom

micha14:02:17

or something along those lines

micha14:02:39

usually when i think i need to fish around in the dom it's because there is an aspect of the overall model that i've missed

micha14:02:04

like perhaps you need an abstraction that is one level higher

micha14:02:23

an abstraction that encompasses this relationship between the siblings

thedavidmeister14:02:45

yeah, i’m looking at trying something...

micha14:02:59

one pattern i have grown fond of is using dynamic vars and macros

thedavidmeister14:02:04

i got [[a b] [c d] ...]

micha14:02:15

the macro binds dynamic vars which contain the "glue" state

thedavidmeister14:02:22

looking at dropping the first and mapping into

thedavidmeister14:02:55

to get [[a b c] [c d e]]

thedavidmeister14:02:01

dunno how good an idea that is

micha14:02:10

i'm not following

micha14:02:19

i think i missed some context simple_smile

thedavidmeister14:02:55

oh well, (loop-tpl :bindings [[id data] becomes (loop-tpl :bindings [[id data next-id]

thedavidmeister14:02:18

so i can stick “next-id” on the DOM element

micha14:02:23

i think there are better alternatives to ids and fishing in the dom though

micha14:02:38

just hoisting that relationship into a higher level abstraction

micha14:02:56

what kind of operation is one sibling doing to the next one?

micha14:02:15

like focusing it or something?

thedavidmeister14:02:54

actually… all that’s really happening

thedavidmeister14:02:05

is the browser by default moves the cursor to a new input

thedavidmeister14:02:10

i want it to select the text in the input

micha14:02:55

you could use a :enter event handler perhaps

micha14:02:01

i think that's what it's called

micha14:02:06

have you already tried that?

micha14:02:50

hm one sec

thedavidmeister14:02:55

i only just articulated it like that for the first time just now 😛

micha14:02:08

the one i'm looking for is the event that's the inverse of "blur"

micha14:02:28

:focusin might be the one

micha14:02:08

(input :focusin #(.select (-> % .-target js/jQuery)) ...

micha14:02:12

something like that?

thedavidmeister15:02:54

yeah i’ll try that now

thedavidmeister15:02:09

annoyingly, it doesn’t seem to be triggering focus

micha15:02:22

it might be :focus

micha15:02:43

not sure if it's different for different jquery versions

micha15:02:14

but when the browser does something automatically that changes the ui state it usually fires an event

micha15:02:18

so you can do stuff

thedavidmeister15:02:16

i’m surprised that it doesn't

thedavidmeister15:02:54

maybe i don’t understand something about hoplon

thedavidmeister15:02:25

so i’m building inputs based on a collection cell in loop-tpl

thedavidmeister15:02:23

then i delete something from the cell

thedavidmeister15:02:45

the browser sticks the cursor in one of the remaining inputs

thedavidmeister15:02:55

but doesn’t seem to make focus happen

micha15:02:11

but it has the focus?

micha15:02:28

i suspect that maybe it is firing the event but then unselecting the text maybe

micha15:02:41

that SO answer recomends doing the select asynchronously

micha15:02:56

so it will happen after all the focusing business is complete

thedavidmeister15:02:58

mmm it’s not triggering focus

thedavidmeister15:02:09

i just did a console log to check

micha15:02:24

and you tried both :focus and :focusin?

micha15:02:29

and annoying

micha15:02:33

browsers man

micha15:02:47

a disgrace

thedavidmeister15:02:14

i’m wondering if the input is actually getting removed from the middle

micha15:02:18

everything is a special case for them

micha15:02:46

nothing will be removed form the middle

micha15:02:51

only from the end

thedavidmeister15:02:02

that does appear to be what’s happening

thedavidmeister15:02:12

but all the values/attributes shift?

micha15:02:34

this way you eliminate dom churn

micha15:02:47

only properties of the objects change

micha15:02:02

the objects themselves are not moved or manipulated

thedavidmeister15:02:30

that’s kinda messing with me

thedavidmeister15:02:39

actually now that i know that’s happening, i’m suspicious that’s messed with me before too

thedavidmeister15:02:53

any idea how I could react to what hoplon is doing?

micha15:02:08

the things that dissapear will have cells that become nil

micha15:02:18

that's one thing you might be able to use

micha15:02:51

hm actually no

micha15:02:55

not for things in the middle

micha15:02:04

can you make a minimal repro case?

micha15:02:29

i am having a hard time picturing the issue

thedavidmeister15:02:16

so, you do need datascript

thedavidmeister15:02:11

oh well i suppose you don't

thedavidmeister15:02:18

i can cut this back a bit more

thedavidmeister15:02:25

just pulling things out 😛

micha15:02:10

haha great

thedavidmeister15:02:50

:input #(d/transact! conn
          (if-not (= "" @%)
            (if-not @id
              ; create
              [{:data @%}]
              ; update
              [{:db/id @id :data @%}]
              )
            (if @id
              ; delete
              [[:db.fn/retractEntity @id]])))

thedavidmeister15:02:01

that’s my input handler, lol

thedavidmeister15:02:07

(loop-tpl :bindings [[id data] state] [
      (input
        :type "text"
        :value data
        ; input occurs on keydown but after the input's value has been updated
        ; c.f. keydown that occurs before the value has changed.
        :input #(d/transact! conn
          (if-not (= "" @%)
            (if-not @id
              ; create
              [{:data @%}]
              ; update
              [{:db/id @id :data @%}]
              )
            (if @id
              ; delete
              [[:db.fn/retractEntity @id]])))

micha15:02:12

do you think the issue is related to datascript somehow?

thedavidmeister15:02:30

the state is updating fine

thedavidmeister15:02:57

i did (cell= (println state))

thedavidmeister15:02:36

by the time it gets to the loop-tpl it’s just something like this ([43 "asdf"] [65 "sdlkfj"] [71 "asdf"] [75 "asdf"] [76 "asdf"] [nil nil])

thedavidmeister15:02:10

so, imagine i have that sequence, making some inputs

thedavidmeister15:02:23

then i delete “asdf” one letter at a time from input 71

thedavidmeister15:02:10

on the last :input it will be an empty value for the input, so it will delete 71 from the db

thedavidmeister15:02:07

the cursor will now be at the end of 75

thedavidmeister15:02:13

does that make sense?

micha15:02:03

ok yes that makes sense

micha15:02:21

what is :input, is that a built-in browser thing?

thedavidmeister15:02:04

it’s keydown but after the value has updated

thedavidmeister15:02:25

i could do something similar by putting a small timeout on keydown

micha15:02:08

interesting, i wasn't aware of that one

micha15:02:52

seems like hanging something on the :input callback is what you want

micha15:02:12

like in your handler you can add to the end an async call to select the text

micha15:02:23

because it's the same input element

micha15:02:39

like it's just moving the cursor to the end of the element that fired the input event in the first place

micha15:02:03

so in your input event callback you can do the transaction and then use with-timeout 0 to select the text

thedavidmeister15:02:29

just trying to figure out how to do that now 😉

thedavidmeister15:02:50

i think i might have to call it a night and try again tomorrow

micha15:02:23

(input
  :input #(do (d/transact ...)
              (with-timeout 0 (-> % .-target js/jQuery .select)))
  ...

thedavidmeister15:02:26

now that i know that hoplon is actually rejigging the attributes

thedavidmeister15:02:29

i didn’t know about with-timeout

micha15:02:45

it's just a macro around js/setTimeout

micha15:02:52

there is with-interval too

micha15:02:15

but i always was annoyed at how the timeout was the second argument to setTimeout

micha15:02:27

so i made a macro

thedavidmeister15:02:39

yeah but better than setTimeout because you can more easily get to the variables in the current scope

micha15:02:40

the ES6 people probably spent years of their lives arguing about that

micha15:02:05

we just write a macro and 30 seconds later we're back to business

micha15:02:22

+1 for clojure

thedavidmeister16:02:24

_.delay = function(func, wait) {
  var args = Array.prototype.slice.call(arguments, 2);
  return setTimeout(function(){ return func.apply(null, args); }, wait);
};

thedavidmeister16:02:36

that’s what you’d normally be doing >.<

thedavidmeister16:02:47

seriously +1 for clojure simple_smile

micha16:02:58

haha i know, it's ridiculous what they do in js world

micha16:02:18

they've spent like half of their professional careers working on the syntax for fat arrows

micha16:02:22

it's pathetic really

micha16:02:00

waiting for everyone in the world to upgrade their browsers so the engine supports it

micha16:02:08

laughable

thedavidmeister16:02:15

yeah, i need to figure out how to get clojure into existing projects

thedavidmeister16:02:23

so i can use it in my day job 😉

micha16:02:35

in yiddish they call that "keline keppel'dik"

micha16:02:47

(small headed)

micha16:02:13

the cool thing is that you can do interop both ways really easily in clojure and cljs

micha16:02:23

so you can make a cljs library that can be used from js

micha16:02:26

and vice versa

micha16:02:41

so like at my job we have a legacy c# webapp

micha16:02:55

alan has incorporated javelin in there via the splint library

micha16:02:07

so we can use javelin from js

micha16:02:18

to replace giant balls of jquery spaghetti

thedavidmeister16:02:21

javelin looks like the most obvious place to start

thedavidmeister16:02:43

i’ve been lecturing my devs on one way data flow for years

thedavidmeister16:02:57

although, i didn’t know that’s what it was called until recently 😛

thedavidmeister16:02:15

and javelin looks like a good way to help everyone out with that

micha16:02:36

> At the very least, using splint probably won't make what you have any worse.

thedavidmeister16:02:39

well, i’ve found another bug

thedavidmeister16:02:45

thanks for helping, again

micha16:02:51

sure anytime

thedavidmeister16:02:12

some of the subtleties of hoplon can be tricky

micha16:02:45

yeah the best way to solve them is by forming a higher level abstraction

micha16:02:01

like when you see things that want to talk to each other and it's not straightforward to do it

micha16:02:25

that usually indicates the need for some higher level abstraction that can encompass those relationships

thedavidmeister16:02:03

well in this case, it was the way the DOM manipulation was happening that threw me

thedavidmeister16:02:12

i thought it was a new input but it was the same input

micha16:02:33

although, in reality what has happened is that it has Just Worked for a long time

micha16:02:41

and you only now ran into an edge case

micha16:02:51

which actually is a good sign i think

micha16:02:09

like with react type things you have to get down into lifecycle protocols from the beginning

thedavidmeister16:02:17

the only reason i was even thinking of getting “next input” into the DOM was to try and workaround the issue i was having

micha16:02:19

even for relatively simple cases

thedavidmeister16:02:43

but actually i just needed a better understanding of what was happening

micha16:02:54

the loop-tpl dom churn elimination stuff usually does the right thing automatically

micha16:02:16

only rarely do you need to help it with extra stuff like you have now

thedavidmeister16:02:41

could be worth triggering the events as part of hoplon

thedavidmeister16:02:43

food for thought

thedavidmeister16:02:06

if the input really was deleted and the cursor was moved to a new input (which is what looks like is happening)

thedavidmeister16:02:16

i would expect a few events to trigger

micha16:02:23

well osmetimes you do

micha16:02:39

but sometimes you don't

micha16:02:04

i guess setting some defaults in hoplon wouldn't hurt

micha16:02:14

but i don't think it's cut and dried

micha16:02:25

like what if your inputs were nested inside some divs and whatnot

micha16:02:37

things like that

thedavidmeister16:02:22

well, i guess you’d have to observe what the browser does

micha16:02:43

seems like a good thing for a browser polyfill to me

micha16:02:52

it's not really hoplon related

micha16:02:07

really the issue is what should happen when you change input value from js

micha16:02:26

the browser, or at least your browser, moves the cursor to the end

micha16:02:42

a js shim or whatever could standardize this

micha16:02:52

it doesn't seem to be hoplon's problem, per se

micha16:02:03

because the same issue could happen outside of loop-tpl

thedavidmeister16:02:12

it was confusing because what i did in hoplon was delete something in a collection

micha16:02:13

anywhere where you update input values from js

thedavidmeister16:02:16

that was mapped to input

thedavidmeister16:02:34

so i was thinking “i deleted this thing, so the input was deleted"

micha16:02:49

but you don't really want to delete the input

micha16:02:00

because that has perf implcations etc

micha16:02:10

event handlers, internal state, etc

micha16:02:24

like you want those elements to be able to contain their own state

micha16:02:32

so if you destroy them you lose that state

micha16:02:41

that's why in react you need willmount and so on

thedavidmeister16:02:57

lol, this blog post for splint

micha16:02:02

because things can't really contain their own state, the state needs to come from outside

thedavidmeister16:02:16

is a bit too close to home

thedavidmeister16:02:26

i’m definitely going to show the guys at work this 😉

micha16:02:42

that's a really good blog post i think

thedavidmeister16:02:11

yeah, it sounds too much like one of my rants...

thedavidmeister16:02:43

except that the JS example is about as far as i manage to get people to not where they’re coming from

micha16:02:46

well it looks like reasonable jquery in the post perhaps

micha16:02:55

but we have hundreds of lines of that in every view

micha16:02:09

and the views are partials that all refer to each other's global variables

micha16:02:19

it's pretty sweet

micha16:02:22

on opposite day

micha16:02:27

lol also it's all generated by spark tempaltes on the backend

thedavidmeister16:02:42

yeah look, i still see people interleaving “reading DOM”, “doing calculations”, “updating DOM”, “binding event handlers” within functions that have meaningless names

micha16:02:02

c# generating js via spark which contains jquery tempaltes which are run by code in partials rendered by c# ...

micha16:02:55

yeah how can you make a meaningful name when you do't even really know what the meaning is of the thing you're doing?

thedavidmeister16:02:11

the whole concept that there might be invisible dependencies getting created en-masse, and that might be really bad, is totally new to a lot of people

micha16:02:14

you'd be naming everything like workaroundToMitigateEffectsOfOtherWorkaround

micha16:02:10

we're really lucky at adzerk i guess

micha16:02:29

i have forgotten about how people work like that

thedavidmeister16:02:52

well the blog post pretty much points out why

thedavidmeister16:02:59

JS, the language, totally encourages that

micha16:02:07

we don't have a lot of people, but everyone knows how to make good software

dm316:02:29

you are awesome! simple_smile

dm316:02:18

I've been doing Clojure for ~2 years, but your (and Alan's) work made me break from the previous experience only about half a year ago

thedavidmeister16:02:52

the last company i was at, we did a lot of whitelabel work for other agencies

micha16:02:08

me too, i remember my first year of doing clojure i learned more abot computing than i had in the previous 10 years combined

thedavidmeister16:02:14

so i’ve seen how a bunch of different companies do stuff

thedavidmeister16:02:35

the crap JS is pretty ubiquitous

thedavidmeister16:02:50

i’m still a cljs noob, but it’s scratching some itches that have been bothering me for a while simple_smile

thedavidmeister16:02:04

anyway, gotta cruise

micha16:02:26

haha :redeye:

thedavidmeister16:02:09

i gotta stop the late night hoplon binges 😛

micha16:02:13

you're in singapore?

micha16:02:40

i would like to visit your country one day

thedavidmeister16:02:05

definitely come to melbourne if you do simple_smile

micha16:02:08

i've always enjoyed hanging out with all the australians i've met

micha16:02:33

when i build my boat i will sail there for sure

thedavidmeister16:02:36

we have the worst weather but the best culture

micha16:02:01

what's wrong with the weather?

micha16:02:12

other than the fact that you have to live your whole life standing on your heads

thedavidmeister16:02:20

hah, it’s just really random

thedavidmeister16:02:41

we get all the seasons every day

micha16:02:42

and your toilets swirl counter clockwise instead of the correct northern hemisphere clockwise fashion

thedavidmeister16:02:29

ah yes, i think the first settlers miscalibrated those, we never quite fixed that

micha16:02:51

haha i hear the US embassy has a multimillion dollar device to correct it

micha16:02:08

so the ambassador can poop properly

thedavidmeister16:02:17

ah yes, but it only works within the embassy 😉

micha16:02:23

naturally

micha16:02:32

it's a state secret

micha16:02:45

gives us an edge in negotiations

micha16:02:54

being able to poop better

thedavidmeister16:02:55

well, if you like coffee, head to melbourne

thedavidmeister16:02:05

if you like beaches, head to sydney

micha16:02:29

i like small towns with bored but interesting people and pubs

thedavidmeister16:02:37

oooh, well go north

thedavidmeister16:02:48

or anywhere in the middle

thedavidmeister16:02:59

or adelaide, or tasmania...

thedavidmeister16:02:16

actually, go anywhere in australia other than melbourne or sydney >.<

micha16:02:24

haha that's what i've heard

micha16:02:41

i'd also like to see new zealand

thedavidmeister16:02:56

yeah me too, it looks amazing

thedavidmeister16:02:05

thinking of heading there in a couple of years

micha16:02:08

any place that has more sheep than people can't be too bad

micha16:02:35

i've never had my day ruined by a sheep

thedavidmeister16:02:36

hah, there’s that too

micha16:02:46

and they don't know how to drive

thedavidmeister16:02:52

it’s a crazy beautiful place though

micha16:02:16

yeah, a friend of mine tells me they have a lot of dangerous wildlife though

thedavidmeister16:02:28

compared to what?

thedavidmeister16:02:38

not australia, obviously

micha16:02:50

like when they had the flooding a few years ago, the whole place was awash in poisonous snakes, crocodiles, and bull sharks

micha16:02:16

he says his neighbor had sharks swim into the first floor of his house

micha16:02:34

sounds legit lol

thedavidmeister16:02:35

there’s not much down here that doesn’t want to kill you tbh

micha16:02:56

i guess that's why you need a big knife

thedavidmeister16:02:34

it’s not so bad in the cities, but my mum has a shotgun in the kitchen for snakes, lol

micha16:02:43

i'm from florida, where most of the wildlife is either harmless or very lazy

micha16:02:07

we only have a few types of poisonous snakes

micha16:02:51

we especially don't have any poisonous mammals that i know of

thedavidmeister16:02:03

we take the KISS approach, all snakes are poisonous

micha16:02:15

hahah yes

micha16:02:57

i forget where colin fleming is from, i think melbourne?

thedavidmeister16:02:53

don’t think so?

thedavidmeister16:02:00

anyway, i really gotta go

micha16:02:23

it's the weekend

alandipert17:02:05

great convo btw

micha17:02:23

dude i just reread that splint post

alandipert17:02:24

i enjoyed the bit about toilets and standing on heads

micha17:02:34

i forgot about it

alandipert17:02:43

i wish i could make it smaller

alandipert17:02:53

i feel like i didn't get to the essential code explosion problem with callbacks

alandipert17:02:08

circled it but didn't nail it

micha17:02:23

well pretty close for sure

micha17:02:39

also it's neat how the examples are inline

alandipert17:02:03

yeah it's fun

alandipert17:02:09

also splint is in the blog if you ever wanna use it in a post

alandipert17:02:21

do a little bret victor action or whatever

micha17:02:07

hah i'll make a post-it note to remember

levitanong20:02:30

Is there any support for the mutual recursion of defelems? Also, last time I checked, hoplon didn’t support ifs for rendering. Has this changed? If it hasn’t, is loop-tpl the only way to handle the termination of the recursion? So many questions!

levitanong20:02:15

‘cause right now, the workaround i’m using instead of if is

(defelem node [{:keys [subtree]} _ ]
  (let [child-node (cell= (if-let [cn (:child-node subtree)]
                           [cn]))]
    (div
      (loop-tpl :bindings [cn child-node]
        (node :subtree cn))))