Upcoming and OnDemand Webinars View full list

Server-Side Validation in Your Ember Application

Geoffrey Schorkopf

Validating your data is important, especially when handling data on both a frontend application and a server-side application. Most forms nowadays can handle validating presence, length and complex regexes (I’m looking at you, password fields), and have matching server-side validations. In Ember CLI, there’s even a few excellent add-ons to help connect form models with useful error messaging.

But what about those trickier errors that require communication between your client and server? You’ll still want to show an error to the user, but must make a request/response to do so. Today, let’s look at uniqueness validation, something not typically covered in Ember add-ons. Our tools today will be a Rails JSON API and an Ember CLI frontend app. We’ll be leveraging Ember Data’s helpful DS.Errors class.

User email addresses are a common attribute that must be unique per row. Let’s set up our simple user registration form in Ember first. It looks like a lot of code, but this is pretty standard route, controller and template to get the form started. Feel free to skim if you know what’s up:

// my-fine-client/app/router.js
import Ember from 'ember';

var Router = Ember.Router.extend();
Router.map(function() {
  this.resource('users', function() {
    this.route('new');
  });
});

export default Router;
// my-fine-client/app/models/user.js
import DS from 'ember-data';

export default DS.Model.extend({
  email: attr('string')
});
// my-fine-client/app/routes/users/new.js
import Ember from 'ember';

var UsersNewRoute = Ember.Route.extend({
  model: function() {
    return this.store.createRecord('user');
  }
});

export default UsersNewRoute;
// my-fine-client/app/controllers/users/new.js
import Ember from 'ember';

var UsersNewController = Ember.ObjectController.extend({
  actions: {
    save: function() {
      var self = this,
          user = this.get('model');

      user.save().then(function() {
        // Log the user in, redirect them, etc.
        self.get('controllers.sessions').send('login', user.get('email'));
      });
    }
  }
});

export default UsersNewController;
# my-fine-client/app/templates/users/new.hbs
<form {{action 'save' on='submit'}}>
  <label>Email: {{input value=email}}</label>
  <button>Sign Up!</button>
</form>

From here on, we’ll make some tiny tweaks to get our server errors in line with DS.Errors expectations. Let’s start with some pseudocode:

  1. Given there is a user with the email “tomster-rocks@bignerdranch.com”
  2. And I fill out the user email field with “tomster-rocks@bignerdranch.com”
  3. And I click “Sign Up!”
  4. Then I should see the error “An Ember-Lover with that Email Already Exists!”

We’ve tackled 1 through 3 so far. Hoorah! The goal of this blog post is to knock out number 4.

First off: what should our API look like? According to Ember Data, you’ll want something that looks like this:

{
  "errors": {
    "email": ["An Ember-Lover with that Email Already Exists!"]
  }
}

On the Rails model side, we’ll need a validation on uniqueness with the message of our choosing. In this case, let’s use our messaging from step 4 of our pseudocode above:

# my-fine-server/app/models/user.rb
class User < ActiveRecord::Base
  validates :email, uniqueness: { message: 'An Ember-Lover with that Email Already Exists!') }
end

We’ll also want to make sure we handle non-persisted data with our users controller. When Ember calls the action user.save(), it’s implicitly sending a POST request with the model’s parameters. Our controller needs to instantiate a new User, with the passed in email string, and attempt to save the object to our database. Here, we’re tackling the “sad path”: rendering the proper status code and error messaging for Ember to interpret:

# my-fine-server/config/routes.rb
MyFineServer::Application.routes.draw do
  resources :users, only: [:create]
end
# my-fine-server/app/controllers/users_controller.rb
class UsersController < ApplicationController
  def create
    user = User.new(user_params)

    if user.save
      render json: user
    else
      render json: { errors: user.errors }, status: :unprocessable_entity
    end
  end

  private

  def user_params
    params.require(:user).permit(:email)
  end
end

It’s that simple on the Rails end! The biggest obstacle is making sure your JSON response reads the way Ember Data understands; Ember Data is fairly fickle that way, but once it’s all hooked up, DS.Errors does the heavy lifting.

Back to Ember-land. Now that we’re getting a response from user.save() that Ember can understand, we can automatically render errors in the template. Note, before we do anything with the template, ember.debug (the handy debugger for development Ember applications) will tell us we’re headed in the right direction:

Ember Server Error

Lovely! For more information on debugging in Ember, check out the Ember.js Guides.

Now that we have Ember reporting the server error correctly, we need to add messaging to our form. Luckily, Ember Data matches this error to an errors attribute on our model object (check out my-fine-client/app/routes/users/new.js for a reminder that our new.hbs template is backed by an instance of our User model). Here’s what our updated template could look like:

# my-fine-client/app/templates/users/new.hbs
<form {{action 'save' on='submit'}}>
  <label>Email: {{input value=email}}</label>
  {{#each errors.email as |error|}}
    {{error.message}}
  {{/each}}
  <button>Sign Up!</button>
</form>

And here’s what we end up with:

User Error Message

Voila! We’ve given users a helpful error message without navigating away from our form, simply by leveraging Ember Data and DS.Errors. Enjoy writing those ambitious web applications, folks!

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project