I am writing an ember app (using ember 2.3.0) using emberfire & torii for authentication. After a user logs in, their uid is available to me in the torii session
object. I also have a user
model and would like to access other data related to the current user in my templates, routes, etc.
I can get this to work in a single route by doing something like:
let uid = this.get('session').get('uid');
this.store.findRecord('user', uid).then(user => {
console.log(user.get('firstName'));
});
but would like to prevent having to write this for each route/controller that needs to access it.
Could anyone advise on the best way to do this? Is the best way to use a service? If so, how can I ensure that the code in my service is executed after my session object is available?
I managed to get my application to work with the following solution:
Create a method to login user using firebase
I created a mixin to handle the logging in behaviour. This is then used on both the login page and the sign up page.
// Mixin to handle logging in
import Ember from 'ember';
export default Ember.Mixin.create({
user: Ember.inject.service(),
email: null,
errorMsg: null,
logInUser(email, password) {
// logout existing user if any and then login new user
this.get('session').close()
.then(() => {
// if already a user logged in
this.firebaseLogin(email, password);
})
.catch(() => {
// if no user logged in
this.firebaseLogin(email, password);
});
},
firebaseLogin(email, password) {
this.get("session").open("firebase", {
provider: 'password',
email: email,
password: password
})
.then((data) => {
// If successful, fetch the user and transition to home page
console.log("Successfully logged in as ", data);
this.get('user').loadCurrentUser().then(() => {
this.transitionToRoute('index');
});
})
.catch(error => {
this.set('errorMsg', error);
});
},
});
Create a user
service
This is used to associate the user
model with the authentication user.
app/services/user.js
import Ember from 'ember';
export default Ember.Service.extend({
store: Ember.inject.service(),
session: Ember.inject.service(),
currentUser: null,
loadCurrentUser() {
return new Ember.RSVP.Promise((resolve, reject) => {
const uid = this.get('session').get('uid');
if (!Ember.isEmpty(uid)) {
return this.get('store').find('user', uid).then((user) => {
this.set('currentUser', user);
resolve();
}, reject);
} else {
this.set('currentUser', null);
resolve();
}
});
}
});
Inject user
service into application route
The application route is called whenever the application is loaded (when the user refreshes the page for example). Therefore, as @Deovandski mentioned in his answer, you need to inject it on the Application route in order for the user account to be available globally.
app/routes/application.js
import Ember from 'ember';
export default Ember.Route.extend({
user: Ember.inject.service(),
beforeModel() {
return this.get("session").fetch()
.then(() => {
// Session retrieved successfully
console.log('session retrieved');
return this.get('user').loadCurrentUser();
})
.catch(() => {
// Session could not be retrieved
this.transitionTo('login');
});
}
});
Inject the user
service wherever it is needed
You can then access the current user in the following manner:
user: Ember.inject.service()
...
let currentUser = this.get('user').get('currentUser');
As mentioned by locks in the comments, the best bet is to use a service. I am using Ember-Simple-auth for my session, and this is how I implemented my own custom session that holds the logged in user globally:
Services/session-account.js
import Ember from 'ember';
const { inject: { service }, RSVP } = Ember;
export default Ember.Service.extend({
session: service('session'),
store: service(),
loadCurrentUser() {
return new RSVP.Promise((resolve, reject) => {
const uid = this.get('session.data.authenticated.uid');
if (!Ember.isEmpty(userId)) {
return this.get('store').find('user', uid).then((user) => {
this.set('user', user);
resolve();
}, reject);
} else {
resolve();
}
});
}
});
Injecting Services into Application Route
You need to inject it on the Application route in order for the user account to be available globally.
const { service } = Ember.inject;
export default Ember.Route.extend(ApplicationRouteMixin, {
session: service('session'),
sessionAccount: service('session-account'),
beforeModel() {
return this.get('sessionAccount').loadCurrentUser();
}
});
Example of injecting services into any other Route/Controller
This example uses the session-account to compare the current logged in user to the model being retrieved. The purpose is to only allow the profile info to be edited by the user who owns it.
import Ember from 'ember';
const { service } = Ember.inject;
export default Ember.Route.extend ({
session: service('session'),
sessionAccount: service('session-account'),
model: function() {
return Ember.Object.create ({
user: this.modelFor('user'),
users: this.store.findAll('user')
});
},
afterModel(model, transition) {
if (model.user.get('id') === this.get('sessionAccount.user.id')) {
// Allow Editing
}
else{
this.transitionTo('index');
}
}
});