Simple Form Submission with EmberJS

Ember 2.6.0

I've spent way more time than necessary to determine the appropriate way to submit a form with EmberJS. There is literally nothing in the documentation (that I have found, correct me if I'm wrong) that gives a concrete example of "this is how you do this" and for an opinionated framework, I'm a little surprised. For the purpose of this article, we're going to submit a Thing which consists of 2 properties: name and description.

// thing/model.js
import Model from 'ember-data/model';  
import attr from 'ember-data/attr';

export default Model.extend({  
  name: attr('string'),
  description: attr('string'),
});

First, we need a route so that we can display the form as well as handle the save action.

// create-thing/route.js
import Ember from 'ember';

export default Ember.Route.extend({  
  model() {

  },

  actions: {
    createThing() {
    }
  }
});

And the template:

<!-- create-thing/template.hbs -->  
<form class="form-horizontal">  
  <div class="form-group">
    <label for="name" class="col-md-2 control-label">Name</label>
    <div class="col-md-5">
      {{input value=model.name class="form-control"}}
    </div>
  </div>
  <div class="form-group">
      <label for="description" class=" col-md-2 control-label">Description</label>
      <div class="col-md-5">
        {{textarea value=model.description class='form-control'}}
      </div>
  </div>
  <div class="form-group">
   <div class="col-sm-offset-2 col-sm-10">
     <button {{action "createThing"}} class="btn btn-sm btn-primary">Save</button>
   </div>
 </div>
</form>  

The part I struggled with the most was, what is the model here? Well, it should be the Thing model. What we have to do is create a new Thing record and tell our route that it is the model.

// create-thing/route.js
import Ember from 'ember';

export default Ember.Route.extend({  
  model() {
    return this.store.createRecord('thing');
  },

  actions: {
    createThing() {
    }
  }
});

So this is great. Except when we click Save nothing really happens. Let's go ahead and persist the new Thing.

// create-thing/route.js
import Ember from 'ember';

export default Ember.Route.extend({  
  model() {
    return this.store.createRecord('thing');
  },

  actions: {
    createThing() {
      let thing = this.modelFor(this.routeName);
      var self = this;
      thing.save().then(function() {
        self.transitionTo('things');
      }).catch(function(reason) {
      });
    }
  }
});

What's happening here is we're getting the route's current model, which will already contain the properties from the form due to binding, and then we're calling save and handling the results via the promises.

And as a bonus, assuming you're server is returning the json-api standard error response, adding the following to your template.hbs will render the errors; here I'm just assuming errors for Thing.name.

<div class="panel panel-danger">  
{{#each model.errors.name as |error|}}
  {{error.message}}
{{/each}}
</div>  

That, I've found, is the simplest approach. YMMV depending on the complexities of your models.