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

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.

Subscribe to our mailing list