emberj.js is quite a young project so the amount of tutorials isn't high nor the documentation isn't of Django level - but still it's there and it's not that bad. At the moment Ember is close to reaching the 1.0 release and it's quite stable allowing real production application development. In the same category we can also find other similar frameworks like Backboje.js or Angular.js. Each has its strong and weak points.
In this article I'll showcase basic ember features and ember application development process for a Django-Tastypie-Ember application. You need at least basic Django and django-tastypie (REST API-making application for your models). The whole application developed in this article can also be downloaded and launched on your localhost.
On the Python side we will need Django 1.5 or newer (or older with backported "verbatime" template tag). We start with a fresh Django project and application:Next step is to install (pip) django-tastypie and south. Tastypie makes a REST API which ember can use to manipulate the data stored in application models. South is handy for model changes migrations (semi-optional). Add those apps to INSTALLED_APPS:
Add also the quotes application created in previous step.And configure database settings as you desire. I used a SQLite3 database:
manage.py syncdb manage.py migrate
From emberjs.com download ember.js. From handlebarsjs.com download handlebars.js. From a github ember-data-tastypie-adapter repository fetch (packages/ember-data-tastypie-adapter/lib) tastypie_adapter.js and tastypie_serializer.js.
The most tricky part is ember-data which needs to be build. Assuming you use Linux and can install some system packaged this should be easy. Clone the https://github.com/emberjs/data git repository and try to build ember-data:
git clone https://github.com/emberjs/data.git cd data sudo bundle sudo rake dist
As you can read on ember website the framework has few layers, similar to Django or other frameworks. I won't repeat what's written there. I'll just give a quick overview from a Django developer point of view.
Templates are managed by the handlebars library, which uses quite similar tags to those from django templates. The newly added verbatim template tag prevents rendering of Django template tags - thus preserving handlebars templates used by the ember application. As handlebars can't include templates from external files usually all handlebars templates are in one file - one after another (although you could use Django to include verbatimed file-based-templates into the primary template file).
Controller is something like a view in Django. This object stores the state of the application (a part of it). Compared to Django view this object is "alive" as it can respond to events or cause template re-rendering etc. There is a lot of event-driven (asynchronous) behavior in ember application in general.
Views are a layer between a controller and a template. They are used to translate primitive events like click, submit into meaningful evens handled later by the controller (like "createQuote" and not "submit"). In simple cases we won't even have to define a view for given template-controller set.
Models are quite similar layer to Django models. In Django-Ember applications they will reflect your Django models quite close. As ember-data can't do Python Tastypie is used. It creates a REST JSON API which is used by the ember-data tastypie adapter to provide read/write functionality for the ember models. Models in ember are also asynchronous. Executing Model.find() to fetch all entries won't return them but an object that is fetching them and will have them "in a moment". That asynchronous nature affects how controllers or in general ember applications work and are codded - something a Django developer may not be used to.
Routing or route-objects are used to map templates (like urls.py), but also to match (by keeping matching naming) a template with a controller and a model object. In a full blown configuration router tells the controller to represent a data object from the model and render the template for it. In a simple case it's just something like urls.py, while in extended version it can do some extra features like doing stuff on init.
Application renders the primary template and then puts the HTML code of the url-specific template in the place of "outlet" tag. In this template we can create stuff like header, footer, some navigation. Without verbatim tag the outlet would have been render by Django.
tastypie is a handy app that will make an REST API for use to use in ember application. To make a Tastypie resource we first need to have a Django model, like this one:A simple quotes model. To make a resource create quotes/resources.py file with: Our quotes resource needed that we also define a resource for the User model used in relation. If we plan to use data from related objects we have to define resources for them and define those relations also in those resources. We also have to include those resources in urls.py: So now we should have a working Tastypie API. URL like http://127.0.0.1:8000/api/v1/user/?format=json should show a list of users. If you add some quotes via the admin then http://127.0.0.1:8000/api/v1/quote/?format=json should list them too.
Next step is to make Ember models that will use those resources to get/put data.issue). If you don't like it then edit tastypie_serializer and remove the suffix from keyForBelongsTo:
When we have working Tastypie resources we can start creating models in ember application. Data aspects in ember are handled by ember-data. Here we have models for our resources:
At start we define a "store" for which we will create models. Store has an adapter - a plugin that knows how to translate operations on ember models to the operations on the data backend. Model names are matched to the resources names. For longer names made of more than one word (like a FooBarResource) we may need to add resource_name that matches what the adapter wants (you will see requests in the browser console).
This is the third place where we have to describe our models. Each field has a type like string, number, boolean, or date. Relations are done via DS.belongsTo, or DS.hasMany (which needs a reverse DS.belongsTo relation defined too).
We now have working models. Quotes.Quote.find() will return an object that will fetch all quotes. We will use that to show a list of quotes on the main page.
PSo we made a "quotes-list" template. In that template we iterate over "quotes" - a list of quotes. Based on the template name the URL is generated - "/#/quotes-list" (if we don't change that behavior).
Now we have somehow set a list of quotes under that variable. Also we have to add this template to the templates map. Routing is the place for that:
Router.map is a "simple" map of all used templates - in our case one template. Next in line are two extended routes - primary Quotes.IndexRoute and Quotes.QuotesListRoute for the template (note the naming reflecting template name). When we enter the root URL (/) we will be redirected to the quotes-list template URL. When the routing for that template is launched it will assign the list of quotes (object that will have it when it fetches it) to the variable used in the template.
If you look closely you will notice that the username shows after the quotes texts are shown. This is caused by the relation. When we want to display the username in the template ember makes a query to the API for the user data with username. If we had a tree of related objects those would load one after another and template would notice changes (object fetched) and render data from them as they are fetched.
Template-Controller (built in controller for now) ability to watch for changes and take actions/re-render part of the template without reloading the page and without the need for use to code all the annoying stuff is one of the key features of this framework.
At first many things in ember may look like magic (events and asynchronous actions), but when you look how this basic app is being made and how it works - the magic will become clear (or you will become a sorcerer which is cool anyway :)). If you have questions google for solutions or ask directly on stackoverlow, check the documentation or visit #emberjs IRC channel on Freenode.
Now something harder - lets make a template for adding new quotes. This can be done in pure ember-way or the worse, old way with jQuery and DOM manipulation. Knowing the ember way may not be always obvious (that need some experience and more examples in the docs). In this template we will have a form with an input, so I'll use a TextField view embedded in ember. The advantage of using this view is that it binds the input value with a variable available in the controller (in both ways), which makes it easy to fetch/modify the value from the controller (and in other cases to for example watch for changes). Here is the template with the view used:Ember.TextField is a simple view rendering a text input. The "valueBinding" defines the variable under which we will have the input value in AddQuoteController. Here is the controller/template view code (add the template to the map too):
Let us start with AddQuoteView view. As you can see it "translates" a submit event coming from the template into a "saveQuote" event passed to the controller. The controller creates a new Quote object, commits changes to the backend and then it clears the input value (two way binding). this.set/this.get is the ember way to set/get things. At the end we use "transitionToRoute" to redirect the user to the quotes list where he will see his quote (while save request is probably still not finished). Yet another ember-in-action nice feature.
The save operation will fail in the Tastypie resource as we didn't allowed POST requests for object creation. We have to add it to "allowed_methods" as well add some input validation:
obj_create method is called when the record is beign created. We override it to set the correct user as poster or return API error if he isn't authenticated (this isn't an exception so logging for production apps is a very good thing to add).
In upcoming articles we will add more features and Tastypie resources will grow in size. More methods will get overridden and more validation will be applied. Note that this API may not return data that current user shouldn't see/get. That's why we override Tastpie methods to for example filter data only to that the current user should see. Security for a public database query tool like this API is an important issue.
This is the end of this article (although there will be more). I've tried to show a quick start with ember, the initial steps to make an application and some big picture on how ember applications work and differ from pure Django apps. You can also download the whole code and play with it. I'll add new features in upcoming tutorials.