Using Rails & Devise with Ember.js
December 2013: The code has been updated to work with the latest versions of Ember and Ember Data.
Using Devise with Ember.js is pretty easy. Basically, what we need to do is store the current user's data somewhere in HTML to avoid sending an ajax request when our app starts.
Note, this article assumes you've already integrated Ember.js into your Rails app. If not, there is a great series of articles by Brian Cardarella: Building an Ember app with RailsAPI
So, let's start. A meta
tag seems the perfect place for putting the current user's data.
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<%= stylesheet_link_tag "application", media: "all" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
<%- if user_signed_in? %>
<meta name="current-user" content="<%= UserSerializer.new(current_user).to_json(root: false) %>" />
<% end %>
</head>
<body>
</body>
</html>
Using UserSerializer
right in the view is not a very good practice but in our case (very simple server side layout) it will work.
Next, we need to create CurrentUserController
with an isSignedIn
property.
App.CurrentUserController = Ember.ObjectController.extend
isSignedIn: (->
@get('content') != null
).property('content')
Then we need to create an initializer that will actually parse the data from the dom put it into CurrentUserController
. We can do it in /initializers/currentUser.js.coffee
:
Ember.Application.initializer
name: 'currentUser'
initialize: (container) ->
store = container.lookup('store:main')
attributes = $('meta[name="current-user"]').attr('content')
if attributes
user = store.push('user', store.serializerFor(App.User).normalize(App.User, JSON.parse(attributes)))
controller = container.lookup('controller:currentUser').set('content', user)
container.typeInjection('controller', 'currentUser', 'controller:currentUser')
What we do here is create an instance of User
and then put it into the content
variable of CurrentUserController
. We then perform typeInjection
on all the controllers in our app to give them currentUser
variable so that we don't need to set it manually.
Now, you can access currentUser
in any template without needing to set it manually in your controller's needs
:
<header>
<h1>
{{#if currentUser.isSignedIn}}
Hi, {{currentUser.name}}
{{else}}
Hello, Guest
{{/if}}
</h1>
</header>
If you do not want to inject currentUser
into every controller out there, here's what you can do:
First, remove this line from the initializer:
container.typeInjection('controller', 'currentUser', 'controller:currentUser')
Then, whenever you need the current user, use needs
:
App.ApplicationController = Ember.Controller.extend
needs: ['currentUser']
It will be available in the template as controllers.currentUser
:
{{#if controllers.currentUser.isSignedIn}}
Hi, {{controllers.currentUser.name}}
{{else}}
Hi, Guest
{{/if}}
You're the 34570th person to read this article.