javascript 在 Ember.js 中渲染应用程序之前等待模型加载

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18105099/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 10:44:49  来源:igfitidea点击:

Waiting for models to load before rendering app in Ember.js

javascriptember.jsember-data

提问by Justin Stayton

I have a number of different application-level models — i.e., current user, current account, etc. — that I want to load before rendering my application. How and where should this be done? This question/answerhelped a lot, but it doesn't cover the async aspect.

我有许多不同的应用程序级模型——即当前用户、当前帐户等——我想在呈现我的应用程序之前加载它们。这应该如何以及在哪里完成?这个问题/答案有很大帮助,但它不包括异步方面。

The following code accomplishes what I want, but loading the models in beforeModel(to take advantage of it waiting for the promise to resolve) doesn't seem right. Should I even be loading these models in ApplicationRoute?

下面的代码完成了我想要的,但加载模型beforeModel(利用它等待承诺解决)似乎不正确。我什至应该加载这些模型ApplicationRoute吗?

App.ApplicationController = Ember.Controller.extend({
  currentAccount: null
});

App.ApplicationRoute = Ember.Route.extend({
  beforeModel: function () {
    var self = this;

    return App.Account.find(...).then(function (account) {
      self.controllerFor('application').set('currentAccount', account);
    });
  }
});

Thanks for your help!

谢谢你的帮助!

回答by Meori Oransky

The trick is to return a promise from the route's model method.
This will cause the router to transition into App.LoadingRoute route, until the promise resolves (which can be used for loading indication bars/wheels etc.)
When the promise resolves, the App.LoadingRoute will be deactivated, and the original route's setupController method will be called.
This works for ember-data promises, JQuery's $.ajax promises and ember-model's fetch promises.
Just make sure you return the actual model after resolving the promise.
This can also be a good place to handle errors if the promise is rejected - but I'll leave that to some other question.

诀窍是从路由的模型方法返回一个承诺。
这将导致路由器过渡到 App.LoadingRoute 路由,直到 promise 解析(可用于加载指示条/轮子等)
当 promise 解析时,App.LoadingRoute 将被停用,并且原始路由的 setupController 方法将被调用。
这适用于 ember-data 承诺、JQuery 的 $.ajax 承诺和 ember-model 的 fetch 承诺。
只要确保在解决承诺后返回实际模型。
如果承诺被拒绝,这也可以是处理错误的好地方 - 但我会把它留给其他一些问题。

As for where you should load your models - that is dependent on your app's usage.
Usually you would load a model where the URL indicates you need that model - a rule of thumb would be the indication of a model ID in the URL.
This of course changes if you need to prefetch some data.

至于您应该在哪里加载模型 - 这取决于您的应用程序的使用情况。
通常您会加载一个模型,其中 URL 指示您需要该模型 - 经验法则是 URL 中模型 ID 的指示。
如果您需要预取一些数据,这当然会改变。

And now for some code:

现在是一些代码:

App.SomeRoute = Ember.Route.extend({
   model: function(params){
       return App.SomeModel.fetch(params.model_id).then(function(modelData){
           // it is better to return the actual model here, and not the promise itself
           return App.SomeModel.find(params.model_id);
       });

   },
   setupController: function(controller, model){
       controller.set("model", model);
       // do some controller setup here - can be omitted if no setup is needed
       // this will run only after the promise has been resolved.
   }
});

App.LoadingRoute = Ember.Route.extend({
        activate: function(){
            this._super();
            // add some loading indication here
        },
        deactivate: function(){
            this._super();
            // remove loading indication
        }
}

Hope this helps.

希望这可以帮助。

回答by greg.arnott

You want to preload data/models to initialize your application, and feel beforeModel is incorrect?

您想预加载数据/模型来初始化您的应用程序,并且觉得 beforeModel 不正确?

Sounds like you need an application initializer!

听起来您需要一个应用程序初始化程序!

Your friend in this instance:

在这种情况下,您的朋友:

App.deferReadiness();// halt progress of application until all instances of this call (ie: multiple initializers) are matched by an instance the following call:

App.deferReadiness(); // 停止应用程序的进度,直到此调用的所有实例(即:多个初始化程序)与以下调用的实例匹配:

App.advanceReadiness();// consider this to be equivalent to a promise resolve call.

App.advanceReadiness(); // 认为这相当于一个 promise resolve 调用。

1) From you looking up the user directly, modifying where mentioned to suit your app setup:

1)从您直接查找用户,修改提到的位置以适合您的应用程序设置:

Ember.Application.initializer({
    name: 'loadUser',
    after: 'store',
    initialize: function(container, app) {
        // modify this following to suit how you're determining the account
        var url = 'user/' + currentAccount;
        // tell the app to pause loading until advanceReadiness is declared
        app.deferReadiness();

        // load from JSON 
        Ember.$.getJSON('url').then(function(json) {

            var store = container.lookup('store:main');
            store.load(app.User, json);

            // tell app to start progressing again
            app.advanceReadiness();
        });
    }
});

2) Through meta tag:

2)通过元标记:

Ember.Application.initializer({
    name: 'currentUser'
    after: 'store',

    initialize: function(container, app) {
        app.deferReadiness();

        $(function() {
            // Look up an attribute in a meta tag
            var store      = container.lookup('store:main'),
                attributes = $('meta[name="current-user"]').attr('content');

            if (attributes) {
                var obj        = store.load(app.User, JSON.parse(attributes)),
                    user       = App.User.find(obj.id),
                    controller = container.lookup('controller:currentUser').set('content', user);

                container.typeInjection('controller', 'currentUser', 'controller:currentUser');
            }
            app.advanceReadiness();
        });
    }
});

3) Through Session data:

3)通过Session数据:

Ember.Application.initializer({
    name  : 'currentUser',
    after : 'session',
    initialize: function(container, app) {
        var controller = container.lookup('controller:currentUser');
        container.typeInjection('controller', 'currentUser', 'controller:currentUser');
    }
});

回答by Alex

I managed to get this work by using nested Promises and the afterModel method in the ApplicationRoute.

我设法通过在 ApplicationRoute 中使用嵌套的 Promise 和 afterModel 方法来完成这项工作。

App.ApplicationRoute = Ember.Route.extend({

    model: function() {

        // load the reservation (a globally needed model)
        return App.Reservation.fetch().then(function(reservations) {
            return reservations.get('firstObject');
        });

    },

    afterModel: function() {

        // Load all other globally needed models
        var self = this;
        return App.Gender.fetch().then(function(genders) {
            self.controllerFor('application').set('genders', genders);
            return App.FilterAttribute.fetch().then(function(filterAttributes) {
                self.controllerFor('application').set('filterAttributes', filterAttributes);
                //return App.SomeOtherModel...
            });
        });

    },

    setupController: function(controller, model) {
        controller.set('reservation', model);
    }

});

Works just perfectly :-) The application remains in the LoadingRoute until all records are loaded. Note that I am using Ember Model, but this should make no difference, it just have to return a Promise.

完美运行 :-) 应用程序保留在 LoadingRoute 中,直到加载所有记录。请注意,我使用的是Ember Model,但这应该没有区别,它只需要返回一个 Promise。