Fork me on GitHub
#hoplon
<
2016-06-14
>
leontalbot12:06:32

Where can I learn about Hoplon routing?

jumblerg13:06:44

@micha: i’m thinking it would be helpful if ->node were made extensible so we can pass different types of children into hoplon-extended elements, and they can self-prescribe how they want to be rendered into the dom.

jumblerg13:06:26

either by multimethod or by creating a protocol for ->node, curious to hear your thoughts when you have a few

jumblerg13:06:28

@leontalbot: there’s no explicitly-supported routing methodology baked into hoplon atm

jumblerg13:06:28

there’s some code you could borrow from my ui experiments though, since i’ve built a solution into the window elem

jumblerg13:06:36

maybe there’s some stuff you might find useful in there

micha13:06:20

@jumblerg yeah a protocol is the way to go, that fn is bogus

jumblerg13:06:34

awesome, in the works.

micha13:06:30

good stuff to work out while still alpha

micha13:06:41

thanks for that

micha13:06:25

interested to see how your use cases work out

jumblerg13:06:56

does it also make sense to support INode as a protocol behind the attribute multimethod while at it?

jumblerg14:06:01

i have cases, however, where certain types need to render to different things depending upon whether they’re a js/Attr or a js/Element, so I’m wondering, if we agree on a protocol for attibutes, if we should have separate IElementable and IAttributeable protocols.

jumblerg14:06:36

i also just remembered that js/Attr no longer inherits from js/Node in newer implementations of the dom, so i guess they should be separate.

jumblerg14:06:25

going with INode and IAttr if we determine the latter makes sense also.

dm314:06:02

excuse my noobishness: how are these protocols useful? like, what is possible once Hoplon has them?

jumblerg14:06:28

well, there’s already an implicit protocol, so to speak, in the ->node fn

jumblerg14:06:44

which is an implementation detail at the moment

jumblerg14:06:03

but if we open it up, then users can define new types of things, such as i’m doing in the ui lib, and simply specify how hoplon should go about inserting those types into the dom.

dm314:06:28

that's what I was interested in 🙂

dm314:06:18

do you have an example of a custom Node?

jumblerg14:06:46

sure. internally, strings and numbers are “custom nodes”.

jumblerg14:06:11

externally, i have an Elem type in the ui library i’m working on

dm314:06:28

ok, so you can appendChild anything that is an INode

jumblerg14:06:07

kinda like the do! and on! multimethods let us do with attributes now

jumblerg14:06:18

but with multimethods, of course 🙂

dm314:06:17

hopefully won't turn into React 🙂

jumblerg14:06:52

very much agreed. this is just one fn to allow things to describe how they want to render into the dom, not the first in a set of lifecycle methods.

jumblerg14:06:55

i’m reticent to open a can-o-worms here, but if we were to attempt to gc things at some point in the future, which requires explicitly detaching things in the absence of weak references, another unrendering fn could prove useful. the issue with React appears to me, in my unofficial and somewhat uninformed opinion, is that the lifecycle methods leaked out in such a way they seemed to be getting implemented on everything.

jumblerg14:06:27

for a very small set of primitive types, they could makes sense, internally, so to speak

jumblerg14:06:48

but everything else should be building on those primitives

dm314:06:57

React lifecycle shows up when you integrate with non-React js

jumblerg14:06:17

i’ve seen a few libs where custom components seem to implement a bunch of lifecycle methods. i think this was not what the creators of React intended either, incidentally, based on a blogpost i recall reading at one point. i suspect they were abused by third parties because they were there. but i should probably stop talking now, because i’m speaking of things i know nothing of. 🙂

dm314:06:41

well, in pure React world you don't really need lifecycles

dm314:06:52

you just rerender the components into the tree

dm314:06:04

if the state/props have changed

dm314:06:18

so maybe just shouldComponentUpdate

dm314:06:41

when you have interaction with state in the DOM though

dm314:06:51

you are in trouble

dm314:06:50

so if your custom Elems end up creating some state that they need to clean up

dm314:06:55

you're kinda halfway there

jumblerg14:06:54

yeah, a detach lifecycle function would be a departure from the hoplon way of doing things

jumblerg14:06:28

and i have no design to add one atm

jumblerg14:06:25

in this specific case, we’re making some existing functionality open to extension

micha16:06:02

i don't see the need for all the GC stuff

micha16:06:28

like sure, there are some ways you can do things that will leak memory, but you can always find lots of ways to do that in the browser

jumblerg16:06:21

@micha: ithe *-tpl stuff prevents leaks, but if things were gc’d, i wonder if the overall memory profile could be reduced.

micha16:06:29

it's not just about memory, it's also about having a sane state model

jumblerg16:06:34

say someone renders a bunch of images in an infinite scrolling thing… the current approach will remove them from the dom, but do the images hang around in the tpl cache?

micha16:06:02

an infinite scrolling thing can't really be infinite

jumblerg16:06:45

say i navigate from one state that has a bitmap image via img to another, then back… doesn’t that image element retain its bitmap data while detached from the dom?

jumblerg16:06:19

the “infinite" scroll is a bad example, basically, even through it is controlled, the memory profile of the app will expand as i navigate through more states and more things are added to and removed from the dom because they are cached by the *-tpls, and if some of these elements are heavy, such as bitmap images, this might not be great for certain cases.

jumblerg16:06:31

(albeit cases hoplon was not necessarily designed for)

jumblerg16:06:45

in this case, unless my previous assertion is incorrect, a more pragmatic approach could be to purge img elems of their bitmap data in certain cases, make the memory management model a bit more sophisticated via ref counting in the future.

jumblerg16:06:03

i don’t think it makes sense to expose a detach lifecycleish method. i’m completely on board with the current approach, i think it is a much better model. just playing a bit of devil’s advocate to explore the drawbacks of this approach and understand what we can do to make it better or applicable to even more cases.

micha16:06:36

things that are in the pool will consume memory, but i don't think that's a big deal in practice

micha16:06:05

you can verify that by adjusting page sizes

micha16:06:03

the things in the pool will be mostly "empty"

micha16:06:24

like for instance your image thing, the image URL will be nil while it's in the pool probably

jumblerg16:06:55

agreed in the case of most web app. if i jam a ton of high-res images in there because i’m building a photo sharing app - building getty images - i imagine it become problematic at some point.

jumblerg16:06:25

i think they retain the bitmap data, i’ve preloaded images by passing the elems urls before putting them in the dom before

micha16:06:05

i don't see how setting the url to nil is less effective than trashing the img element

micha16:06:31

other than the overhead of an empty img element object

micha16:06:35

which is negligable

jumblerg16:06:48

does the pool currently set the url to nil?

micha16:06:58

no, that just happens naturally

micha16:06:08

the url will almost certainly be a formula cell

micha16:06:18

which depends on the nth thing in the loop items

micha16:06:21

which will be nil

jumblerg16:06:24

ah, good point 🙂

jumblerg21:06:17

@micha: let me know when you have a few mins to discuss ^^^

micha21:06:49

@jumblerg: looks good, but perhaps without the last commit, d3afd653dd7fe8faee661f4cbc5f39e06a0dcbbb

micha21:06:29

actually i think the only change we need to do is make the INode protocol

micha21:06:30

and then modify ->node, adding a second-to-last clause like:

micha21:06:48

(defprotocol INode
  (-as-node [this]))
  
(defn- ->node
  [x]
  (cond (string? x)          ($text x)
        (number? x)          ($text (str x))
        (satisfies? INode x) (-as-node x)
        :else                x))

micha21:06:04

or something along those lines

jumblerg21:06:49

@micha: yeah, the first commit is the more, ah, conservative approach

micha21:06:51

the dynamic var stuff makes things kind of crazy

jumblerg21:06:04

it does a little

jumblerg21:06:14

i’m not sure how i feel about it

micha21:06:14

and issues with that approach in async environment

micha21:06:25

the dynamic bindings won't be bound in callbacks

jumblerg21:06:29

well, yeah, gotta close over the dynamic var with a let binding first

jumblerg21:06:18

it does feel a little messy to me

jumblerg21:06:29

however, it comes in really handy in a number of contexts

jumblerg21:06:15

esp in the ui project, where i have a bunch of attribute constructors that need a reference to the elem

micha21:06:28

the (defn html ...) i think is ok, we don't need to roll that into another function

jumblerg21:06:56

better code reuse, but passing a flag feels a little weird

micha21:06:12

yeah it's not really reuse then

micha21:06:17

it's just combining

jumblerg21:06:18

i did it because of the dynamic var thing

jumblerg21:06:57

i do think the elem var is, in a way, simply modeling the messy context we’re dealing with

jumblerg21:06:39

for example, we have a js/Attr type

jumblerg21:06:58

(.createAttr js/document) has been depricated

jumblerg21:06:28

we can’t construct an attribute without a reference to the elem it is being associated with anymore

jumblerg21:06:37

so it comes in handy there

micha21:06:28

why do you need to construct an attribute object?

jumblerg21:06:41

to bind the children to the cell

jumblerg21:06:52

or an attribute to a cell later

jumblerg21:06:14

(i’m messing around with ->attr right now)

micha21:06:38

i don't see why the native Attribute type is relevant though

micha21:06:08

we're operating on a higher level, those are craeted when you actually do something to an element

micha21:06:33

a string or other type would do the job just as well

micha21:06:54

since for our purposes we don't actually intend to add attributes to the actual dom node

micha21:06:05

because that implementation is closed

micha21:06:20

so the native Attribute type is kind of redundant in that case i think

jumblerg21:06:04

maybe, in the current version of ui i’m working on each attribute is a function

jumblerg21:06:21

and it returns an Attribute

micha21:06:29

it could return a different thing

micha21:06:33

just as well

jumblerg21:06:47

easily, could just implement IAttr or something

micha21:06:13

like if you want to add an attribute to the element, you can do that already

micha21:06:22

but you want to make meta attributes

micha21:06:29

that aren't actually attributes on the real dom element

micha21:06:44

in which case the dom Attribute spec is irrelevant, it seems

micha21:06:34

as i understand it, attributes in hoplon are merely methods on the dom object

micha21:06:45

you can call them by setting the attirbute

micha21:06:16

we extended it with the do! and on! multimethods, so you can add your own methods essentially to the prototype

micha21:06:36

(div :foo 100) is really like (foo (div) 100)

micha21:06:54

where foo is a function

micha21:06:18

the attribute notation is just easier to work with in the UI

micha21:06:30

because you don't need to thread everything with macros then

micha21:06:08

or rather, i should say:

micha21:06:28

(div :foo 100) is like (doto (div) (do! :foo 100))

jumblerg21:06:23

do you think it makes sense to expose a protocol like IAttr, with a ->attr similar to ->node behind the do! and on! mulimethods?

micha21:06:47

i'm not sure that ->node is going to help anymore 😕

micha21:06:26

is there a simple example of how it could be used?

micha21:06:48

like what else could you appendChild other than dom elements, textnodes, or comments?

jumblerg21:06:09

custom types

jumblerg21:06:01

(deftype Elem [o m i]
  IPrintWithWriter
  (-pr-writer [this w _]
    (write-all w "#<Elem: " (.-tagName o) " " (.-tagName m) " " (.-tagName i) ">")) ;; todo: print all attributes
  hoplon.core/INode
  (->node [_]
    (with-let [_ o]
      (.appendChild m i)
      (.appendChild o m)))
  hoplon.core/ICustomElement
  (-append-child! [_ new-elem]
    (hoplon.core/append-child! i new-elem))
  (-insert-before! [_ new-elem old-elem]
    (hoplon.core/insert-before! i new-elem old-elem))
  (-remove-child! [_ old-elem]
    (hoplon.core/remove-child! i old-elem))
  IManageLayout
  (-align-horizontal [_ v]
    (align-horizontal- o m i v))
  (-align-vertical [_ v]
    (align-vertical- o m i v))
  IFollowLayout
  (-position-vertical [e v]
    (set! (.. e -style -verticalAlign) (or v o-verticalAlign))))

jumblerg21:06:43

this allows hoplon to cache the Elem in .-hoplonKids

jumblerg21:06:49

and treat it uniformly

micha21:06:45

i can't see what it's trying to do

micha21:06:02

what is o m and i

jumblerg21:06:23

oh, those are the outer, middle, and inner divs that, together, comprise an elem

jumblerg21:06:33

that’s the way the ui box model is implemented

micha21:06:15

you could accomplish this without the Elem type, with a function

micha21:06:37

you can use specify! to add the extra protocols to a regular div, for example

micha21:06:32

i have to think about this

jumblerg21:06:06

the first versions were done with fns, but it seemed it reached a point where things needed to be structured better

jumblerg21:06:25

i have a considerable number of cases i’m juggling that we should probably work through together so you can get a holistic sense of things

micha21:06:11

does everything work with the first two commits in place?

jumblerg21:06:19

yeah, quite nicely

jumblerg21:06:38

the third i can do without, that was just a proof-of-concept for discussion

micha21:06:44

we should test it in different browsers

micha22:06:01

now that we're referencing concrete types and whatnot

jumblerg22:06:04

agreed, it’s not a pr yet for that reason

micha22:06:41

like for example, what if you do e.appendChild(document.createTextNode("foobar"))

micha22:06:20

e.appendChild(document.createComment("asdf"))

micha22:06:25

or whatever the incantation is for that

jumblerg22:06:39

this should cover that, i think

jumblerg22:06:12

i believe they are all of type Node

micha22:06:27

cool, yeah that works

micha22:06:41

i ran into issues with the types in ie before

micha22:06:48

that's why i mention it

jumblerg22:06:27

i’ve no doubt, i’ll be sure to put it through the paces.

micha22:06:36

like ie8 doesn't even have that type

jumblerg22:06:00

yeah… about that… do we still want to support 8 in hoplon 6?

micha22:06:17

a good question

jumblerg22:06:26

we should probably formally define what we’re supporting these days

jumblerg22:06:57

if we leap to 11+, we get mutation observers and everything

jumblerg22:06:58

i can just tell the users of my webapp to upgrade, if for whatever reason, they’re still running an old one

jumblerg22:06:07

but even the financial services companies are finally on 11

jumblerg22:06:43

but maybe we should survey the rest of the community to see what browser support they need

micha22:06:55

what would reactjs do

jumblerg22:06:59

i say that if ppl still want ie 8 they can stick with hl 5.

micha22:06:30

it depends what we gain and lose with the tradeoff

micha22:06:42

like currently we should be supporting ie8 just fine

jumblerg22:06:05

i concur there needs to be a good case for moving up, agreed

micha22:06:42

that's a lot of browsers

jumblerg22:06:24

6% of what kind of users? the kinds of user who use webapps? i doubt it.

jumblerg22:06:04

but i guess we support 8 and up for the time being, until we have something to gain by dropping a version or two?

micha22:06:16

seems reasonable to me