Fork me on GitHub
#hoplon
<
2016-08-27
>
roti12:08:50

Hi. I am looking for a way to handle the "input" event on a textfield with some delay, to achieve or simulate a "user stopped typing" event. Is there any support from hoplon for this? Example:

(input :type "text"
              :input #(reset! filtered-customers (search customers (.-value (.-target %)))))

roti12:08:37

This will call the handler on every keypress

roti12:08:25

Any ideas how to make it better without fiddling with javascript timeout stuff?

laforge4913:08:12

Use async/core? That's the answer on the server side, but for clients I'm finding it slows load time significantly. If that is not a concern, then you have your answer.

jumblerg14:08:35

it should give you a better abstraction over the timeout fiddling, anyway.

roti14:08:54

@jumblerg thanks. so basically the function given to debounce is called only once there is a 800ms or greater delay between two keyup events, right?

jumblerg14:08:34

after 800ms has passed, in this case, the callback will evaluate.

jumblerg14:08:45

i doubt this is the optimal interval to use. i set it once based on a particular usage case and haven’t tuned it since. adjust as necessary.

jumblerg14:08:48

this is a common problem i typically encounter when trying create a better user experience by (a) validating content on a per-field basis or (b) omitting the submit button given a scenario where an atomic transaction containing the values of multiple fields is unnecessary

jumblerg14:08:35

maybe we should have some sort of abstraction to assist with this problem in hoplon itself, at a minimum some throttle and debounce fns in a utils ns.

thedavidmeister14:08:21

@jumblerg i’d love to see throttle and debounce javelin cells

jumblerg14:08:40

@thedavidmeister: think the debounce fn i linked to was actually stripped from a javelin throttle cell micha wrote; there’s one floating around in a gist somewhere. i personally haven’t found a need for them, however, since i rarely have cells on the “command side” of my applications which is where i’ve done all my throttling and debouncing.

thedavidmeister14:08:36

i’ve got something dialing out to my server on keypress, so it would be nice

alandipert14:08:42

@roti i made another exampel just now using a cool debounce function i found, https://gist.github.com/alandipert/71a11fcd688d016333838dcade8dbf78

thedavidmeister14:08:04

why not have a throttle-cell=?

micha14:08:10

it might not be a formula i guess

jumblerg14:08:54

@thedavidmeister: typically, the user generates some input which invokes a callback. that callback in turn invokes a stack of additional functions which ultimately update my model. i haven’t found many cases where i benefit from cells in this case.

alandipert14:08:59

i think cells already do what most people use debounce for

alandipert14:08:12

like in jq/underscore at least, it's used to dedup many identical events, which is one thing cells already do

jumblerg14:08:30

cells get used on the “query side” of my app where i’m binding views to my models.

alandipert14:08:55

the thing you don't have with cells automatically though is a callback after the "last" value was added for some time window length

micha14:08:29

e use the throttle thing on like typeahead widgets and form validation

micha14:08:46

so we send requests to the server at some maximum rate, the throttle timeout

thedavidmeister14:08:24

yeah that’s cool

micha14:08:32

there are some corner cases to consider

thedavidmeister14:08:39

it would be nice to have something in hoplon

thedavidmeister14:08:55

whether it’s a cell or just one of these examples

micha14:08:04

like if there is only a single event it should be sent immediately

micha14:08:38

the denouncing things i found don't do that correctly

thedavidmeister14:08:41

well with throttle you should trigger straight away

thedavidmeister14:08:57

and then trigger at most X times per second after that

thedavidmeister14:08:05

debounce is different

micha14:08:07

right exactly

alandipert14:08:38

debounce is kinda the opposite maybe?

alandipert14:08:46

like after the "batch" that throttle made, fire an event

thedavidmeister14:08:50

this is a good demo

micha14:08:22

i don't know of a use for debounce in js really

thedavidmeister14:08:48

well, an example above is not showing error text immediately

thedavidmeister15:08:45

or you might have a really expensive calculation

thedavidmeister15:08:53

for like, window resize, maybe

thedavidmeister15:08:09

or maybe it has an animation that you don’t want to trigger many times, like masonry style

thedavidmeister15:08:40

but i agree that throttle is what you normally want

alandipert15:08:00

spellcheck is another example, want to run it n secs after user stopped inputting

micha15:08:50

if do that while they type

alandipert15:08:28

it depends on your ui

alandipert15:08:36

it doesn't always make sense to constantly show the invalid state flash/ui

alandipert15:08:52

debouncing is an expedient way to not show ephemeral input warnings

jumblerg15:08:55

i’m not entirely clear what the difference between the two happens to be, since everyone with a blog and a keyboard seems to suggest differing definitions. i thought what micha is calling a throttle was a debounce.

thedavidmeister15:08:14

well the word “throttle” in other contexts means what it means in that codepen

thedavidmeister15:08:28

you rate limit something, but you still allow it to happen as much as it can within that limit

thedavidmeister15:08:54

i don’t know if “debounce” means anything elsewhere

thedavidmeister15:08:46

so, if we have event X that triggers Y, throttle = Y cannot trigger faster than a certain rate, debounce = Y can only be triggered after “enough” time has passed after the last time X happened

jumblerg15:08:05

my case for the debounce was filtering a list when the user typed; due to the size of the list the computation was a bit expensive, so i wanted to wait until the user paused to assume some valid atomic state within the input string itself.

thedavidmeister15:08:25

yeah that’s a debounce

thedavidmeister15:08:33

i have a very similar use case

thedavidmeister15:08:45

i haven’t gotten around to optimising it yet though

thedavidmeister15:08:46

throttle is useful when you want something expensive to happen in “real time” but actually, the thing that triggers it commonly occurs faster than the calculation can keep up

jumblerg15:08:57

ok, yeah, that’s my understanding

micha15:08:46

debounce is used mostly for systems that poll (dirty checking)

micha15:08:21

the javelin dependency graph make most of those use cases redundant

micha15:08:56

like without the dependency graph javelin would use it to wait for the dependencies of a formula to settle down before recomputing it's own value

thedavidmeister15:08:31

but debounce is also used to try and infer “intent” from users

thedavidmeister15:08:41

like hover intent

thedavidmeister15:08:03

debounce is a bad name for it, imo

thedavidmeister15:08:52

but it’s what we have to work with

micha15:08:06

yeah it comes from signals like where the voltage needs to ramp up and stabilize in a circuit

micha15:08:39

to approximate a binary signal

micha15:08:49

close the switch and the voltage bounces for a while before it settles down on the steady value

thedavidmeister15:08:04

i just learned something 🙂

micha15:08:09

we could add them to javelin as utility functions

micha15:08:24

seems useful enough

thedavidmeister15:08:47

i would find it useful

thedavidmeister15:08:01

cells are awesome, but sometimes they can be a little too responsive

jumblerg15:08:06

from so:

Throttle: the original function be called at most once per specified period.
Debounce: the original function be called after the caller stops calling the decorated function after a specified period.

thedavidmeister15:08:20

and you actually want things to chill out a bit

jumblerg15:08:26

most succinct, meaningful definitions i found

micha15:08:13

i think @thedavidmeister had really good explanation

micha15:08:48

throttle is call as many times as possible without exceeding a specified max rate

micha15:08:13

with minimum latency

micha15:08:08

the concept is to approximate real time even when the input rate exceeds the rate at which you can process the signal

jumblerg15:08:29

agreed. i have yet to need these functions for use with javelin though, i would find them more useful without the cells as hoplon utils.

micha15:08:33

so you drop frames when you get backed up

micha15:08:36

vs debouncibg, where you expect a lot of useless values and you only care about the last one

jumblerg15:08:49

right, such as when a user is typing into an input field

jumblerg15:08:01

and the filtering from that input is happening on the server

micha15:08:03

depending yeah

jumblerg15:08:10

so that’s one js use case

micha15:08:13

we use throttling there

micha15:08:29

we update validation in real time

jumblerg15:08:50

ah, right, if you’re validating using the castra thing

micha15:08:55

we don't use debouncing anywhere as far as i know

jumblerg15:08:30

does the throttling implementation guarantee you’ll always get the last input from the user?

micha15:08:41

of course

micha15:08:13

it just drops intermediate values when necessary

jumblerg15:08:41

makes sense

micha15:08:41

even for window resize I'd probably use throttle

micha15:08:53

so it appears to be real time

micha15:08:40

and like if you want to validate only when done if have a formula that can compute when done

thedavidmeister15:08:53

here’s an example of window resize where you want debounce

thedavidmeister15:08:43

the animation is very obvious

micha15:08:45

ah yeah you want to minimize event rate there

micha15:08:05

minimum number of changes per time

micha15:08:16

the opposite of real time

jumblerg15:08:30

incidentally, i started using that lib once, then i realized it was a wrapper around https://github.com/metafizzy/outlayer that added no additional value as best i could tell.

jumblerg15:08:42

aside from seo

jumblerg15:08:49

now i do this with hoplon ui as follows:

(elem :sh (b (r 1 1) md (- (r 1 1) 258)) :sv (r 1 1) :g 8 :c :transparent
        (let [n (b 1 sm 2 md 3 lg 4)]
          (for-tpl [col (cell= (apply map vector (partition n projects)))]
            (elem :sh (cell= (r 1 n)) :gv 8
              (for-tpl [{i :id n :name {m :key} :#contact d :_txt u :image} col]
                (project :id i :name n :manufacturer m :description d :url u)))))))))

jumblerg15:08:29

that’s all you need for a pinterest style layout

micha15:08:34

so simple and it's all one abstraction

jumblerg15:08:34

although it doesn’t handle the additional case of distributing all the elements across the columns at the end of a long scroll where the heights of the various elements don’t always average out

jumblerg16:08:22

incidentally, i’ve been wondering if it might make sense to add a standard :let attribute to hoplon

micha16:08:02

how would it work

jumblerg16:08:54

the problem with the standard let expression, such as in the example above, is that it will only return the last expression, but in hoplon, it would be convenient if we could do something like:

(elem :sh (b (r 1 1) md (- (r 1 1) 258)) :sv (r 1 1) :g 8 :c :transparent :let [n (b 1 sm 2 md 3 lg 4)]
  (elem :sh n "some other thing")
  (for-tpl [col (cell= (apply map vector (partition n projects)))]
    (elem :sh (cell= (r 1 n)) :gv 8
      (for-tpl [{i :id n :name {m :key} :#contact d :_txt u :image} col]
        (project :id i :name n :manufacturer m :description d :url u)))))))))

jumblerg16:08:03

it also has the added benefit of being a bit more expressive by permitting the indentation of the elements to better communicate the semantics of how the returned elements will be nested in the dom.

jumblerg16:08:27

i haven’t thought it through enough to decide whether i think this really a good idea, but figured i’d toss it out there.

jumblerg16:08:07

maybe there could also be a :bind that utilizes your bound-fn implementation to make css-style-inheritance explicit

jumblerg16:08:41

(elem :s (r 1 1) 
  :let [h1 {:f 26 :ft :500 :fh 25 :fw 2 :fc (c 0xCCC)}
        h2 {:f 21 :ft :300 :fh 23 :fw 2 :fc (c 0xCCC)}]
  (elem h1 :sh (r 1 1) "one thing")
  (elem h2 :sh (r 1 1) "another thing”))

micha16:08:52

you need a macro though

micha16:08:13

then you lose apply on elements

micha16:08:24

the left form we already have seems pretty ideal to do what it does

micha16:08:32

the let form

jumblerg16:08:52

we support that sort of syntax on doseq, for, and by extension the tpls, so it would be consistent. but probably not worth losing apply over.

jumblerg16:08:31

incidentally, i’ve learned through practice that hoplon becomes more composeable when you define groups of attributes that are commonly used together (let’s call them styles) as maps assigned to variables, which i then pass in as the first arguments to any element i want them applied to.

jumblerg16:08:34

before i was creating separate h1, h2 functions, but i find this approach works better.

jumblerg16:08:54

eg

;;; styles ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;-- breakpoints ----------------------------------------------------------------

(def sm 760)
(def md 1240)
(def lg 1480)

;-- colors ---------------------------------------------------------------------

(def grey      (c 0xF6F8FA))
(def orange    (c 0xFFC160))
(def black     (c 0x1F1F1F))
(def blue      (c 0x4394CD))

;-- palettes -------------------------------------------------------------------

(def success {:c (c 0xdff0d8) :bc (c 0xd6e9c6) :fc (c 0x3c763d)})
(def warning {:c (c 0xfcf8e3) :bc (c 0xfaebcc) :fc (c 0x8a6d3b)})
(def danger  {:c (c 0xf2dede) :bc (c 0xebccd1) :fc (c 0xa94442)})
(def info    {:c (c 0xd9edf7) :bc (c 0xBCE8F1) :fc (c 0x31708f)})

;-- text -----------------------------------------------------------------------

(def banner-title-font     {:f 84 :ff "kaushan"   :ft :500 :fc orange :fh 100 :fw 3})
(def banner-subtitle-font  {:f 22 :ff "core-sans" :ft :300 :fc :white         :fw 14})
(def section-title-font    {:f 21 :ff "geometos"  :ft :500 :fc black          :fw 6})
(def section-subtitle-font {:f 18 :ff "avenir"    :ft :300 :fc black          :fi :italic})

micha16:08:29

the main problem of the let attribute is you can't use it at runtime

micha16:08:59

so i think the let form is probably better

jumblerg16:08:15

yeah, no arguments here since the implementation would introduce other limitations

micha16:08:22

you can't add lexical bindings to a closure that already exists

micha16:08:02

dynamic scope is for that

micha16:08:23

or global scope

flyboarder16:08:14

^ speaking of dynamic scope, I cleaned up my (almost pretty) binding’s example, thanks to @micha this model has worked wonders for me! https://github.com/flyboarder/blaze/blob/master/src/index.cljs.hl

leontalbot16:08:47

I am having memory problems

leontalbot16:08:12

heroku says my app uses 520M but is set to 512M

leontalbot16:08:52

was wondering if, regular hoplon apps could easily require more than 512M dyno

micha16:08:41

are you compiling cljs on the dyno?

micha16:08:11

that needs a lot of memory

micha16:08:34

especially if you use Google closure compiler optimizations

leontalbot16:08:03

hmm, well I upload a jar that inside has my cljs and my clj code

micha16:08:39

so you build the cljs app JavaScript locally?

micha16:08:48

not on the dyno

leontalbot16:08:54

I guess yes, when doing make-war it compiles js

micha16:08:18

are you running tomcat or something?

leontalbot16:08:36

I use ring, so I guess jetty

micha16:08:34

weird i dunno

micha16:08:59

it isn't hoplon related though

micha16:08:11

cause that is done at build time

leontalbot16:08:48

ok perfect thanks.

micha16:08:44

if you can get on the machine and see how much memory it uses

micha16:08:58

like on your local machine run the jar

leontalbot17:08:41

How can I easily see what fn cause memory to climb up?

micha17:08:15

you can use top command or something to see how much memnory java is using

micha17:08:26

if it's using a lot you can use something like visualvm to track it down

micha17:08:43

attach to the running process and look at how memory is allocated