2013-07-18 32 views
2

我正在嘗試爲Ember制定一個指標工具的前端。我到目前爲止已經編寫的代碼已經被Eviltrout的emberreddit應用Ember:獲取相互依賴的對象的數據

https://github.com/eviltrout/emberreddit

的目標是非常影響而有互相依賴兩類:度量和過濾器。

1)應用程序初始化完成後,將從服務器加載作爲Filter-class實例的過濾器。過濾器加載後,它們將顯示爲屏幕上的複選框。之後,度量對象應該將過濾器作爲參數並向服務器查詢數據。

2)一旦用戶更改複選框並更新過濾器對象,應用程序應該再次將過濾器作爲參數並從服務器獲取新的度量標準數據。

我的問題是,我不知道如何處理這兩個對象與異步ajax調用之間的依賴關係。在當前狀態下,當應用程序已經開始加載度量標準時,它不會完成加載過濾器。因此,篩選器不會作爲度量ajax-call的參數傳遞。

我的問題是:做這個燼的最好方法是什麼?肯定有辦法處理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') 
}); 

回答

1

您的afterModel鉤子可以在一系列相關的承諾中做到這一點。當前的實現將立即返回,而不是連鎖承諾,並最終返回最後一個承諾作爲鉤子的結果。在繼續到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請求的數量。

+0

謝謝Darshan!有效! 你的批處理調用建議是好的,我想我會實現初始頁面加載。但是,我希望保持個別調用也可以允許更改各個元素,而無需一次獲取所有數據。 –

+0

'Promises'真棒。 :) –