-
-
Notifications
You must be signed in to change notification settings - Fork 926
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add binding ability #209
Add binding ability #209
Conversation
Hi @jsguy I have a few comments (ok, maybe a lot of comments):
And some comments about mithril.bindings.js:
function hides(flag) {
return {style: {display: flag ? "none" : "block"}}
}
m("div", hides(ctrl.shouldHide()), "foo") Or, to make it look closer to mithril.bindings.js: var bindings = {
hides: function(flag) {
if (!this.style) this.style = {}
this.style.display = flag ? "none" : "block"
}
}
var b = function(attrs) {
for (var key in attrs) {
if (bindings[key]) bindings[key].call(attrs, attrs[key])
}
}
m("div", b({hides: ctrl.shouldHide(), class: "foo"}), "bar") Doing stuff at the virtual DOM level also gives you the ability to leverage I'm kinda inclined to not add the The PR is very similar to |
I have a few comments (ok, maybe a lot of comments): the PR is breaking tests in the Travis CI build
isFunc is only getting set to true if cachedAttr is a function. Does that mean the binding is not meant to activate on first render? If so, why not?
on line 240, why do you iterate over m.bindings if you already know that the desired key is attrName? Can't you access m.bindings[attrName] directly without the for/if?
why does binder take tag as a parameter, if it already takes node?
it's not possible to manage the lifecycle of DOM elements created by a binding (i.e. if my binding creates elements, and the element w/ this binding gets removed, how does the binding know to remove the elements it created?)
m.bindings should never be undefined (i.e. it should be possible to write m.bindings["myBinding"] = foo without having to do object sniffing/initialization)
it's difficult to figure out when a binding will break the core API, e.g. how does one know that {oninput: doStuff, value: someProp} behaves as expected, but that m("div", {onchange: doStuff, value: someProp}) ignores onchange?
And some comments about mithril.bindings.js: The text binding seems redundant, why not just use m("h1", foo()) or m("h1", m.trust(foo()))?
Is it possible to have a value binding that binds on change in one place and on input on another? The only way I can see is to hack the names (e.g. valueInput, valueChange)
It feels like many bindings can be implemented in a much less magical way with helper functions, e.g.
Or, to make it look closer to mithril.bindings.js: var bindings = {
I'm kinda inclined to not add the b helper into core because of the issue of overwriting attributes.
If it's a app-space function, then the developer can debug their own helpers. If the helper is in core, then it becomes a library problem, which might require a lot of code to handle all possible edge cases.
The PR is very similar to b in spirit, but it creates a lot of subtle and complex interactions. Would using something like b instead of this PR work for you?
|
Looks like I branched off a broken build - I'll create a new one from a fixed one - after all, I only want 7 lines added :) |
I think it's very conceivable that someone would try to use the mechanism for, say, a tooltip, since the API looks generic and it is very similar to the Angular directive API.
Doing it at the virtual element level lets you do both because you can tap into What I'm trying to get at is that I generally favor editing at the virtual dom level than the real DOM level because poking the real DOM can expose you to problems that have already been solved by Re: syntax: I was just polluting the global scope in the snippets above for the sake of making the code simpler and the idea easier to grasp. There are lots of ways that the syntax can be improved, for example: m.bindings = {
value: function(prop) {
if (typeof prop == "function") {
this.value = prop()
this.onchange = m.withAttr("value", prop)
}
else this.value = prop
}
}
var mx = function(selector, attrs, children) {
for (var attrName in attrs) {
if (m.bindings[attrName]) customAttrs[name].call(attrs, attrs[attrName])
}
return m(selector, attrs, children)
}
mx("input", {value: someProp, onchange: doMoreThings}) I'm actually putting together a utilities library where a more robust iteration of this specific example could fit well. One could alternatively monkeypatch
It does affect debugging. For example, imagine someone types Anyways, tl;dr: wrt whether this feature should live in core: I'd prefer to have this outside of core for a few reasons:
|
I don't think people will try to use the bindings for things they shouldn't - after all, there is no exposed interface - just the bindings object, not exactly ideal. I'll have a play around and see how it goes. |
Whilst it is not as integrated as I would have liked, it actually turned out pretty good: https://github.com/jsguy/mithril.bindings I'm interested to see your article on multi binding - binding is one of, if not the most, important features of these types of libraries - you can make the core as optimal as you like, but if the user binding API is cumbersome, or the templating solution is too verbose, people won't want to use it. |
Hi,
I've added the ability to create custom bindings, this is crucial for extendability, for example with these small additions, you can do 2-way bindings on one line:
https://github.com/jsguy/mithril.bindings
Also, see example code:
https://github.com/jsguy/mithril.bindings/blob/master/example.htm
Note: This does not change the default behaviour of Mithril, it only adds the ability to customise how bindings are handled.
As soon as these changes are accepted, I'll add the bindings project to the wiki - I really think this is a useful addition - let me know what you think!