Metaprogramming-Editing and Saving Generic Attributes

Published on Jan 08, 2016

In this video we further expand our completely generic model viewer by letting the user edit and save attributes. That means you can click on an attribute, edit it, and choose to either cancel or save your changes. In addition, the component that we end up creating is a cool pattern that you may find useful in your apps.

The actual techniques we use to do this are, by themselves, rather simple- componentizing functionality, using get-based metaprogramming- but combining them is definitely satisfying.


Links

Code

First, generate a component:

ember g component attribute-display

Then loop over that component where you were previously displaying it simply:

{{#each model.constructor.attributes as |attribute|}}
  {{attribute-display model=model attribute=attribute}}
{{/each}}

The handlebars for the component is split into editing/not editing, and takes significant advantage of the get helper for metaprogramming:

{{#if isEditing}}
  {{attribute.name}}: {{input value=newValue placeholder=(get model attribute.name)}}
  <button {{action 'save'}}>Save</button>
  <button {{action 'cancel'}}>Cancel</button>
{{else}}
  <span {{action 'editModel'}}>
    {{attribute.name}}: {{get model attribute.name}}
  </span>
{{/if}}

The javascript defines some defaults then handles the actions. The most interesting is the save action:

import Ember from 'ember';

export default Ember.Component.extend({
  isEditing: false,
  newValue: null,
  actions: {
    editModel(){
      this.set('isEditing', true)
    },
    cancel(){
      this.set('newValue', null)
      this.set('isEditing', false)
    },
    save(){
      let {model, attribute} = this;
      model.set(attribute.name, this.get('newValue') || model.get(attribute.name));
      model.save();
      this.set('isEditing', false)
    }
  }
})

Transcript

In some previous episodes, we’ve used the example of the generic model viewer in order to introduce some new properties of DS.Model. And to remind you, our generic property viewer, it can take any model, and it will show you all the properties of it as well as what those properties are for the individual models.

And so this is really cool, but you might want to do even more. And in this episode, I’m going to show you how you can edit the attributes in this generic model viewer. So if you click on the attribute, you can change it, and you could either save it and it’ll change, or you can cancel it and it’ll go back. I’m going to show you how we did that.

The first thing we’ll do is create a component, and we’ll call that component attribute-display. Then when we’re displaying the models, we’ll take this and replace it with the component. In this component, we’ll just paste in what we had before, but we don’t need the break because it’s in a div. And you can see it’s looking just like it did before except now it’s in a component. And since it’s in a component, we can do lots of things with the state of the individual attribute.

The first thing we’ll change is we’ll add an isEditing attribute, and if isEditing is true, then we will display an input field, and we’ll fill this out in a minute, and if it’s false then we’ll display just the property like we did before. Of course if we check this now, it’ll look just like we did before because isEditing is false.

So let’s have an action that turns isEditing to true. So add this action of edit, and we’ll wrap that around what we’re showing when isEditing is false. And this action, edit, all it does is it sets isEditing to true. We can see this in action. We click one of these and it gives us an empty input field.

Now let’s make these a little bit more interactive. We’ll start with a ‘Cancel’ button here that will turn it back into just the regular property. So if we have a cancel action, it’s going to do the inverse of the edit action, so it’s going to set isEditing to false. And then we’ll go ahead and just put a button with that action, and we’ll see that this works. This takes us back to the non-editing state.

So this is great and all that we can go back and forth, but now we want to be able to actually edit this. So we’ll do this in two steps. First by mapping the value, and it having a good placeholder, and second by adding a ‘Save’ button that saves stuff correctly.

So we’ll create a value called newValue that starts off as null, but then you can edit it within the template. So that’s going to be the value that we have on the input. However when they click this, they don’t want to just see an empty spaaaaaace because they previously had something there. So that’s what we’re going to use the placeholder for, and we’re going to go ahead and copy this in order to get the placeholder, and we’ll stick it in parentheses since it’s already inside a handlebars. And what this gives us is it has the previous value as a placeholder, but then you can overwrite it.

Now let’s get a ‘Save’ button so we can save those changes. So we’ll put this button next to the ‘Cancel’ button, and then it’s what’s in the action that’s really interesting. First, we’ll grab the model and attribute properties from the component. And then, we’ll do something interesting. We’ll of course set something on the model, and what we’re setting we’ll get from the attribute, from the name property on the attribute. And then what we’re setting it as... well, if we have a newValue that we’ve set, we’ll do that. Otherwise, we’re going to use this same model of grabbing the attribute using the model and the attribute name. So this is a whole lot of metaprogramming-type stuff here, but it’s not too much different than what we’ve done before. Then we call save and we’ll set isEditing to false.

We can see this in action. We’ll go ahead and change the level from 2 to 3, and we’ll save it, and there we go. We reload the page and it’s still 3, and if we change it to 2 and hit ‘Cancel’ again, well it’s still 3 because we hit ‘Cancel’. But there is something we forgot to do. If we hit it again, it’ll say 2 right there instead of 3. So here in our cancel, we need to make sure to set newValue back to null.

And with that, we’ve created a generic proper attribute editor for our generic model viewer. Of course, we’re not completely done with this. If we wanted to go further, we could edit the relationships. We could add and delete instances. But in our next episode, we’re going to go ahead and get back to doing lots of stuff about Ember data. If you really want to see more about this, go ahead and tell me in the comments and I would consider creating more. Otherwise, I will see you next week with more Ember data.

Subscribe to our mailing list