4

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?

Update

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');
Ross
  • 115
  • 1
  • 8
  • Using a service is the way to go. I advise you try to implement it and then update your question with the eventual code you have. – locks Mar 02 '16 at 01:38

1 Answers1

3

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');
    }
  }
});
Deovandski
  • 770
  • 1
  • 8
  • 22
  • 1
    Thanks. I ended up doing something very similar to this but modified to work with Firebase. I created a `loadCurrentUser` method in a service, which is called both when the user logs in and in the Application route. – Ross Mar 20 '16 at 23:35
  • Glad that your implementation worked! Also, I suggest editing my answer or adding an update section to your question with the Firebase modification since emberfire is one of the tags. – Deovandski Mar 20 '16 at 23:47
  • @Ross Could you share your code for the modification of this answer? I am building an Ember app with Emberfire too and am running into the same problem you were. –  Jun 27 '16 at 16:52
  • @NickEmmons see my edit to the original question above. Hope that helps – Ross Jun 29 '16 at 20:57