How to load data in application route


#1

I’m using ESA (ember-simple-auth) and have login and logout actions defined in application.js route as follows:

# routes/application.js

import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
import config from '../config/environment';

export default Route.extend(ApplicationRouteMixin, {
  currentUser: service('current-user'),

actions: {
    login: function() {
      let oauthUrl = config.oauthUrl;
      let clientId = config.clientID;
      let redirectURI = `${window.location.origin}/callback`;
      let responseType = `token`;
      let scope = `profile%20openid`;
      window.location.replace(oauthUrl
                            + `?client_id=${clientId}`
                            + `&redirect_uri=${redirectURI}`
                            + `&response_type=${responseType}`
                            + `&scope=${scope}`
      );
    },
    logout() {
      this.get('session').invalidate();
    }
  },

  beforeModel() {
    return this._loadCurrentUser();
  },

  sessionAuthenticated() {
    this._super(...arguments);
    this._loadCurrentUser();
  },

  _loadCurrentUser() {
    return this.get('currentUser').loadCurrentUser().catch(() => this.get('session').invalidate());
  }

The problem is that when I tried to load some data in model hook like that in the same router:

# routes/application.js
...
model() {
    return this.store.findAll('shop');
  },
...

it failed because I had no token at that moment and /shops end-point was protected in the back-end side (Rails).

I was going to display a drop-down list in the application.hbs template:

{{#if session.isAuthenticated}}
<select>
{{#each model as |shop|}}
    <option value={{shop.id}}>{{shop.identifier}}-{{shop.name}}</option>
{{/each}}
</select>
{{/if}}

In the same application.hbs temlate I have a navbar where the above drop-down list should be displayed. I can display loged in user name and logout button or login button in the navbar depending on if the user is logged in or not:

{{#if session.isAuthenticated}}
        {{#if currentUser.user}}
<li class="nav-item dropdown">
            <a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown"
              aria-haspopup="true" aria-expanded="false">
              <span class="oi oi-person" title="user" aria-hidden="true"></span>
              {{currentUser.user.username}}
            </a>
            <div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
              <a {{action 'logout'}} class="dropdown-item">{{t 'navbar.logout'}}</a>
            </div>
          </li>
        {{/if}}
      {{else}}
        <a {{action 'login'}} class="btn btn-light">{{t 'navbar.login'}}</a>
      {{/if}}

How to achieve that behaviour ? Is it possible to load a model from another route, controller ? Or what I’m doing is not possible ? Thank you.


#2

I’m not sure if this is actually best practice or not but what we do in our application route model hook is just check to see if we’re authenticated before doing anything, e.g.:

  model: function(/*params ,transition*/){
    // we don't want to try loading anything if we're not authenticated
    if (this.get("session.isAuthenticated")) {
      return ...;
    }
  }

#3

@dknutsen Dan, thank you for your response. If I apply the solution you advised, it works but I have to reload the page to see the shops in the drop-down list once authenticated. Is it the problem is that application.js router extends ApplicationRouteMixin (the others extend AuthenticatedRouteMixin) ? I have my navbar that should display the shop select list in application.hbs template. The idea is as follows:

  • display an empty navbar with Login button if not authenticated
  • display the navbar with select list of shops and Logout button once authenticated.

May by I have to put everything in a separate shop template and try to render it in application template. In this case I’ll have to define all the shop related routes as nested…


#4

You could always trigger a refresh when authentication succeeds. Again not sure if best practice, but it should work.


#5

what is strange is that if I display the authenticated user shop in the select dropdown list (user belongs_to shop), it works.

# application.hbs
...
{{#if session.isAuthenticated}}
<select>
  <option value={{currentUser.user.shop.id}}>{{currentUser.user.shop.shopName}}</option>
</select>
{{/if}}

It seems like model hook is called a little bit later ?