Created
November 3, 2014 19:33
-
-
Save AndyDangerous/7f57cc85471330c54852 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<iframe width="420" height="315" src="//www.youtube.com/embed/LYN8BXmb6h4" frameborder="0" allowfullscreen></iframe> | |
There are some tutorials out there to help you get started with pairing the excellent Ember front end javascript framework with a Rails back end API. It can be a little harder to find a condensed source to help you go beyond simple GET requests. | |
This tutorial is based off of a project called [Snowcial](www.snowcial.pw), and will cover implementing CRUD actions for an existing `groups` model. You can substitute this with whatever model makes sense for your project. Requirements include a working app* with `index` and `show` actions. We will go over adding `create`, `update`, and `destroy`. I'll also assume that you are comfortable with Ruby and Rails already though you'll only need passing understanding of javascript and Ember. | |
*App, in this case, means a Rails app providing a JSON API and an Ember front end consuming it. [This set of tutorials](http://reefpoints.dockyard.com/2014/05/07/building-an-ember-app-with-rails-part-1.html) from [Dockyard](http://dockyard.com/) will get you up to speed. | |
###### A note about testing | |
Testing should be first and foremost on your mind when thinking about implementing new functionality for your apps. Unfortunately it is outside the scope of this tutorial. Test your apps. Ask for help or advice if you need it. | |
#### Ember | |
##### Step 1: Routes | |
Routes in Ember are not the same as routes in Rails, but they are also not entirely different. We'll start here. create a new `app/routes/groups/create.js` file and add the following. We are adding a create function for the `group` model and the action to go along with it. | |
```javascript | |
//app/routes/groups/create.js | |
import Ember from 'ember'; | |
export default Ember.Route.extend({ | |
model: function() { | |
return this.store.createRecord('group', { | |
name: '' | |
}); | |
}, | |
actions: { | |
create: function() { | |
var my_model = this.controller.get('model'); | |
my_model.save(); | |
this.transitionTo('groups.show', my_model); | |
} | |
} | |
}); | |
``` | |
Follow suit for the `edit`, `index`, and `show`. | |
```javascript | |
//app/routes/groups/edit.js | |
import Ember from 'ember'; | |
export default Ember.Route.extend({ | |
actions: { | |
submit: function() { | |
var my_model = this.controller.get('model'); | |
my_model.save(); | |
this.transitionTo('groups.show', my_model); | |
} | |
} | |
}); | |
``` | |
```javascript | |
//app/routes/groups/index.js | |
import Ember from 'ember'; | |
export default Ember.Route.extend({ | |
model: function() { | |
return this.store.find('group'); | |
} | |
}); | |
``` | |
```javascript | |
//app/routes/groups/show.js | |
import Ember from 'ember'; | |
export default Ember.Route.extend({ | |
actions:{ | |
delete: function() { | |
this.controller.get('model').destroyRecord(); | |
this.transitionTo('groups.index'); | |
} | |
} | |
}); | |
``` | |
##### Step 2: Templates | |
You will need your templates to have forms for your `create` and `edit` actions. Ember's magic will help you out. Unfortunately, my editor doesn't have handlebars highlighting. That's a thing that you'll want unless you're a total masochist or Swiss, in which case you will prefer [Emblem](http://emblemjs.com/). | |
```javascript | |
//app/templates/groups/create.hbs | |
<h4 class="create-new-group">Create New Group</h4> | |
<p>Name: {{input value=name class='group-name'}}</p> | |
<p>Description: {{input value=description class='group-description'}}</p> | |
<p><button type='submit' {{action 'create'}} class='commit-group-creation'>Submit</button></p> | |
``` | |
```javascript | |
//app/templates/groups/edit.hbs | |
<h4>Edit</h4> | |
<p>Name: {{input value=name class='group-name'}}</p> | |
<p>Description: {{input value=description class='group-description'}}</p> | |
<p><button type='submit' {{action 'submit'}} class='commit-group-change'>Submit</button></p> | |
``` | |
```javascript | |
//app/templates/groups/index.hbs | |
<h3>Groups</h3> | |
<table> | |
<thead> | |
<th>Name</th><th>Trips Taken</th> | |
</thead> | |
<tbody> | |
<p>{{#link-to 'groups.create' class="create-group"}}Create{{/link-to}}</p> | |
{{#each}} | |
<tr> | |
<td> | |
{{~#link-to 'groups.show' this}} | |
{{name}}{{~/link-to}}</td><td>{{trips.length}}</td> | |
</tr> | |
{{/each}} | |
</tbody> | |
</table> | |
``` | |
```javascript | |
//app/templates/groups/show.hbs | |
<h4>{{name}}</h4> | |
{{link-to 'Edit' 'groups.edit' model class='edit-group'}} | |
<button {{action 'delete'}} class="delete-group">Delete</button> | |
<h5>Group Description:</h5> | |
<p>{{description}}</p> | |
<h5>Trips</h5> | |
<ul> | |
{{#each trips}} | |
<li>{{name}}</li> | |
{{/each}} | |
</ul> | |
``` | |
At this point, you may feel the urge to delete `app/templates/groups.hbs`. Go for it. Or don't. It's basically just another partial that can hold your various `groups` templates. | |
##### Step 3: `router.js` | |
Lastly, you'll need to add these new routes to your `router.js` | |
```javascript | |
//app/router.js | |
import Ember from 'ember'; | |
import config from './config/environment'; | |
var Router = Ember.Router.extend({ | |
location: config.locationType | |
}); | |
Router.map(function() { | |
this.route('demo'); | |
this.resource('groups', function() { | |
this.route('show', {path: ':group_id'}); | |
this.route('edit', {path: ':group_id/edit'}); | |
this.route('create', {path: 'create'}); | |
}); | |
}); | |
export default Router; | |
``` | |
#### Rails | |
##### Step 1: Serializers | |
If you don't already have a serializer for your model then you'll need to add `gem 'active_model_serializers'` to your `Gemfile` and generate a new serializer. | |
```terminal | |
$ rails generate serializer group | |
``` | |
The most important part of your serializer is adding the correct attributes. I have added a bit more to mine because I want some of the associations to come along as part of the JSON. | |
```ruby | |
#app/serializers/group_serializer.rb | |
class GroupSerializer < ActiveModel::Serializer | |
embed :ids, include: true | |
attributes :id, :name, :description | |
has_many :trips | |
end | |
``` | |
*Note that `embed` is deprecated. | |
##### Step 2: CSRF Considerations | |
Rails has a default protection against [Cross-Site Request Forgery (CSRF)](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)) attacks. While an in-depth analysis of site security is beyond the scope of this tutorial, you can avoid the problem by changing your ApplicationController. | |
```ruby | |
#app/controllers/application_controller.rb | |
class ApplicationController < ActionController::Base | |
protect_from_forgery with: :null_session | |
end | |
``` | |
That should get you up and running. If you have questions or comments then sound off below or feel free to reach out on [twitter](http://twitter.com/amention) or [GitHub](http://github.com/AndyDangerous). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment