当前位置: 首页 > 工具软件 > ember-loading > 使用案例 >

#7 Loading/Error 子状态

丰智
2023-12-01

英文原版:https://guides.emberjs.com/v2.13.0/routing/loading-and-error-substates/

Ember Router允许你提供一个读取数据时的反馈–loading状态,同样也提供了error状态。

loading 子状态

在beforeModel,model和afterModel钩子函数的执行过程中,数据同时也是需要通过消耗一些时间来读取的。典型的,在每个钩子中的promise对象返回前,router都会暂停当前的transition。

考虑下面的情况:

app/router.js

Router.map(function() {
  this.route('slow-model');
});
app/routes/slow-model.js

import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return this.get('store').findAll('slow-model');
  }
});

如果你导航到slow-model路由,那么在model钩子中,查询会需要一段时间才能完成。在这段时间期间,你的用户界面不会有任何的反馈来说明当前正在发生什么事情。如果你从另一个路由导航到slow-model路由,那么在model数据读取完成之前,你将会继续看到前一个路由的模板,并且当slow-model路由的数据读取完成之后,slow-model的模板会猛然的出现。

那么,在transition期间,我们应该怎么来提供一些什么反馈才能不会这么尴尬呢?

只需要定义一个模板,名叫 loading(当然,可以选择性的生成一个响应的路由)。loading子状态的子transition是同步加载,并且URL不会变化,不同于其他的transitions,当前的主transition不会被终止。

一旦主slow-model的transition完成,那么loading路由就会自动退出并且slow-model中的transition会继续进行。

对于嵌套路由,如下:

app/router.js

Router.map(function() {
  this.route('foo', function() {
    this.route('bar', function() {
      this.route('slow-model');
    });
  });
});

当访问foo.bar.slow-model路由时,Ember会从foo.bar.slow-model-loading层级开始尝试查找与相应路由名字相匹配的routeName-loading模板或者loading模板:

  1. foo.bar.slow-model-loading
  2. foo.bar.loading 或 foo.bar-loading
  3. foo.loading 或 foo-loading
  4. loading 或 application-loading

需要注意一下slow-model路由,Ember不会去试着找以slow-model.loading命名的模板,但是在其他层级这种语法是有效的。这在对于在叶子路由创建自定义loading是比较有帮助的。

当访问foo.bar路由时,Ember会寻找:

  1. foo.bar-loading
  2. foo.loading 或 foo-loading
  3. loading 或 application-loading

注意哟,foo.bar.loading现在不被考虑在内哦!

loading事件

如果beforeModel/model/afterModel没有立即返回resolve,那么这时一个loading事件将会在当前路由触发:

app/routes/foo-slow-model.js

import Ember from 'ember';

export default Ember.Route.extend({
  model() {
    return this.get('store').findAll('slow-model');
  },
  actions: {
    loading(transition, originRoute) {
      let controller = this.controllerFor('foo');
      controller.set('currentlyLoading', true);
      return true; // allows the loading template to be shown
    }
  }
});

如果在路由中没有定义loading函数,那么这个事件会冒泡到父路由,这就给application路由处理它的机会。

当使用 loading函数时,我们可以利用transition的promise属性来判断loading事件是否结束:

app/routes/foo-slow-model.js

import Ember from 'ember';

export default Ember.Route.extend({
  ...
  actions: {
    loading(transition, originRoute) {
      let controller = this.controllerFor('foo');
      controller.set('currentlyLoading', true);
      transition.promise.finally(function() {
          controller.set('currentlyLoading', false);
      });
    }
  }
});

error子状态

Ember提供了一个类似的方法在loading子状态出现错误的时候上场。

与默认的loading事件处理函数的实现相类似,默认的error处理函数会在找一个适当的error状态时进行切入。

app/router.js

Router.map(function() {
  this.route('articles', function() {
    this.route('overview');
  });
});

伴随着loading子状态,如果从articles.overview路由的model钩子(beforeModel或afterModel)中抛出一个错误或者返回一个rejected promise,那么Ember会通过一下流程寻找error模板或者路由:

  1. articles.overview-error
  2. articles.error 或 articles-error
  3. error 或 application-error

如果上面的任何一个被找到,那么router会立即transition进入到这个子状态(URL不会变化)。错误的”原因”会被作为参数传入error路由的model钩子。

除非在error子状态中调用setupController()方法,否则model(0钩子不会被调用。看下面的例子:

setupController: function(controller, error) {
  Ember.Logger.debug(error.message);
  this._super(...arguments);
}

如果没有可用的error子状态,那么error信息会被记录。

error事件

如果articles.overview路由的model()钩子返回了一个被rejected的promise,那么error事件将会被触发并且向上冒泡。可以利用这个事件来显示错误信息和重定向到登录页:

app/routes/articles-overview.js

import Ember from 'ember';

export default Ember.Route.extend({
  model(params) {
    return this.get('store').findAll('privileged-model');
  },
  actions: {
    error(error, transition) {
      if (error.status === '403') {
        this.replaceWith('login');
      } else {
        // Let the route above this handle the error.
        return true;
      }
    }
  }
});

类似于loading事件,你可以在application中来处理error事件。这样会避免你在多个路由中编写重复代码。

本节完

 类似资料: