Управление состоянием Backbone.js / инициализация представления на основе фрагмента URL - PullRequest
25 голосов
/ 25 мая 2011

Я пытаюсь отслеживать состояние в этом приложении, используя Backbone.js:

enter image description here

У меня есть "ChartAppModel" с набором значений по умолчанию:

ChartAppModel = Backbone.Model.extend({

defaults: { 
    countries : [], 
    selectedCountries : [],
    year : 1970,
},

initialize: function() { 
    loadAbunchOfData();
    checkStartState();
}

});

Если задан начальный фрагмент, это состояние по умолчанию должно быть перезаписано:

var startState = $.deparam.fragment(); //using Ben Alman's BBQ plugin
this.set({selectedCountries: startState.s, year: startState.y});

Теперь, например, SidebarView готов к обновлению:

ChartAppViewSidebar = Backbone.View.extend({

initialize: function(){
      this.model.bind("change:selectedCountries", this.render);
},

render : function(){
      [... render new sidebar with check boxes ...]
},

Проблема в том, что у меня также есть обработчик событий на боковой панели, который обновляет модель:

events: {
"change input[name=country]": "menuChangeHandler",
},

menuChangeHandler : function(){
      [... set selectedCountries on model ...]
},

Так что будет цикл обратной связи ... А потом, я также хотел бы способ выдвинуть новыйсостояние - так что я слушаю изменения модели:

ChartAppModel = Backbone.Model.extend({

initialize: function() { 
    this.bind("change", this.setState);
}

});

... и относительно скоро этот менеджер состояний рухнет ...

Вопросы:

1) Как мне инициировать мои представления (например, «какие флажки должны быть отмечены») на основе фрагмента?(приветствуются любые советы о лучших методах для состояния / начального состояния, которые не являются типичным "маршрутом")

2) Как можно избежать моих представлений, устанавливающих атрибут для модели, которую они сами слушают?

3) Как я могу выдвинуть новое состояние на основе части модели?

Бонус:)

4) Как бы вы выложили коддля описанного приложения?

Спасибо!

Ответы [ 2 ]

15 голосов
/ 25 мая 2011

Это один четко определенный вопрос!

Возникает вопрос, что такое модель. Я полагаю, что существует определенное определение того, что представляет собой модель в магистральном мире, и я не уверен, что ваша стратегия соответствует этому определению. Также вы сохраняете состояние как в URL, так и в модели. Вы можете просто сохранить состояние в URL, как я объясню.

Если бы я делал это, было бы 2 просмотра. Один для элементов управления вашего приложения и вложенный в него для вашего графика: GraphView и AppView. Модель будет данными, которые вы собираетесь построить, а не состоянием интерфейса.

Использование контроллера для запуска представления приложения, а также для обработки любого состояния интерфейса, определенного в URL.

Есть вопрос о рычагах состояния в Backbone. Традиционные веб-приложения использовали ссылку / URL в качестве основного рычага состояния, но все, что сейчас меняется. Вот одна из возможных стратегий:

Checkbox Change Event -> Update location fragment -> trigger route in controller -> update the view
Slider Change Event -> Update location fragment -> trigger route in controller -> update the view

Самое замечательное в такой стратегии состоит в том, что она заботится о случае, когда URL-адреса передаются или добавляются в закладки

Url specified in address bar -> trigger route in controller -> update the view

Я возьму удар по примеру с псевдокодом. Для этого я сделаю некоторые предположения относительно данных: Данные представляют собой популяцию собак с течением времени (с детализацией по году), где ползунок должен иметь нижнюю и верхнюю границу, а объем данных слишком велик, чтобы загрузить все это клиенту сразу.

Сначала давайте посмотрим на Модель для представления статистических данных. Для каждой точки на графике нам нужно что-то вроде {население: 27000, год: 2003} Давайте представим это как

DogStatModel extends Backbone.Model ->

и набор этих данных будет

DogStatCollection extends Backbone.Collection ->
    model: DogStatModel
    query: null // query sent to server to limit results
    url: function() {
        return "/dogStats?"+this.query
    }

Теперь давайте посмотрим на контроллер. В этой стратегии, которую я предлагаю, контроллер соответствует своему названию.

AppController extends Backbone.Controller ->
    dogStatCollection: null,
    appView: null,

    routes: {
         "/:query" : "showChart"
    },

    chart: function(query){
        // 2dani, you described a nice way in your question
        // but will be more complicated if selections are not mutually exclusive
        // countries you could have as countries=sweden;france&fullscreen=true
        queryMap = parse(query) //  
        if (!this.dogStatCollection) dogStatCollection = new DogStatCollection
        dogStatCollection.query = queryMap.serverQuery
        if (!this.appView) {
           appView = new AppView()
           appView.collection = dogStatCollection
        }
        appView.fullScreen = queryMap.fullScreen
        dogStatCollection.fetch(success:function(){
          appView.render()
        })            
    }
2 голосов
/ 25 мая 2011

2) Как я могу избежать того, чтобы мои представления устанавливали атрибут для модели, которую они сами слушают?

Вы этого не делаете.Ваша модель должна выполнять проверку любых атрибутов, которые вы пытаетесь обновить, поэтому вашему представлению требуется для прослушивания в случае сбоя установки атрибута или проверки значения.

Что делает ваше представлениеесли пытается установить значение для модели, то модель либо устанавливает его, либо изменяет данные и устанавливает его, либо отклоняет его.Ваше представление должно быть соответствующим образом обновлено.

3) Как я могу выдвинуть новое состояние на основе части модели?

// for each attribute
_.each(["attribute1", "attribute2", "attribute3", ...], _.bind(function(val) {
    // bind the change
    // bind the setState with `this` and the `value` as the first parameter
    this.bind("change:" + value, _.bind(this.setState, this, value));
}, this));
...