Ajax 余烬:获取相互依赖的对象的数据

Ajax 余烬:获取相互依赖的对象的数据,ajax,asynchronous,ember.js,Ajax,Asynchronous,Ember.js,我正在尝试使用Ember为度量工具构建前端。到目前为止,我编写的代码在很大程度上受到了Eviltrout的emberreddit应用程序的影响 目标是有两个相互依赖的类:度量和过滤器 1) 一旦应用程序初始化,过滤器(过滤器类的实例)将从服务器加载。加载过滤器后,它们将在屏幕上显示为复选框。之后,度量对象应将过滤器作为参数,并向服务器查询数据 2) 一旦用户更改复选框并因此更新过滤器对象,应用程序应再次将过滤器作为参数,并从服务器获取新的度量数据 我的问题是,我不知道如何使用异步ajax调用处

我正在尝试使用Ember为度量工具构建前端。到目前为止,我编写的代码在很大程度上受到了Eviltrout的emberreddit应用程序的影响

目标是有两个相互依赖的类:度量和过滤器

1) 一旦应用程序初始化,过滤器(过滤器类的实例)将从服务器加载。加载过滤器后,它们将在屏幕上显示为复选框。之后,度量对象应将过滤器作为参数,并向服务器查询数据

2) 一旦用户更改复选框并因此更新过滤器对象,应用程序应再次将过滤器作为参数,并从服务器获取新的度量数据

我的问题是,我不知道如何使用异步ajax调用处理这两组对象之间的依赖关系。在它的当前状态下,我的应用程序在已经开始加载度量的情况下没有完成加载过滤器。因此,过滤器不会作为度量ajax调用的参数传递

我的问题是:做这个余烬最好的方法是什么?必须有一种方法来处理ajax调用的顺序。我的直觉是,手动添加观察者不是一个好办法

以下是我的应用程序的模型:

//FILTER MODELS

var defaultFilters = ['dates', 'devices'];

//set Filter class. The Filter object will be multiplied for each filter.
App.Filter = Ember.Object.extend({

    //capitalize first letter to get title
    filterTitle: function() {
        return this.get('id').charAt(0).toUpperCase() + this.get('id').slice(1);
    }.property('id'),

    //set attribute to see if filter has loaded
    loadedFilter: false,

    //create method to load filter values from server
    loadValues: function() {
        var filter = this;
        return Ember.Deferred.promise(function (p) {  
            if (filter.get('loadedFilter')) { 
                p.resolve(filter.get('values'));
            } else {
                p.resolve($.getJSON("http://127.0.0.1:13373/options/" + filter.get('id')).then(function(response) {
                var values = Ember.A();
                response[filter.get('id')].forEach(function(value) {
                    values.push(value);
                });
                filter.setProperties({values: values, loadedFilter: true});
                return values;
                }))
            }})}
    }  
);



//reopen class to create "all" method which returns all instances of Filter class
App.Filter.reopenClass({
    all: function() {
        if (this._all) {return this._all; }
        var all = Ember.A();
        defaultFilters.forEach(function(id) {
            all.pushObject(App.Filter.create({id: id}));
        });
        this._all = all;
        return all;
}});

//Create a Filters array to store all the filters.
App.Filters = App.Filter.all();

//METRIC MODELS
App.Metric = Ember.Object.extend({

    metricTitle: function() {
        return this.get('id').charAt(0).toUpperCase() + this.get('id').slice(1);
    }.property('id'),

    loadedMetric: false,

    filtersBinding: 'App.Filters',

    loadValues: function() {
    var metric = this;
    var filters = metric.get('filters');
    if (filters.get('loadedFilters'))
    console.log('loading metrics'); 
    return Ember.Deferred.promise(function (p) {  
        if (metric.get('loadedMetric')) { 
            p.resolve(metric.get('values'));

        } else {
            p.resolve(
                console.log('sending ajax'),
                $.ajax({ 
                url: "http://127.0.0.1:13373/" + metric.get('id') + "/",
                data: JSON.stringify(metric.get('filters')),
                }).then(function(response) {
            var values = Ember.A();
            response[metric.get('id')].forEach(function(value) {
                values.push(value);
            });
            metric.setProperties({"values": values, "loadedMetric": true});
            return values;
            }))
        }})}


});

App.Metric.reopenClass({


    findByView: function(searchView) {
        if (this._metrics) {return this._metrics; }
        var metrics = Ember.A();
        defaultMetricsSettings.forEach(function(metric) {
            if (metric.view == searchView)
                metrics.pushObject(App.Metric.create({id: metric.id},{view: metric.view}, {calculation: metric.calculation}, {format: metric.format}, {width: metric.width}));
        });
        this._metrics = metrics;
        return metrics;
    }
});
App.ApplicationRoute = Ember.Route.extend({
    //set application routes model to all filters
    model: function() {
        return App.Filter.all();
    },

    //after filter has loaded, let's load its values
    afterModel: function(model) {
        return model.forEach(function(item) {
            item.loadValues();
        });
    },

    //create a controller called ApplicationController and pass the filter as its model
    setupController: function(controller, filter) {
        controller.set('model', filter);
    }

});

App.DashboardRoute = Ember.Route.extend({
    model: function() {
        return App.Metric.findByView('Dashboard');
    },

    afterModel: function(model) {
        return model.forEach(function(item) {
            item.loadValues();
        });
    },

    setupController: function(controller, metric) {
        controller.set('model', metric);
    }
});
App.ApplicationController = Ember.ArrayController.extend({
    //ApplicationController controls all the filters. Let's create a controller to handle each instance of a filter
    itemController: 'filter'
});

App.FilterController = Ember.ObjectController.extend({
    //this sets the titleId property that is used only for binding html attributes in template. Stupid way to do this.
    titleId: function() {
        return "#" + this.get('filterTitle');}.property('filterTitle')
});
以下是路线:

//FILTER MODELS

var defaultFilters = ['dates', 'devices'];

//set Filter class. The Filter object will be multiplied for each filter.
App.Filter = Ember.Object.extend({

    //capitalize first letter to get title
    filterTitle: function() {
        return this.get('id').charAt(0).toUpperCase() + this.get('id').slice(1);
    }.property('id'),

    //set attribute to see if filter has loaded
    loadedFilter: false,

    //create method to load filter values from server
    loadValues: function() {
        var filter = this;
        return Ember.Deferred.promise(function (p) {  
            if (filter.get('loadedFilter')) { 
                p.resolve(filter.get('values'));
            } else {
                p.resolve($.getJSON("http://127.0.0.1:13373/options/" + filter.get('id')).then(function(response) {
                var values = Ember.A();
                response[filter.get('id')].forEach(function(value) {
                    values.push(value);
                });
                filter.setProperties({values: values, loadedFilter: true});
                return values;
                }))
            }})}
    }  
);



//reopen class to create "all" method which returns all instances of Filter class
App.Filter.reopenClass({
    all: function() {
        if (this._all) {return this._all; }
        var all = Ember.A();
        defaultFilters.forEach(function(id) {
            all.pushObject(App.Filter.create({id: id}));
        });
        this._all = all;
        return all;
}});

//Create a Filters array to store all the filters.
App.Filters = App.Filter.all();

//METRIC MODELS
App.Metric = Ember.Object.extend({

    metricTitle: function() {
        return this.get('id').charAt(0).toUpperCase() + this.get('id').slice(1);
    }.property('id'),

    loadedMetric: false,

    filtersBinding: 'App.Filters',

    loadValues: function() {
    var metric = this;
    var filters = metric.get('filters');
    if (filters.get('loadedFilters'))
    console.log('loading metrics'); 
    return Ember.Deferred.promise(function (p) {  
        if (metric.get('loadedMetric')) { 
            p.resolve(metric.get('values'));

        } else {
            p.resolve(
                console.log('sending ajax'),
                $.ajax({ 
                url: "http://127.0.0.1:13373/" + metric.get('id') + "/",
                data: JSON.stringify(metric.get('filters')),
                }).then(function(response) {
            var values = Ember.A();
            response[metric.get('id')].forEach(function(value) {
                values.push(value);
            });
            metric.setProperties({"values": values, "loadedMetric": true});
            return values;
            }))
        }})}


});

App.Metric.reopenClass({


    findByView: function(searchView) {
        if (this._metrics) {return this._metrics; }
        var metrics = Ember.A();
        defaultMetricsSettings.forEach(function(metric) {
            if (metric.view == searchView)
                metrics.pushObject(App.Metric.create({id: metric.id},{view: metric.view}, {calculation: metric.calculation}, {format: metric.format}, {width: metric.width}));
        });
        this._metrics = metrics;
        return metrics;
    }
});
App.ApplicationRoute = Ember.Route.extend({
    //set application routes model to all filters
    model: function() {
        return App.Filter.all();
    },

    //after filter has loaded, let's load its values
    afterModel: function(model) {
        return model.forEach(function(item) {
            item.loadValues();
        });
    },

    //create a controller called ApplicationController and pass the filter as its model
    setupController: function(controller, filter) {
        controller.set('model', filter);
    }

});

App.DashboardRoute = Ember.Route.extend({
    model: function() {
        return App.Metric.findByView('Dashboard');
    },

    afterModel: function(model) {
        return model.forEach(function(item) {
            item.loadValues();
        });
    },

    setupController: function(controller, metric) {
        controller.set('model', metric);
    }
});
App.ApplicationController = Ember.ArrayController.extend({
    //ApplicationController controls all the filters. Let's create a controller to handle each instance of a filter
    itemController: 'filter'
});

App.FilterController = Ember.ObjectController.extend({
    //this sets the titleId property that is used only for binding html attributes in template. Stupid way to do this.
    titleId: function() {
        return "#" + this.get('filterTitle');}.property('filterTitle')
});
控制器:

//FILTER MODELS

var defaultFilters = ['dates', 'devices'];

//set Filter class. The Filter object will be multiplied for each filter.
App.Filter = Ember.Object.extend({

    //capitalize first letter to get title
    filterTitle: function() {
        return this.get('id').charAt(0).toUpperCase() + this.get('id').slice(1);
    }.property('id'),

    //set attribute to see if filter has loaded
    loadedFilter: false,

    //create method to load filter values from server
    loadValues: function() {
        var filter = this;
        return Ember.Deferred.promise(function (p) {  
            if (filter.get('loadedFilter')) { 
                p.resolve(filter.get('values'));
            } else {
                p.resolve($.getJSON("http://127.0.0.1:13373/options/" + filter.get('id')).then(function(response) {
                var values = Ember.A();
                response[filter.get('id')].forEach(function(value) {
                    values.push(value);
                });
                filter.setProperties({values: values, loadedFilter: true});
                return values;
                }))
            }})}
    }  
);



//reopen class to create "all" method which returns all instances of Filter class
App.Filter.reopenClass({
    all: function() {
        if (this._all) {return this._all; }
        var all = Ember.A();
        defaultFilters.forEach(function(id) {
            all.pushObject(App.Filter.create({id: id}));
        });
        this._all = all;
        return all;
}});

//Create a Filters array to store all the filters.
App.Filters = App.Filter.all();

//METRIC MODELS
App.Metric = Ember.Object.extend({

    metricTitle: function() {
        return this.get('id').charAt(0).toUpperCase() + this.get('id').slice(1);
    }.property('id'),

    loadedMetric: false,

    filtersBinding: 'App.Filters',

    loadValues: function() {
    var metric = this;
    var filters = metric.get('filters');
    if (filters.get('loadedFilters'))
    console.log('loading metrics'); 
    return Ember.Deferred.promise(function (p) {  
        if (metric.get('loadedMetric')) { 
            p.resolve(metric.get('values'));

        } else {
            p.resolve(
                console.log('sending ajax'),
                $.ajax({ 
                url: "http://127.0.0.1:13373/" + metric.get('id') + "/",
                data: JSON.stringify(metric.get('filters')),
                }).then(function(response) {
            var values = Ember.A();
            response[metric.get('id')].forEach(function(value) {
                values.push(value);
            });
            metric.setProperties({"values": values, "loadedMetric": true});
            return values;
            }))
        }})}


});

App.Metric.reopenClass({


    findByView: function(searchView) {
        if (this._metrics) {return this._metrics; }
        var metrics = Ember.A();
        defaultMetricsSettings.forEach(function(metric) {
            if (metric.view == searchView)
                metrics.pushObject(App.Metric.create({id: metric.id},{view: metric.view}, {calculation: metric.calculation}, {format: metric.format}, {width: metric.width}));
        });
        this._metrics = metrics;
        return metrics;
    }
});
App.ApplicationRoute = Ember.Route.extend({
    //set application routes model to all filters
    model: function() {
        return App.Filter.all();
    },

    //after filter has loaded, let's load its values
    afterModel: function(model) {
        return model.forEach(function(item) {
            item.loadValues();
        });
    },

    //create a controller called ApplicationController and pass the filter as its model
    setupController: function(controller, filter) {
        controller.set('model', filter);
    }

});

App.DashboardRoute = Ember.Route.extend({
    model: function() {
        return App.Metric.findByView('Dashboard');
    },

    afterModel: function(model) {
        return model.forEach(function(item) {
            item.loadValues();
        });
    },

    setupController: function(controller, metric) {
        controller.set('model', metric);
    }
});
App.ApplicationController = Ember.ArrayController.extend({
    //ApplicationController controls all the filters. Let's create a controller to handle each instance of a filter
    itemController: 'filter'
});

App.FilterController = Ember.ObjectController.extend({
    //this sets the titleId property that is used only for binding html attributes in template. Stupid way to do this.
    titleId: function() {
        return "#" + this.get('filterTitle');}.property('filterTitle')
});

您的
afterModel
hook可以通过一系列依赖的承诺来实现这一点。当前实现将立即返回,而您将链接承诺并最终返回最后一个承诺作为钩子的结果。路由器将等待整套呼叫完成,然后继续
setupController

afterModel: function(model) {
  var promise;
  model.forEach(function(item)) {
    if (promise) {
      promise = promise.then(function() {
        item.loadValues();
      });
    } else {
      promise = item.loadValues();
    }
  }

  return promise;
}

我不确定您有多少个电话,但您可能希望将其中一些电话批处理在一起,以减少HTTP请求的数量。

谢谢Darshan!成功了!您关于批处理调用的建议很好,我想我会在初始页面加载时实现它。但是,我想保持单独调用的可能性,以允许在不必一次获取所有数据的情况下更改单个元素。
承诺
非常棒。:)