Вопрос по javascript, database – Работа с несохраняемыми значениями в Backbone

7

Существует ли стандартный способ работы с несохраняемыми значениями в Backbone.

например

<code>MyModel = Backbone.extend(Backbone.Model, {
    initialize: function () {
        this.set({'inches': this.get('mm') / 25});
    }
})
</code>

Если я вызову save () для этой модели, она выдаст ошибку, так как нет соответствующего поля базы данных дляinches, Я могу придумать несколько способов исправить это, но мне интересно, есть ли проверенный и проверенный подход, который лучше всего использовать для этого?

На данный момент мое предпочтительное решение состоит в расширении BackbonetoJSON метод и разрешить передачу логического параметраdontCleanup чтобы позволить ему по-прежнему возвращать все значения модели (включая несохраняемые), когда это необходимо, например, для перехода к шаблону.

Ваш Ответ

3   ответа
3

Model defaults как ваша действительная схема, а затем вернуть только подмножествоthis.attributes это действует во времяtoJSON.

var Model = Backbone.Model.extend({
  defaults: {
    foo: 42,
    bar: "bar"
  },

  toJSON: function () {
    var schemaKeys = _.keys(this.defaults);
    var allowedAttributes = {};
    _.each(this.attributes, function (value, key) {
    if (_.include(schemaKeys, key)) {
      allowedAttributes[key] = value;
    }
    return allowedAttributes;
  }
});

Обратите внимание, что_.pick сделает код немного короче, как только вы подчеркнете 1.3.3. Я не видел "пробовал и проверял" В моих путешествиях по магистральному сообществу есть соглашение, и поскольку в магистрали остается так много открытых вариантов, иногда соглашения не появляются, но мы увидим, что дает этот вопрос stackoverflow.

ПерезаписываяtoJSON Метод, кажется, популярный подход. Однако в этом случае, используя хэш по умолчанию в качестве сохраняемой схемы, вы можете столкнуться с проблемами, если хотите установить значения по умолчанию для непостоянного атрибута. Возможно, отдельный атрибут, какschemaKeys было бы лучше:schemaKeys: [ 'foo', 'bar' ]
Мне нравится это, кроме того, что это означает, что мне нужно поддерживать схему модели в актуальном состоянии с помощью схемы базы данных (что, возможно, не является необоснованным). Я мог бы приспособить это так, чтобы при инициализации, если значения по умолчанию еще не существовали, оно было создано с использованием ключей в объекте, переданном inn в модель. wheresrhys
2

non-persisted attributes в Backbone.js некоторое время занимался моей работой, особенно с тех пор, как я начал использовать ember / ember-data, которая обрабатывает различные ситуации через вычисленные свойства, атрибуты ember-data или контроллеры.

Многие решения предлагают настроитьtoJSON метод. Однако некоторые популярные плагины Backbone (особенно те, которые имеют дело с вложенными моделями), реализуют свои собственныеtoJSON метод и позвонитьBackbone.Model.prototype.toJSON чтобы получить объектное представление атрибутов модели. Таким образом, перезаписавtoJSON метод определения модели, вы потеряете некоторые (потенциально важные) функции этих плагинов.

Лучшее, что я придумал, - это включитьexcludeFromJSON массив ключей в определении модели и перезаписатьtoJSON метод наBackbone.Model.prototype сам:

Backbone.Model.prototype.toJSON = function() {
  var json = _.clone(this.attributes),
      excludeFromJSON = this.excludeFromJSON;
  if(excludeFromJSON) {
    _.each(excludeFromJSON, function(key) {
      delete json[key];
    });
  }
  return json;
};

MyModel = Backbone.Model.extend({
  excludeFromJSON: [
    'inches'
  ]
});

Таким образом, вам нужно будет только определить непостоянные ключи (если вы забудете это сделать, вам скоро напомнят, когда ваш сервер выдает ошибку!).toJSON будет вести себя как обычно, если нетexcludeFromJSON собственность присутствует.

В твоем случае,inches является вычисляемым свойством, полученным изmm, поэтому имеет смысл реализовать это как метод в вашей модели (гарантируя, что значение в дюймах будет правильным при изменении мм):

MyModel = Backbone.Model.extend({
  inches: function() {
    return this.get('mm') / 25;
  }
});

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

var getMixin = {
  get: function(attr) {
    if(typeof this[attr] == 'function') {
      return this[attr]();
    }
    return Backbone.Model.prototype.get.call(this, attr);
  }
};

MyModel = Backbone.Model.extend({
  inches: function() {
    return this.get('mm') / 25;
  }
});
_.extend(MyModel.prototype, getMixin);

Что позволит вам сделать:

new MyModel().get('inches');

Этот подход не затрагивает основнуюattributes хэш, означающий, чтоinches не появится вtoJSON представление,unless выset значениеinches позже, в этом случае вам понадобится что-то вродеexcludeFromJSON массив.

Если у вас есть необходимостьset inches значение, вы также можете прослушать изменения и скорректировать значениеmm:

MyModel = Backbone.Model.extend({

  initialize: function() {
    this.on('change:inches', this.changeInches, this);
  },

  inches: function() {
    return this.get('mm') / 25;
  },

  changeInches: function() {
    this.set('mm', this.attributes.inches * 25);
  }
});
_.extend(MyModel.prototype, getMixin);

Увидетьполный пример на JSBin.

Стоит также отметить, что(официальная?) цельtoJSON method недавно был переопределен как подготовка модели для синхронизации с сервером. По этой причине звонюtoJSON всегда должен возвращать только «постоянный» (или "сохраняемые") атрибуты.

11

но никогда не ставил это на место. Для всех способов, которыми я обработал это, вот мои два фаворита:

Non-"attribute" values View Models Non-Attribute Values

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


myModel.someValue = "some value";

Большая проблема заключается в том, что вы не получаете все события, связанные с вызовомset на модели. Поэтому я склоняюсь к тому, чтобы обернуть это в метод, который делает все для меня. Например, обычный метод, который я использую на моделях:select сказать, что эта модель была выбрана:


MyModel = Backbone.Model.extend({
  select: function(){
    if (!this.selected){
      this.selected = true;
      this.trigger("change:selected", this, this.selected);
    }
  }
});

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

Для этого я склонен использовать модели представления.

View models.

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

Есть очень много способов, которыми вы можете сделать это. Вот что может быть очень простой версией:


MyModel = Backbone.Model.Extend({ ... });

MyViewModel = function(model){
  var viewModel = Object.create(model);

  viewModel.toJSON = function(){
    var json = model.toJSON();
    json.inches = json.mm / 25;
    return json;
  };

  return viewModel;
});

Большое преимущество упаковки сObject.create в том, что теперь у вас есть ситуация наследования прототипа, так что все ваши стандартные функции из модели все еще на месте. Мы просто переопределилиtoJSON метод в модели представления, чтобы он возвращал объект JSON сinches приписывать.

Затем в представлении, которое нуждается в этом, вы бы обернули вашу модель в функцию initialize:


MyView = Backbone.View.extend({
  initialize: function(){
    this.model = MyViewModel(this.model);
  },

render: function(){ var data = this.model.toJSON(); // returns with inches } });

You could call new MyViewModel(this.model) if you want, but that's not going to do anything different, in the end, because we're explicitly returning an object instance from the MyViewModel function.

When your view's render method calls toJSON, you'll get the inches attribute with it.

Of course, there are some potential memory concerns and performance concerns with this implementation, but those can be solved easily with some better code for the view model. This quick and dirty example should get you down the path, though.

Если вы используете модели представления, как вы можете получить доступ к значению в дюймах вне представления? (например, если вам нужно это значение в коллекции)
Обдумав это, я a) привлек к нему внимание, поскольку он довольно гибкий, и я мог бы иметь много вариантов, основанных на исходной модели, в очень несвязном и понятном виде; но б) Так как он использует наследование прототипов, может быть, производительность не так уж и плоха, но он добавляет гораздо больше объектов в мое приложение, что при обработке более 300 записей вызывает беспокойство. Я попытаюсь придумать, как объединить ваши подходы и подход @Peter Lyons. wheresrhys
Расширение модели для соответствия конкретным потребностям вида с помощью ViewModel и при этом сохранение исходной модели без изменений для общего использования - очень элегантный маневр. Ницца!

Похожие вопросы