Editing individual fields-with cancel option
Published on Oct 12, 2016
When a full form is too clunky, but you don't want to auto-save after every keystroke... saving individual fields may be a happy middle ground.
This episode uses lots of metaprogramming in order to create a flexible and powerful component.
Links
- one-way-bound input screencast
- ember-changeset screencast
- EmberSchool.com settings section (must be logged in with github- can view in demo version)
Code
{{single-save-input label="Name" object=user field='name'}}
{{single-save-input label="Email" object=user field='email'}}
{{single-save-input label="Playback Speed" object=user field="defaultPlaybackSpeed"}}
//templates/components/single-save-input.hbs
{{#if editing}}
{{label}}: <input value={{newValue}} oninput={{action (mut newValue) value="target.value"}}>
<button {{action 'save' object}}>Save</button>
<button {{action 'cancel' object}}>Cancel</button>
{{else}}
{{label}}: {{get object field}}
<button {{action 'edit'}}>Edit</button>
{{/if}}
//components/single-save-input.js
import Ember from 'ember';
export default Ember.Component.extend({
editing: false,
actions: {
edit(){
this.set('editing', true)
this.set('newValue', this.get(`object.${this.get('field')}`))
},
save(object){
object.set(this.get('field'), this.get('newValue'))
object.content.save()
this.set('editing', false)
},
cancel(){
this.set('newValue', this.get(`object.${this.get('field')}`))
this.set('editing', false)
}
}
});
Transcript
This weekend as I was creating the settings tab for EmberSchool, I used a really cool pattern that I want to share with you guys. So here you hit ‘Edit’ and it opens up this field, and then you can either ‘Save’ or ‘Cancel’. Let’s say you change it and you hit ‘Cancel’, it’s back to normal. You change it and you hit ‘Save’, then it changes. Now if we go, and we’ll edit one of these, so we’ve got these two things open. We hit ‘Save’ here, so it’s going to save our user, and these are based on the same object. You hit ‘Save’, it’s saved there, but you hit ‘Cancel’ and it goes back to how it was.
You may notice a similarity to how ember-changeset works, and we are replicating some of the functionality, but we’re doing it for individual fields here. And the way we use this is even simpler. So I’ve called it single-save-input
, I don’t know if that’s the best name or not, and we put in a label
, an object
, and a field
. So just one line and it gets us all this functionality. And it’s reusable for every other type of text input.
So let me show you how this works. So we’ll look at the handlebars first. So we have our if editing
and then our else
block. The else
block is what displays at first. So we display the label
and then we use the get
helper to get the specific field from the object. So in this one, we’re grabbing the name
off of the user
. Then, there’s the edit
action. And we’ll go ahead and take a look at what that does. So you click the edit
button and then it calls this action, and of course it’ll set editing
true, and so that will start displaying all of this. But it will do something else. It’ll set something called newValue
. And so we’re basically grabbing the old value of that field, so we’re getting the object
and then the field on it, and this is a nice bit of metaprogramming, and then we’re assigning that to newValue
. Then we’re using newValue
here to display what the value is on this input
, and if you don’t understand what’s happening right here, then go ahead and watch the Video 117 on one-way-bound inputs.
So anyways, the value that we’re displaying is the new value. And then on the input, the action we’re taking is to mutate the newValue
. Alright, so we’re doing that and then we can hit either save
or cancel
. We’ll go with cancel
first. So first we repeat this line down here, and if you’re paying close attention, you’ll notice that this line recently changed. That’s to make it look more like this because they’re doing the same thing, they might as well look the same. And then we set editing
to false. So it’s basically resetting. It’s a cancel
. And so any changes that we made to newValue
, any mutations we made here are now mut
. Then on save
, so here we actually set something on the object, so we’re getting the field name, so in this one it will be name
, this one it will be email
, and we’re setting that to what the new value is. And then we’re calling save
. And because the user we’re passing in here is a promise, we have to call content
. There are probably situations where you don’t have to call content. Anyways, and then we set editing
to false
.
So that’s the code that gets you this really cool functionality. So your question now may be, Jeffrey, are you going to make a single-save-input
addon, and my answer is not right now, for two reasons. First, because I don’t think single-save-input
is the right name. I don’t know what the right name is. And second, because I don’t want to deal with all the edge cases that are going to come up with that. But if you want to do it, feel free, or if you want to incorporate the code, then it’s going to be below in the show notes.