Roman Ganz
Roman Ganz

Reputation: 1571

Request in Ember before rendering a route

In my App's localStorage I have stored a token for auth. If the user makes a full reload I need to make a request to the backend to verify this token and to get the username. For all this session stuff I have made an ember service. My application route looks like this:

import Ember from 'ember';
export default Ember.Route.extend({
    currentSession: Ember.inject.service(),
    beforeModel: function(transition) {
        if (transition.targetName !== 'login') {
            const session = this.get('currentSession');
            if (!session.isLoggedIn) {
                this.transitionTo('login');
            }
        }
    }
});

On reload the beforeModel method on the application route is triggered, and I can get the isLoggedIn property from the service.

In the service I have a initializeService method which is called on init:

import Ember from 'ember';
export default Ember.Service.extend({
    store: Ember.inject.service(),
    username: '',
    isLoggedIn: false,

    initializeService: function() {
        const token = localStorage.getItem('Token');
        if (token) {
            const self = this;
            this.get('store').findRecord('session', token).then(function(session) {
                self.set('username', session.get('username'));
                self.set('isLoggedIn', true);
            });
        }
    }.on('init'),
});

This works basically, but there is a race condition because of the async request findReocord. initializeService is not called until this.get('currentSession'); in beforeModel, and while the service is requesting the backend, beforeModel continues. So the if (!session.isLoggedIn) is always true. I tried to initialize the service earlier by using an instance-initializer but that didn't work either. I think I need to make some kind of a synchronous findRecord.

Is there a possibility to do a request synchronous or is there a better way?

Upvotes: 2

Views: 360

Answers (1)

Ember Freak
Ember Freak

Reputation: 12872

Introduce method named loadCurrentUser in current-session service which returns Promise and in beforeModel use then method to implement your logic.

Sample Code, current-session.js service file

import Ember from 'ember';
export default Ember.Service.extend({
    store: Ember.inject.service(),
    username: '',
    isLoggedIn: false,
    init() {
        this._super(...arguments);
        this.loadCurrentUser(); //if you are not calling loadCurrentUser method then this will ensure username and isLoggedIn set properly
    },
    loadCurrentUser() {
        return new RSVP.Promise((resolve, reject) => {
            const token = localStorage.getItem('Token');
            if (!isEmpty(token)) {
                return this.get('store').findRecord('session', token).then((session) => {
                    this.set('username', session.get('username'));
                    this.set('isLoggedIn', true);
                    resolve();
                }, reject);
            } else {
                resolve();
            }
        });
    }
});

application.js route file

import Ember from 'ember';
export default Ember.Route.extend({
    currentSession: Ember.inject.service(),
    beforeModel: function(transition){
        return this.get('currentSession').loadCurrentUser().then((result) => {
            if (transition.targetName !== 'login') {
              const session = this.get('currentSession');
              if (!session.isLoggedIn) {
                this.transitionTo('login');
              }
            }
        }
    },    
});

Upvotes: 1

Related Questions