Ember路线未按顺序装载

时间:2015-01-04 16:25:57

标签: ember.js

我在设置应用程序控制器之后,我的路线上的beforeModel(或afterModel)未被称为,我遇到了问题。这是一个问题,因为我需要来自应用程序控制器的一些数据。

在另一条路线中,这就像我预期的那样(应用程序控制器在路由之前加载)。唯一的区别是有效的是Ember.ArrayController,而不是Ember.Controller的是beforeModel。我使用Ember.ArrayController虽然没有模型,只是因为如果条件不满足我试图转移它。

如果我将我的非工作控制器更改为model并在路由器中进行无意义的route通话,那么它仍然无效。

我唯一能想到的另一个不同之处是,那个不起作用的是一个由resource定义的嵌套控制器,而且工作的控制器没有嵌套,由{定义{1}}。

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
});

我知道如何确保在我的路线之前加载应用程序路径/控制器吗?

我试图按照以下方式做点什么:

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'));
      }
    });
  },
});

导出默认的SchoolsRoute;

应用程序控制器只是对会话控制器进行别名。

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);
  }
},

没有其他&#34;设置&#34; SessionController的。{它只是通过controllerFor调用。在init上,它调用loadUser方法,该方法立即记录消息&#34; SessionController - loadUser&#34;然后进行store.find调用,当返回时,它会记录一条关于设置用户的消息。

日志显示应用程序首先设置:

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更改为普通对象,然后将其注入应用程序路由,以便我可以将loadUser函数作为beforeModel中的承诺返回。

1 个答案:

答案 0 :(得分:0)

在实例化或连接任何控制器之前调用所有模型挂钩(用于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,我刚刚在写这篇文章时意识到你正在覆盖beforeModel中实现的AuthenticatedRoute钩子,所以当你点击那条路线时它不会触发{{1} }钩子。在其他更改之前尝试此操作,看看它是否修复了您的预期结果。

beforeModel