1
votes

I use the RESTadpater to persist data. When a validation error occurs, I want to return a 422 response and then log the errors and show an indication next to each incorrect field.

My REST response status code is as follows:

Status Code:422 Unprocessable Entity

My REST response body is as follows:

{
  "message": "Validation failed",
  "errors": [
    {
      "name": "duplicate"
    }
  ]
}

In my controller, the becameInvalid fires correctly.

App.AuthorsNewController = Ember.ObjectController.extend({

  startEditing: function () {
    //Create a new record on a local transaction
    this.transaction = this.get('store').transaction();
    this.set('model', this.transaction.createRecord(App.Author, {}));
  },

  save: function (author) {

    //Local commit - author record goes in Flight state
    author.get('transaction').commit();  

    //If response is success: didCreate fires
    //Transition to edit of the new record 
    author.one('didCreate', this, function () { 
      this.transitionToRoute('author.edit', author);
    });

    //If response is 422 (validation problem at server side): becameError fires
    author.one('becameInvalid', this, function () {
       console.log "Validation problem"
    });
  }

  ...

2 QUESTIONS:

  1. I want to log below the 'console.log "Validation problem"', the complete list of errors returned by the server. How can I do that ?

  2. In my hbs template, I want to indicate an error next to the relevant field. How can I do this ?

I am not sure that the data returned via REST adapter is correct. So problem might be at the REST side or at the Ember side ...

2

2 Answers

3
votes

Solution:

In controller save function:

    author.one('becameInvalid', this, function () {
       console.log "Validation problem"
       this.set('errors', this.get('content.errors'));
    });

In hbs template:

    {{view Ember.TextField valueBinding='name'}} 
    {{#if errors.name}}{{errors.name}}{{/if}}
1
votes

Here is how I do it, may not be the best practice but it works for me:

  1. instead of using commit(), I use save(), and if you wonder what's the difference, here is the link. I haven't tried your approach of using transaction, but basically I create the record using record = App.Model.createRecord(...), and here is the code of my apiAddShop function inside the AddShopController:

    apiAddShop: function() {
    //console.log("add shop");
    newShop = App.Shop.createRecord({name:this.get('name'), currentUserRole:"owner"});
    
    //this.get('store').commit(); // Use record.save() instead, then() is not defined for commit()
    var self = this;
    newShop.save().then(function(response){
        self.transitionToRoute('shops');
    }, function(response){
        // if there is error:
        // server should respond with JSON that has a root "errors"
        // and with status code: 422
        // otherwise the response could not be parsed.
        var errors = response.errors;
        for(var attr in errors){
            if (self.hasOwnProperty(attr)) {
                self.set(attr+"Error", true);
                self.set(attr+"Message", Ember.String.classify(attr)+" "+errors[attr]);
            }
            console.log(attr + ': ' + errors[attr]);
        }
        console.log(response.errors.name[0]);
    });
    

    },

  2. the above code assume there is a attrError(boolean) and attrMessage(string) for each of the attributes in your form. Then in your template, you could bind the class of your field to these error attributes, such as <div {{bindAttr class=":control-group nameError:error:"}}>, and the error message could be easily display next to the form field such as: <span {{bindAttr class=":help-inline nameError::hidden"}} id="new_shop_error">{{nameMessage}}</span> Here is my example handlebar gist (have to use gist here, since SO is escaping my html inputs).