One-way-bound <input>s

Published on Feb 12, 2016

Two-way-bound {{input}}s were the default choice for much of the Ember 1.x lifecycle. However, Ember is slowly moving towards a one-way-bound world.

In this episode we replicate the functionality of a two-way-bound {{input}} using the on-way-bound html < input > tag. Then we show off a bit of the flexibility that this unlocks.

Update: The bug we mention at the end is fixed in Ember 2.3.1.


Links

Code

An input element that replicates the functionality of {{input value='monster.name'}}

<input value={{monster.name}} oninput={{action (mut monster.name) value="target.value"}}>

It's more verbose, but it's also more flexible. One example is having it update onblur (when you exit the field) instead of oninput (the moment a change is made to the field):

<input value={{monster.name}} onblur={{action (mut monster.name) value="target.value"}}>

Transcript

In the Ember 1.x series, inputs were by default two-way bound. In the Ember 2.0 series and beyond, we’re slowly moving to a world where inputs are one-way bound. But how do we do that?

That is our topic for this episode. We’re going to take these two-way bound input helpers, and instead use the input tag and create something from that that still has the same functionality. So before we get started on that, let’s look at this functionality.

So here we’ve got a monster editing utility, and here when we change this, we notice that it changes over here. Whether that’s desirable or not is debatable, but that’s how the implementation currently is. And then down here this is definitely something we want. We’re noticing what’s changing.

So our first change will be the obvious. We’re going to turn this just into an html tag with the value of the monster name. And here we’ll see that it has this, but it doesn’t change here or here like it was before. So let’s investigate that.

First we’ll shrink this down a little so we can see it while we still have this open. And we’re going to give it a unique id so that we can go in here and grab that, and then we’ll get the value off of it.

So we’ll see that the value here is ”Sparkachu”, and when we change it, that value is changing. So this is changing but it’s not getting propagated up to our Ember app. So this is exactly what we should’ve expected because it’s one-way bound, because it’s bound on the way down but not on the way up. So we’re going to have to do something in order to make it propagate back up.

So we have our data going down and we need an action going back up. So this action is going to trigger oninput. Now oninput is a JavaScript event that is surprisingly difficult to find documentation on. Here is the global handler version of it. Here is the only place I’ve seen it actually being used within documentation, outside of Ember stuff.

So let’s go ahead and give this event handler, this oninput handler, an action. And we’ll go over each part of this action in turn.

So when we highlight this input field and then we go to the event listeners tab, we see that we do indeed have an input listener, so it is doing something on input. And within here, it has a handler, and that handler, the prototype is a closure action, and closure action is what we gave it here, and then it has an action with an Ember id. So we’re handing oninput a closure action, a method. And then when oninput is fired, which will be every time that input changes basically, then we’re going to call that method. So let’s see what this method is.

So this action is taking two arguments. The first is a subexpression with a mut helper and then monster.name. So this mut helper, it was introduced in the run-up to Ember 2.0. And what it does is it produces a regular JavaScript value that has both the current value and a way to update it. But that’s not all. When it’s combined with the action helper, then it converts the mut object that’s created by the mut subexpression into a function that can be invoked. And it packages that up into the component's action hash.

So what that means is here it’s creating an action and putting it on the actions hash, and then assigning that to oninput. That’s pretty cool.

So that’s the first argument which is basically deciding which action we’re going to take. Then we have the value, and we’re feeding it the string target.value. What does that mean?

So our first hint comes from the event documentation for JavaScript. So you can get a target on an event, and that target is basically a reference to the object that dispatched the event. And in our case, I believe that is this input tag. And of course calling value on that will get this value. Or put it another way, it’s the thing that we got when we called .val using jquery on this tag. That’s the thing that’s changing.

And so with our new inputs, our data down and our action up, it’s changing just like it was before when it’s two-way bound.

So what we’ve done is we’ve basically replicated a two-way bound input using just a one-way bound input, and then an action going up. Of course this is a lot more verbose than what we had before, even when we take out this extra id. But with this verbosity, it comes more flexibility.

For example, instead of oninput, we could have onblur. And what this does is, notice it’s not changing, but I’m going to hit tab, and then it changed when I hit the tab, when I blurred it. It also works if you click out. So as you can see, you can get more things than just oninput, but we’re going to leave it as oninput right now so we can have basically a replication of what we’ve had before.

There is one last gotcha with the oninput. So it all works great when you’re going at the end, but let’s say you want to you add something here. Uh-oh, it popped me right to the end, even though I was entering something right here in the middle. So that’s not great.

And that’s fixed in Ember-one-way-controls, which we’re going to go over in our next screencast. I’ll see you then.

Subscribe to our mailing list