In my EmberJS application, I have a current user initializer, which injects the user into all controllers, routes, and views. It works great when logged in. I need the synchronous loading of the current user object so I can check some user permissions right off the bat.
Here's my initializer:
App.CurrentUserInitializer - Ember.Initializer.extent({
name: 'current-user',
after: 'authentication',
initialize: function(container, app) {
var store = container.lookup('store:main');
app.deferReadiness();
store.find('user', 'me').then(function (user) {
app.register('session:user', user, {instantiate: false});
_.forEach(['route', 'controller', 'view'], function (place) {
app.inject(place, 'currentUser', 'session:user');
});
app.advanceReadiness();
}).catch(function () {
app.advanceReadiness();
});
}
});
Where this breaks down for me, is during login. When the app boots, the initializer is run, but the /users/me
route returns a 401 error. If I don't catch the error and advanceReadiness, booting halts. By catching the error, the app boots, but the initializer isn't run again after login, so the current user isn't loaded.
What are my options here? I can't use @marcoow's recommended method of adding a computed property to the Session, as I need the user loaded at boot time.
I've tried forcing loading the user object on the IndexRoute as a hack, but that doesn't seem to work.
Any tips are appreciated.
I'd register a session:current
object with user
property being null. That would be injected into controllers
and routes
(not sure injecting inside views is a good idea).
So at boot time the user
is unknown, but the user lookup is done before the router goes deeper than application
route, the root:
In the beforeModel
hook of the application
route, you'll load that current user. Then:
this.set('session.user', model)
error
hook of the application
route, in which case you'd have to check why, and if 401
then you can redirect the user to the login route this.transitionTo('login')
Don't forget to set a flag on session
if you got the 401
so that transitionTo
will make our user lookup of beforeModel
to not happen again until we reach the login
route
The code to be used to load the session user and initialise it could be placed in that session:current
object so that you could call it from the application
route or the login
controller.
This is for example my session
initialiser (not exactly as I explained, but loading in the initialiser, so closer to what you hve). I used a session
model so that I do /session/current
and then got a user into it (or not) which has the correct id and not me
which then would make the store to load the same user with another id, and so have twice the same user as 2 different records:
app/models/session.js:
import DS from 'ember-data';
import Ember from 'ember';
export default DS.Model.extend({
user: DS.belongsTo('user'),
isAuthenticated: Ember.computed.bool('user.isClaimed')
});
app/initializers/session.js:
import Ember from 'ember';
export default {
name: 'session',
after: 'store',
initialize: function (container, app) {
var store = container.lookup('store:main'),
sid = Ember.$.cookie('sid');
// used to register a session
var register = function (session) {
app.register('session:main', session, {instantiate: false});
app.inject('route', 'session', 'session:main');
app.inject('controller', 'session', 'session:main');
};
// used to create a new session and trigger the backend to get details about it
// useful if the server is able to give an existing session while the browser doesn't know about it
// with external providers for example
var newSession = function () {
var session = store.createRecord('session');
// be sure to wipe out any invalid session ID
Ember.$.removeCookie('sid');
register(session);
return session.save().then(function (model) {
// if we got a valid new session, save its ID
Ember.$.cookie('sid', model.get('id'));
}).catch(function () {
Ember.debug('error saving new session: ' + Array.prototype.join.call(arguments, ', '));
});
};
// overall logic ==================
app.deferReadiness();
if (sid) {
// try to load the existing session
store.find('session', sid).then(function (model) {
register(model);
app.advanceReadiness();
}).catch(function () {
// there was a cookie for the session but it might have expired or the server invalidated it
Ember.debug('error loading session: ' + Array.prototype.join.call(arguments, ', '));
newSession().finally(function () {
app.advanceReadiness();
});
});
}
else {
// we don't have any trace of a session, let's just create a new one
newSession().finally(function () {
app.advanceReadiness();
});
}
}
};
app/router.js:
import Ember from 'ember';
var Router = Ember.Router.extend();
Router.map(function () {
this.resource('session', {path: '/'}, function(){
this.route('login');
this.route('logout');
});
});
export default Router;
app/templates/application.hbs (as an example):
<h2 id='title'>Welcome to my app</h2>
{{#if session.isAuthenticated}}
<a {{action 'session.logout'}}>Logout</a>
{{else}}
{{#link-to 'session.login'}}Login{{/link-to}}
{{/if}}
{{outlet}}
Then once in login controller, when the user actually logs in, the server will return the session
model with the user linked into it, so the Ember binding magic will just update the session object.