Category Archives: Ember.js

A tip on connecting djangorestframework{-jsonapi, -jwt} with ember-simple-auth

The main issue that I’ve encountered was the fact that djangorestframework-jsonapi wanted to get requests in different format that ember-simple-auth sent them. This was great for the rest of my Ember app, but authentication didn’t work. The solution is to include both rest_framework.parsers.JSONParser and rest_framework_json_api.parsers.JSONParser in the mix. The final result in settings.py that worked for me was:

REST_FRAMEWORK = {
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_PARSER_CLASSES': (
        'rest_framework.parsers.JSONParser',
        'rest_framework_json_api.parsers.JSONParser',
        ...
    ),
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework_json_api.renderers.JSONRenderer',
        'rest_framework.renderers.JSONRenderer',
        ...
    ),
    'DEFAULT_METADATA_CLASS': 'rest_framework_json_api.metadata.JSONAPIMetadata'
}

Example of ember-form-for and mirage server side validation

At the time of writing, ember-form-for is the top Ember.js plugin for forms on Ember Observer. It provides a straightforward way to display form fields without having to write all the boilerplate HTML code around it.

What you do is you take:

</p>
<p>    &lt;label&gt;First name: {{input value=model.firstname}}&lt;/label&gt;<br />
    {{#each model.errors.firstname as |error|}}<br />
      &lt;span class=&quot;errors&quot;&gt;{{error.message}}&lt;/span&gt;<br />
    {{/each}}</p>
<p>

and replace it with form-for-model helper:

<br />
{{#form-for model as |f|}}<br />
  {{f.text-field &quot;firstname&quot;}}</p>
<p>  .. // other fields and submit button<br />
{{/form-for}}<br />

It will generate HTML code for you, as well as display validation errors.

Complete form becomes much more readable this way:

<br />
{{#form-for model as |f|}}<br />
  {{f.text-field &quot;firstname&quot;}}<br />
  {{f.text-field &quot;lastname&quot;}}</p>
<p>  {{f.select-field &quot;gender&quot; &quot;unknown male female&quot;}}<br />
  {{f.checkbox-field &quot;terms&quot; label=&quot;I agree to the Terms of Service&quot;}}</p>
<p>  {{f.submit &quot;Submit&quot;}}<br />
{{/form-for}}<br />

I’ve created a dedicated Github branch ember-form-for, that you can experiment with:
https://github.com/ember-examples/server-side-validation-mirage/tree/ember-form-for

Ember Server Side Form validation with Django

In previous post (Ember Server Side Form validation with Mirage), I’ve looked at just the Ember.js side. In this article, I’ll connect it with Django so that we have an actual backend that’s returning our errors. It builds on top of previous Ember.js code. There are also full Github repositories at the end.

Lets start with Django. We’ll be using Django Rest Framework to generate server side Django.

We have a basic models.py:

<br />
from __future__ import unicode_literals<br />
from django.db import models</p>
<p>class Registration(models.Model):<br />
    GENDER_CHOICES = (<br />
        ('male', 'Male'),<br />
        ('female', 'Female'),<br />
        ('unspecified', 'Unspecified')<br />
    )</p>
<p>    firstname = models.CharField(max_length=100)<br />
    lastname = models.CharField(max_length=100)<br />
    submitted = models.DateTimeField(auto_now_add=True)<br />

and an API in views.py:

<br />
from rest_framework import viewsets<br />
from rest_framework.response import Response<br />
from rest_framework import status</p>
<p>from .models import Registration<br />
from .serializers import RegistrationSerializer</p>
<p>class RegistrationViewSet(viewsets.ViewSet):<br />
    def create(self, request):<br />
        serializer = RegistrationSerializer(data=request.data)<br />
        if serializer.is_valid():<br />
            serializer.save()<br />
            return Response(serializer.data, status=status.HTTP_201_CREATED)<br />
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)<br />

One thing to note – we return 400 Error code, instead of 422 that we mocked with Mirage. That is because Ember Django Adapter wraps 400 error to make it compatible with JSON API specification.

All of our business logic of API validation happens in serializers.py:

<br />
from rest_framework import serializers</p>
<p>from . models import Registration</p>
<p>class RegistrationSerializer(serializers.ModelSerializer):<br />
    class Meta:<br />
        model = Registration<br />
        fields = ('id', 'firstname', 'lastname')</p>
<p>    def validate_firstname(self, value):<br />
    	if 'john' not in value.lower():<br />
    		raise serializers.ValidationError('First name must be John')<br />
    	return value</p>
<p>    def validate_lastname(self, value):</p>
<p>    	if 'smith' in value.lower():<br />
    		raise serializers.ValidationError(&quot;Last name can't be Smith&quot;)<br />
    	return value</p>
<p>    def validate(self, data):<br />
    	if data['firstname'] == 'John' and data['lastname'] == 'Doe':<br />
    		raise serializers.ValidationError(&quot;Please enter a more original name&quot;)<br />

With server side in place, we can extend the previous example Ember.js app. We’ll be using Ember Django Adapter to make things easier.

First of, we need to turn of ember-cli-mirage in config/environment.js. We also set the development URL and API namespace at the same time.

<br />
  if (environment === 'development') {<br />
    ENV.APP.API_HOST = 'http://localhost:8000';<br />
    ENV.APP.API_NAMESPACE = 'api';</p>
<p>    ENV['ember-cli-mirage'] = {<br />
      enabled: false<br />
    };<br />
  }<br />

Another source of problems is that DS.RESTAdapter pluralises api endpoints. So instead of /api/registration/ it posts to /api/registrations/. To make it stop doing that we can define ‘registration’ as uncountable:
(ember generate drf-adapter application)

<br />
import DRFAdapter from './drf';<br />
import Inflector from 'ember-inflector';</p>
<p>const inflector = Inflector.inflector;<br />
inflector.uncountable('registration');</p>
<p>export default DRFAdapter.extend({});<br />

Controller is still the same:

<br />
import Ember from 'ember';</p>
<p>export default Ember.Controller.extend({<br />
  actions: {<br />
    save() {<br />
      var model = this.get('model');</p>
<p>      model.save().then((registration) =&gt; {<br />
        // it's a mock, we don't do anything<br />
      }).catch((adapterError) =&gt; {<br />
        // we just need to catch error<br />
      });<br />
    }<br />
  }<br />
});<br />

The main improvement is that template error handling now also displays non-field errors (via model.errors.base):

<br />
{{#each model.errors.base as |error|}}<br />
  &lt;span class=&quot;errors&quot;&gt;{{error.message}}&lt;/span&gt;<br />
{{/each}}</p>
<p>&lt;form&gt;<br />
  &lt;p&gt;<br />
    &lt;label&gt;First name: {{input value=model.firstname}}&lt;/label&gt;<br />
    {{#each model.errors.firstname as |error|}}<br />
      &lt;span class=&quot;errors&quot;&gt;{{error.message}}&lt;/span&gt;<br />
    {{/each}}<br />
  &lt;/p&gt;<br />
  &lt;p&gt;<br />
    &lt;label&gt;Last name:<br />
      {{input value=model.lastname}}<br />
    &lt;/label&gt;<br />
    {{#each model.errors.lastname as |error|}}<br />
      &lt;span class=&quot;errors&quot;&gt;{{error.message}}&lt;/span&gt;<br />
    {{/each}}<br />
  &lt;/p&gt;<br />
  &lt;button {{action 'save'}}&gt;Save&lt;/button&gt;<br />
&lt;/form&gt;<br />

With this in place, form validation errors now work correctly. If server side form validation passes, it also writes data into Data Store.

Ember.js code is in ‘django-api’ branch at https://github.com/ember-examples/server-side-validation-mirage/tree/django-api
Django code is at https://github.com/ember-examples/django-drf-serializer-validations

Ember Server Side Form validation with Mirage

In this Ember.js example, I’ll show how to get ember-cli-mirage to return correct JSON-API response, so that Ember Data correctly processes Error message and makes it accessible inside the template.

This code was tested on Ember 2.6.0.

We have the following registration model:

import Model from 'ember-data/model';<br />
import attr from 'ember-data/attr';</p>
<p>export default Model.extend({<br />
  firstname: attr('string'),<br />
  lastname: attr('string'),<br />
});

There are three main elements that make this work: Controller that saves the data, Template that renders form and errors, and Mirage that mocks the response.

Controller’s save action is very basic, it calls save on the model and catches any errors:

import Ember from 'ember';</p>
<p>export default Ember.Controller.extend({<br />
  actions: {<br />
    save() {<br />
      this.get('model').save().then((registration) =&gt; {<br />
        // it's a mock, we don't do anything<br />
      }).catch((adapterError) =&gt; {<br />
        // we just need to catch error<br />
      });<br />
    }<br />
  }<br />
});<br />

Template displays form and any connected errors:

<br />
&lt;form&gt;<br />
  &lt;p&gt;<br />
    &lt;label&gt;First name: {{input value=model.firstname}}&lt;/label&gt;<br />
    {{#each model.errors.firstname as |error|}}<br />
      &lt;span class=&quot;errors&quot;&gt;{{error.message}}&lt;/span&gt;<br />
    {{/each}}<br />
  &lt;/p&gt;<br />
  &lt;p&gt;<br />
    &lt;label&gt;Last name:<br />
      {{input value=model.lastname}}<br />
    &lt;/label&gt;<br />
    {{#each model.errors.lastname as |error|}}<br />
      &lt;span class=&quot;errors&quot;&gt;{{error.message}}&lt;/span&gt;<br />
    {{/each}}<br />
  &lt;/p&gt;<br />
  &lt;button {{action 'save'}}&gt;Save&lt;/button&gt;<br />
&lt;/form&gt;<br />

Now we only need to setup Ember Mirage. We emulate two different states. If firstname is ‘John’, we let request come through, and in all other cases we display error.

<br />
import Response from 'ember-cli-mirage/response';</p>
<p>export default function() {</p>
<p>  this.namespace = '/api';</p>
<p>  this.post('registrations', function(schema, request) {<br />
   let attrs = JSON.parse(request.requestBody).data.attributes;</p>
<p>   if ( attrs.firstname === 'John') {<br />
     return {<br />
       'id': 1,<br />
       'firstname': 'John',<br />
       'lastname': attrs.lastname<br />
     }<br />
   } else {<br />
     return new Response(422, {}, {<br />
        &quot;errors&quot;: [<br />
          {<br />
            &quot;source&quot;: { &quot;pointer&quot;: &quot;/data/attributes/firstname&quot;},<br />
            &quot;detail&quot;: &quot;Please enter John as name&quot;<br />
          }<br />
        ]}<br />
     );<br />
   }<br />
  });<br />
}<br />

In the above example, it’s important that we return error code 422 (Unprocessable Entity), otherwise it will not be processed by JSONAPIAdapter as error.

Example repository

To make easier to see a complete and working example, I’ve also published a repository with complete code:
https://github.com/ember-examples/server-side-validation-mirage

What I learned building Open Education Week with Ember.js

I am lucky that at the Open Education Consortium, I can work with technology of my choosing. For Open Education Week site, I decided to experiment with Ember.js. It was my first production Ember app and I learned some valuable lessons.

Project background

Open Education Week site enables educators to submit their events and resources.  Visitors can then browse them. I used WordPress in previous years, and it worked fine to a point. It didn’t feel right when l I wanted to create more complex browsing interfaces.

Example of that would be: Show me all resources for colleges, that are in Spanish and have the following keywords in it. WordPress can do it, but it requires a lot of different taxonomies and custom queries.

I decided to build a thin Django API layer on top of existing data.

I tried using WP REST API, but it was hard to adopt. If I wanted to do more complex lookups, I still had to write complex WP_Query lookups. So that still wouldn’t help me on server side.

Good things about developing the site with Ember.js

Tooling is great

OEW is an Ember 2.3 application, written with ember-cli. This meant that I didn’t have to fight with grunt/gulp/etc. to get it to work. It just feels right, and it gives you things like real time reloading of CSS.

Components make things easy

It’s easy to make new components in Ember, that almost behave as Web Components. One example of that is my custom OEmbed component. I could reuse both in grid view and inside interactive map popup. (Demo)

Site feels fast

Once the page loads, the only requests you have to make are to the API. My other WordPress sites load fast, but requre refresh for every click. Single page app is fast.

Community

Community of people that hang out on Slack and IRC is incredible and helpful. I just wished that it would also extend to discussion forums. Real time nature of Slack and IRC just don’t make it easy to Google previous discussions on the same topic.

Not so good things

Site is slower to load

Current size is 400kb of gzipped JavaScript assets. Until it loads, you only get a blank screen. I didn’t find how to make it display some sort of initial loading indicator.

My final Javascript size is about 2x the size of vanilla Ember.js with Ember data. It would be good to have tools that would tell me which addon is taking the rest of the space.

Redirects and prerender.io

People rename website URLs all the time. This means that my API, has to keep history of all slug renames. The problem is then – how to communicate it to Google? Google parses JS apps, but doesn’t follow redirects.

I’m currently running preprender.io to solve some of these problems. It works, but it requires extra work via meta headers.

Outdated recipes

As far as I understand, ember-data assumes that I will want to query objects by their ID. If I want to retrieve them by slug, I have to make a query and then return first item of matching results. This works, but it means that records don’t get retrieved from cache.

That is just one approach, but others did’t seem much better. When I Googled: “Ember.js query by slug”, I got a lot of different threads and discussions with varying approaches. The problem is that a lot of them are from 2013. How do I know if this is still the right approach in 2016?

It’s still early in addon ecosystem

When `ember install [addon-name]` just works, it feels magical. The problem I had is that a lot addons at this point assume good knowledge of Ember. I wish they would bundle demo applications and have more documentation. It reminds me a lot about Django’s ecosystem about 6 years ago. Then we got Read the docs and more mature plugins and things go a lot easier.

Is this site ambitious enough?

Ember’s tagline is: “A framework for creating ambitious web applications”.  The ambitious part is often quoted, when people want to use Ember.js only in part. It’s rarely explained what ambitious means.

Conclusion

Fastboot will fix most of my problems, but it’s still a few months away. The rest of my problems should go away as the community grows.

Would I build this site again with Ember.js? I think I would, because it gives me a much better foundation than a WordPress theme.