Four Key Reasons to Learn Markdown
Back-End Leveling UpWriting documentation is fun—really, really fun. I know some engineers may disagree with me, but as a technical writer, creating quality documentation that will...
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:
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:
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:
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!
Writing documentation is fun—really, really fun. I know some engineers may disagree with me, but as a technical writer, creating quality documentation that will...
Humanity has come a long way in its technological journey. We have reached the cusp of an age in which the concepts we have...
Go 1.18 has finally landed, and with it comes its own flavor of generics. In a previous post, we went over the accepted proposal and dove...