Ember 路线未按顺序加载

Ember routes not loaded in order

我遇到一个问题,我的路线上的 [​​=20=](或 afterModel)未被调用 应用程序控制器已设置之后.这是一个问题,因为我需要来自应用程序控制器的一些数据。

在另一条路线中,这按我的预期工作(应用程序控制器在路线之前加载)。唯一的区别是有效的是 Ember.ArrayController 而无效的是标准 Ember.Controller。我正在使用 beforeModel,即使没有模型,只是因为如果不满足条件,我会尝试从它过渡。

如果我将不工作的控制器更改为 Ember.ArrayController 并在路由器中进行无意义的 model 调用,那么它仍然无法工作。

我能想到的唯一其他区别是,不工作的是嵌套控制器,仅由 route 定义,而工作的控制器未嵌套,由 [=28 定义=].

Router.map(function() {

  this.resource("wizard", function() {
    this.resource("wizard/schools", {path: "/schools"}, function() {
      this.route("new"); // This one doesn't work
    });
  });

  ...

  this.resource("schools", { path: "/schools" }); // This one does work
});

知道如何确保应用程序 route/controller 在我的路由之前加载吗?

我正在尝试按照以下方式做一些事情:

beforeModel: function() {
  var customerAccountPromise = this.controllerFor('application').get('customerAccount');
  var self = this;
  return customerAccountPromise.then(function(customerAccount) {
    return self.get('store').find('school').then(function(schools) {
      if (customerAccount.get('schoolLimit') <= schools.get('length')){
        self.transitionTo('school', schools.get('firstObject'));
      }
    });
  });
},

我目前 运行:

DEBUG: Ember      : 1.8.1
DEBUG: Ember Data : 1.0.0-beta.12
DEBUG: Handlebars : 1.3.0
DEBUG: jQuery     : 1.11.1

更新:这里是工作路线的代码:

import Ember from 'ember';
import AuthenticatedRoute from "./authenticated";

var SchoolsRoute = AuthenticatedRoute.extend({
  model: function() {
    return this.get('store').find('school');
  },
  afterModel: function(schools) {
    Ember.Logger.log("SchoolsRoute - afterModel");
    var customerAccountPromise = this.controllerFor('application').get('customerAccount');
    var self = this;
    return customerAccountPromise.then(function(customerAccount) {
      if (customerAccount.get('schoolLimit') === 1){
        self.transitionTo('school', schools.get('firstObject'));
      }
    });
  },
});

导出默认学校路线;

应用程序控制器只是会话控制器的别名。

import Ember from 'ember';

var ApplicationController = Ember.Controller.extend({
  needs: ['session'],

  currentUser: Ember.computed.alias('controllers.session.currentUser'),
  customerAccount: Ember.computed.alias('currentUser.customerAccount'),

  ...
});

export default ApplicationController;

应用程序的路由器非常简单:

import Ember from 'ember';

var ApplicationRoute = Ember.Route.extend({
  setupController: function(controller, model) {
    Ember.Logger.log("application setupController");
    return this._super(controller, model);
  },
  ...
});

export default ApplicationRoute;

AuthenticatedRoute 中的重要部分是 beforeModel:

beforeModel: function(transition) {
  Ember.Logger.log("AuthenticatedRoute - beforeModel");
  if (Ember.isEmpty(this.controllerFor('session').get('token'))) {
    // set notification
    return this.redirectToSignIn(transition);
  }
},

SessionController中没有其他"setting up"。它只是通过 controllerFor 调用。在 init 上,它调用一个 loadUser 方法,该方法立即记录消息 "SessionController - loadUser" 然后执行 store.find 调用,当 return 时它记录一条消息关于设置用户。

并且日志显示应用程序首先得到设置:

DEBUG: -------------------------------
DEBUG: Ember      : 1.8.1
DEBUG: Ember Data : 1.0.0-beta.12
DEBUG: Handlebars : 1.3.0
DEBUG: jQuery     : 1.11.1
DEBUG: -------------------------------
AuthenticatedRoute - beforeModel
SessionController - loadUser
generated -> route:loading Object {fullName: "route:loading"}
application setupController
Rendering application with default view <school-app@view:toplevel::ember365> Object {fullName: "view:application"}
generated -> controller:loading Object {fullName: "controller:loading"}
Rendering loading with default view <school-app@view:default::ember386> Object {fullName: "view:loading"}
XHR finished loading: GET "http://0.0.0.0:4200/api/users?email=ryan%40ryanjm.com".
SessionController - set user
Ember Inspector Active
XHR finished loading: GET "http://0.0.0.0:4200/api/school".
SchoolsRoute - afterModel

这是会话控制器:

import Ember from 'ember';

var SessionController = Ember.Controller.extend({
  // called by updateHeaders
  loadUser: function() {
    Ember.Logger.log("SessionController - loadUser");
    if (this.get('isSignedIn') && !this.get('currentUser')){
      var self = this;
      this.store.find('user', { email: this.get('email') }).then(function(users) {
        Ember.Logger.log("SessionController - set user");
        self.set('currentUser', users.get('firstObject'));
      });
    }
  },

  // if email/token change, update the headers
  updateHeaders: function() {
    if (this.get('isSignedIn')){
      Ember.$.ajaxSetup({
        headers: {
          'X-User-Email': this.get('email'),
          'X-User-Token': this.get('token')
        }
      });
      this.loadUser();
    }
  }.observes('email', 'token').on('init'),

  hasEmail: Ember.computed.notEmpty('email'),
  hasToken: Ember.computed.notEmpty('token'),
  isSignedIn: Ember.computed.and('hasEmail', 'hasToken'),

});

export default SessionController;

根据 Kingpin2k 的建议,我查看了它是竞争条件的情况,这就是问题所在。

因此,我将我的 SessionController 更改为一个普通对象,然后将其注入到应用程序路由中,这样我就可以 return loadUser 函数作为 beforeModel 中的一个 promise。

在实例化或连接任何控制器之前调用所有模型挂钩(对于 url)。您的应用程序控制器似乎是为您的其中一条路线设置的,因为您已经访问了一个父路线,该路线已经实例化并连接了应用程序控制器上的模型。尝试在您认为有效的路线上刷新页面,应用程序控制器很可能不会被连接(或者您在 setupController 期间这样做,然后应用程序控制器将被连接为出色地)。

也就是说,您可能需要来自应用程序控制器上的基础模型的数据。您可以使用 modelFor 从应用程序控制器获取模型并执行您的逻辑。此外,由于您不需要客户帐户中的任何数据来获取学校信息,因此您可以同时而不是连续地进行这些调用。

beforeModel: function() {
  var customerAccountPromise = this.modelFor('application').get('customerAccount'),
      self = this;
  return Em.RSVP.hash({
       customerAccount: customerAccountPromise,
       schools: this.store.find('school')
  }).then(function(hash) {
      if (hash.customerAccount.get('schoolLimit') <= hash.schools.get('length')){
        self.transitionTo('school', hash.schools.get('firstObject'));
      }
  });
  // also you don't have anything to catch the exception if something goes wrong here!
},

P.S。每次你点击这条路线时,你都会去从服务器获取所有学校。您可能会考虑获取所有学校一次,然后只从商店中获取所有学校,而不是每次都调用它们。但这只是一个建议,可能对您的业务逻辑没有意义。

承诺您的用户加载

var SessionController = Ember.Controller.extend({
  // called by updateHeaders
  loadUser: function() {
    Ember.Logger.log("SessionController - loadUser");
    if (this.get('isSignedIn') && !this.get('currentUser')){
      var self = this;
      var loadingPromise = this.store.find('user', { email: this.get('email') }).then(function(users) {
        Ember.Logger.log("SessionController - set user");
        self.set('currentUser', users.get('firstObject'));
      });
      this.set('loadingPromise', loadingPromise);
    }
  },

  // if email/token change, update the headers
  updateHeaders: function() {
    if (this.get('isSignedIn')){
      Ember.$.ajaxSetup({
        headers: {
          'X-User-Email': this.get('email'),
          'X-User-Token': this.get('token')
        }
      });
      this.loadUser();
    }
  }.observes('email', 'token').on('init'),

  hasEmail: Ember.computed.notEmpty('email'),
  hasToken: Ember.computed.notEmpty('token'),
  isSignedIn: Ember.computed.and('hasEmail', 'hasToken'),
  loadingPromise: Ember.RSVP.reject()

});

已验证路由

beforeModel: function(transition) {
  Ember.Logger.log("AuthenticatedRoute - beforeModel");
  var sessionController = this.controllerFor('session');
  if (sessionController.get('hasToken')) {
    // set notification
    return this.redirectToSignIn(transition);
  } else {
    return sessionController.get('loadingPromise');
  }
},

Bleh,我在写这篇文章时才意识到,您正在覆盖 AuthenticatedRoute 中实现的 beforeModel 挂钩,因此当您点击该路线时,它不会触发 beforeModel 挂钩。在其他更改之前尝试此操作,看看它是否修复了您的预期结果。

beforeModel: function(transition) {
  this._super(transition)
  var customerAccountPromise = this.modelFor('application').get('customerAccount'),
      self = this;
  return Em.RSVP.hash({
       customerAccount: customerAccountPromise,
       schools: this.store.find('school')
  }).then(function(hash) {
      if (hash.customerAccount.get('schoolLimit') <= hash.schools.get('length')){
        self.transitionTo('school', hash.schools.get('firstObject'));
      }
  });
  // also you don't have anything to catch the exception if something goes wrong here!
},