Binding UI Events

170 views
Skip to first unread message

pe...@blois.us

unread,
Oct 12, 2013, 2:58:00 PM10/12/13
to polym...@googlegroups.com
Something I've found useful in the past is to be able to bind functions from the model to UI events. I threw together a quick prototype of this, wondering if others think it's useful as well.

Some hacked out prototype code is at- https://gist.github.com/peteblois/6953310

And it just allows doing something like:
<template repeat='{{items}}'>
  <div on-click='{{delete}}'>
  </div>
</template>

function Item() {
  this.delete = this.delete.bind(this);
}
Item.prototype.delete = function() {
  // delete me!
}

It also allows event handlers to work on non-bubbling events (such as media events, which I'm not sure currently work in Polymer)-
<template repeat='{{items}}'>
  <div class='{{watched: watched}}'>{{title}}
    <video src='{{url}}' on-play='{{markAsWatched}}' controls></video>
  </div>
</template>

Things which could be improved:
  • Better or automatic scoping of the function, so it doesn't need to be bound in the constructor.
  • Ability to specify function parameters. Though this doesn't really feel like a step back from the current events.
Thoughts?

John Messerly

unread,
Oct 18, 2013, 4:05:32 PM10/18/13
to pe...@blois.us, Rafael Weinstein, polymer-dev
This is pretty neat. Am I understanding right: the idea is to move the Polymer event bindings in Polymer Expressions? That way, they are hooked up automatically when a <template> expands, without needing to listen for events on the shadow root. So all of the bugs around non-bubbling events go away. And it doesn't take much code either from looking at the gist.

Maybe the next step is try and land a pull request into Polymer Expressions.




Follow Polymer on Google+: plus.google.com/107187849809354688692
---
You received this message because you are subscribed to the Google Groups "Polymer" group.
To unsubscribe from this group and stop receiving emails from it, send an email to polymer-dev...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Rafael Weinstein

unread,
Oct 18, 2013, 4:19:23 PM10/18/13
to John Messerly, pe...@blois.us, polymer-dev, Steve Orvell
I believe Steve is already working on this. I imagine he will chime in soon. We discussed it yesterday.

There are two (progressively fancier) versions of this:

1) <div on-click="{{ handleClick }">

This would provide the benefits already mentioned. The implementation is likely inside the polymer base class whose bind() method will treat any on-* binding as a request to add an event listener to the provided function.

2) <div on-click="{{ addItem(currentItem) }}">, e.g. allow the function be an expression

This would additionally require polymer-expressions to treat expressions inside on-* bindings specially. Notably, it will not register observers on anything in the expression, but will return an object whose value property is a wrapper function which closes over the model and evaluates the expression when it is invoked.

I'm not a security expert, but I think we need to take care in designing both of these to avoid risk of creating XSS attack surface. One approach may be to only white list model functions are methods of polymer elements.

Steve Orvell

unread,
Oct 18, 2013, 4:32:15 PM10/18/13
to Rafael Weinstein, John Messerly, pe...@blois.us, polymer-dev
Yeah, we're exploring doing all of polymer's event handlers via binding for the reasons noted.

My first stab is similar to the gist in that it overrides Element.prototype.bind. I'm planning to move to the approach recommended by @jmesserly above, using expressions.

We cannot do Rafael's #1 because we want to handle:

    <div on-click="{{clickHandler}}">

and this is not a custom element.

Pete Blois

unread,
Oct 18, 2013, 5:43:38 PM10/18/13
to polym...@googlegroups.com, Rafael Weinstein, John Messerly, pe...@blois.us
Great to hear!
I'd still like to see binding to methods on the model, but understand moving with caution.

Scott Miles

unread,
Oct 18, 2013, 6:01:08 PM10/18/13
to Pete Blois, polymer-dev, Rafael Weinstein, John Messerly, pe...@blois.us
>> I'd still like to see binding to methods on the model, but understand moving with caution.

By design, when looking at a polymer-element, the host `element` is the model and the methods are are on the `element`, so the `on-` syntax is already "binding to methods on the model." 

I think I'm misunderstanding your point here.

Pete Blois

unread,
Oct 18, 2013, 6:35:38 PM10/18/13
to polym...@googlegroups.com, Pete Blois, Rafael Weinstein, John Messerly, pe...@blois.us
Sorry, the view-model duality of the host always trips me up. Is there a better term than model for what the contents of a <template repeat> is bound to?

With a deeply nested/bound UI such as:
<template bind='{{somePath}}'>
  ...
  <template repeat='{{deeperStill}}'>
    ...
    <div on-click='{{goOffline}}'></div>

The relation between the <div> and what the div is bound to is tight, but their relation to the host is somewhat loose, so it seems like the polymer-element code often just turns into some boilerplate 'fetch the model and call a method on it' which may as well be a static method.

P.S. I still have difficulty reasoning about model-view separation when the model is a view.

Scott Miles

unread,
Oct 18, 2013, 6:48:16 PM10/18/13
to Pete Blois, polymer-dev, Rafael Weinstein, John Messerly, pe...@blois.us
On Fri, Oct 18, 2013 at 3:35 PM, Pete Blois <bl...@google.com> wrote:
Sorry, the view-model duality of the host always trips me up. Is there a better term than model for what the contents of a <template repeat> is bound to?

No, IMO that's definitely a model in the standard sense.

For the host node itself, I believe a close pattern is Model-View-Presenter. Most properly then the host contains the presenter-model, but we usually cheat and call it a view-model, or simply model.

The presenter-model is used to drive the UI and can be completely distinct from your business-model (or overlapping or the same). And yes, the `element` is it's own model, presenter, and view. This is the packaging-by-functionality aspect of Polymer.
 

With a deeply nested/bound UI such as:
<template bind='{{somePath}}'>
  ...
  <template repeat='{{deeperStill}}'>
    ...
    <div on-click='{{goOffline}}'></div>

The relation between the <div> and what the div is bound to is tight, but their relation to the host is somewhat loose, so it seems like the polymer-element code often just turns into some boilerplate 'fetch the model and call a method on it' which may as well be a static method.


Inside a template repeat you are potentially dealing with a sub-model, depending on syntax, but it's by design that elements declared in a polymer-element are under the purview of the host node.

We probably need to see a problem case with more context. 

Scott

Scott Miles

unread,
Oct 18, 2013, 7:24:07 PM10/18/13
to Pete Blois, polymer-dev, Rafael Weinstein, John Messerly, Pete Blois
Ok, I think I see what you mean: you would like to be able to delegate to a method on the sub-object in the iteration.

I think we may be able to have our cake and eat it too here. Give us a few days to work on it.

Scott


Justin Fagnani

unread,
May 8, 2014, 2:53:41 PM5/8/14
to Scott Miles, Pete Blois, polymer-dev, Rafael Weinstein, John Messerly, Pete Blois
Did anything ever happen here?

I'm trying to work up an example of a list with selection, and it'd be most natural if I could either use a function expression as the binding, similar to what Rafael was talking about in 2).

My template is something like:

<template repeat="{{ item in items }}">
  <div class="item" on-click="{{ selectItem }}">{{ item }}</div>
</template>

The pain is in trying to figure which data object is being referenced, since selectItem receives DOM objects, not my model objects. Looking at core-list, I see I can get the model from the templateInstance, but that's not so obvious. What I really want to do is:

  <div class="item" on-click="{{ selectItem(item) }}">{{ item }}</div>

or even:

  <div class="item" on-click="{{ item.select() }}">{{ item }}</div>


Cheers,
  Justin

Steve Orvell

unread,
May 8, 2014, 3:00:44 PM5/8/14
to Justin Fagnani, Scott Miles, Pete Blois, polymer-dev, Rafael Weinstein, John Messerly, Pete Blois
No progress yet. The current way is what you noted and I agree it's absolutely not intuitive. We do want to support probably both sytaxes you proposed with the latter having higher priority.


Justin Fagnani

unread,
May 8, 2014, 3:04:31 PM5/8/14
to Steve Orvell, Scott Miles, Pete Blois, polymer-dev, Rafael Weinstein, John Messerly, Pete Blois
Good to know.

While doing this, I also see that I have a bug in the Dart version of PolymerExpressions such that it's returning the internal Scope object for node.templateInstance.model, so I have to write node.templateInstance.model.model. I need to fix that, but I feel like once you get into templateInstance you're digging a little too much into the internals of template binding to be generally accessible to non-experts.

Thanks,
  Justin
Reply all
Reply to author
Forward
0 new messages