Backbone: Adicione um método "reset" ao Backbone.Model

Criado em 31 jul. 2014  ·  22Comentários  ·  Fonte: jashkenas/backbone

Eu precisava atualizar alguns dos dados de um modelo que vieram do servidor. Atualmente, existem duas opções: chamar Model.set ou definir Model.attributes diretamente. Eu não queria que as alterações fossem gravadas, mas também não poderia usar silent porque precisava que as respectivas visualizações fossem atualizadas. Então, eu escrevi um patch de macaco:

Backbone.Model.prototype.reset = (attributes, options) ->
    attrs = attributes || {};
    if options.parse
      attrs = this.parse(attrs, options) || {}

    @set(attrs, options);
    <strong i="9">@changed</strong> = {};

Você se perguntou por que Backbone.Model não tem um método de redefinição como Backbone.Collection ?

question

Comentários muito úteis

@thesmart , Model # set não

Deve ser obrigatório fornecer um motivo ao encerrar uma questão. Gostaria de saber por que @ akre54 fechou isso. Os modelos de backbone são deliberadamente primitivos e sem opinião; no entanto, a falta de Model # reset expressa uma opinião sobre como os modelos são usados. De http://backbonejs.org/#Getting -started

Filosoficamente, o Backbone é uma tentativa de descobrir o conjunto mínimo de primitivas de estruturação de dados (modelos e coleções) e interface de usuário (visualizações e URLs) que geralmente são úteis ao construir aplicativos da web com JavaScript.

Todos 22 comentários

Reiniciar é uma saída de emergência, que permite que você faça uma renderização eficiente em massa facilmente quando você sabe que precisa.

Para o seu caso, use apenas set .

Você pode definir sem fazer alterações de atributo sujas?

Em 1º de agosto de 2014, às 7h28, Jeremy Ashkenas [email protected] escreveu:

Reiniciar é uma saída de emergência, que permite que você faça uma renderização eficiente em massa facilmente quando você sabe que precisa.

Para o seu caso, basta usar set.

-
Responda a este e-mail diretamente ou visualize-o no GitHub.

Chamar Model.clear() antes de Model.set({}) funcionará no seu caso? Dessa forma, você não estenderá os atributos atuais, em vez disso, eles serão substituídos. Model.clear() também suporta a opção silenciosa se você não quiser acionar dois eventos de "mudança" no modelo.

Eu concordo que esse método está faltando. Também pensei em usar model.clear() e model.set() em conjunto. Então me deparei com o problema, que eu acionar o evento change duas vezes agora.
Usar a opção silent ao chamar model.clear() não é uma opção, porque eu também quero que um evento change disparado, quando uma propriedade não for definida.

Um método model.reset() pegaria um novo hash de atributos e preencheria este hash com undefined valores para chaves de atributos antigas que não estivessem presentes no novo hash de atributo.

Model.prototype.reset = function(attrs, options) {
    for (var key in this.attributes) {
        if (key in attrs) continue;
        attrs[key] = void 0;
    }

    return this.set(attrs, options);
};

@lennerd

A respeito:

Model.prototype.reset = function(attrs, options) {
    for (var key in this.attributes) {
        this.unset(key, {silent:true});
    }
    return this.set(attrs, options);
};

Não, isso não ajuda. Imagine que você tem uma chave foo no hash de atributos atual do modelo, que não está presente no novo hash attrs você passou para model.reset() . Quando eu escuto o evento change:foo , ele não será disparado com o novo valor undefined , porque usamos model.unset() no modo silencioso.

Backbone.Model.prototype.reset = function(attrs, options) {
    for (var key in this.attributes) {
        this.unset(key, {silent:true});
    }
    return this.set(attrs, options);
};

var bar = new Backbone.Model();

bar.on('change', function(model) {
    console.log('The model bar has been changed.');
});

bar.on('change:foo', function(model, foo) {
    console.log('Foo has been changed to: ' + foo);
});

bar.set(foo, 'test');
// => The model bar has been changed.
// => Foo has been changed to: test

bar.reset({ foo2: 'test2' });
// => The model bar has been changed.
// Foo was resetted but no change event has been triggered.

http://jsfiddle.net/lennerd/3s1Ltwgu/

Legal, entendi o que você quis dizer. Eu provavelmente optaria por usar this.unset(key, options) vez de substituir this.attributes explicitamente, mas isso é apenas uma questão de substituir attrs[key] = void 0; : panda_face:

Perdoe qualquer ignorância que eu mostre, pois ainda sou relativamente novo no backbone.js, mas o comportamento que está sendo discutido parece ser Model.fetch . A descrição diz:

Reinicia o estado do modelo do servidor

Parece que change eventos ainda são disparados, mas não tornam o modelo "sujo". Existe um motivo pelo qual essa abordagem não pode ser adotada quando você está procurando redefinir atributos com base em uma resposta do servidor? Eu imagino que a única situação em que isso possa ocorrer é quando um modelo muda como efeito colateral de outra operação, mas geralmente esses efeitos colaterais são considerados programação ruim e devem ser evitados, se possível. Se o efeito colateral não puder ser evitado, talvez faça mais sentido enviar um sinalizador de resposta "atualizar modelo XYZ" em vez dos novos atributos do modelo e acionar um fetch sempre que vir tal resposta.

Novamente, perdoe qualquer ignorância que estou exibindo neste comentário.

@kolorahl ,

O que o OP deseja alcançar é limpar os atributos do modelo atual e passar um novo JSON que se torna os novos atributos do modelo. Dessa forma, não queremos atingir o back-end se já tivermos o JSON.

De alguma forma, concordo com @ lupugabriel1 com seu método clear + set. Mas acho que essa é uma funcionalidade que precisa ser considerada. Algo como Backbone.Collection # reset

A razão de eu precisar disso é porque o mundo está mudando. O backbone assume que Model # fetch () XHR é o método primário para carregar dados do servidor, mas estamos fazendo muito mais coisas usando websockets. Quando os dados são enviados ao cliente, é redundante chamar .fetch e precisamos de uma maneira decente de carregar os dados paralelamente e ainda obter um gancho de evento para disparar.

Por que você não está usando #set ?

@jridgewell porque #set tornará os atributos sujos. Vamos tentar usar set e ver o que acontece:

function callback(data_from_server) {
  console.info(data_from_server);
  m = new Backbone.Model(data_from_server);
  m.set('foo', 'what?', {silent: true});
  console.info(m.changedAttributes())
}

Resultado real:

{foo: 'bar'}
{foo: "what?"}

Resultado desejado:

{foo: 'bar'}
false

Definir é adequado para quando o estado mudou fora de sincronia com o servidor, mas Model # reset é necessário porque não há como marcar o estado como sincronizado com o servidor.

Acabei escrevendo um patch de macaco diferente para este recurso:

/**
 * Allow for side-loading data from the server, calling the sync event afterwards.
 * <strong i="6">@param</strong> attributes
 * <strong i="7">@param</strong> options
 */
Backbone.Model.prototype.sync_set = function(attributes, options) {
  var attrs = attributes || {};
  if (options.parse) {
    attrs = this.parse(attrs, options) || {}
  }

  this.set(attrs, options);
  this.changed = {};
  this.trigger('sync', this, attributes, options);
  return this;
}
function callback(data_from_server) {
  console.info(data_from_server);
  m = new Backbone.Model(data_from_server);
  m.set('foo', 'what?', {silent: true});
  console.info(m.changedAttributes())
}
{foo: 'bar'}
false

Provavelmente, também será necessário remover os itens ausentes. Não tenho certeza se Model # set (atributos) faz isso.

Como @lennerd apontou, chamar clear() seguido por set() não é uma boa opção, porque

1) Dispara dois eventos change e
2) Se você usar silent:true na chamada clear , você não obterá eventos de mudança nos atributos que não foram configurados.

collection.reset() é muito intuitivo e acho que Model poderia realmente se beneficiar de um método equivalente. Eu me pego tentando usar model.reset(attrs) o tempo todo, sempre me decepcionando quando ele não está lá. :(

Eu fui em frente e criei uma pequena extensão para adicionar um método reset ao Backbone.Model: Backbone-Model-Reset

Concordo - seria útil ter nativamente. Eu precisava apenas redefinir os atributos sem mutilar o atributo 'id', como clear () estava fazendo. Aqui está a essência disso .

@thesmart , Model # set não

Deve ser obrigatório fornecer um motivo ao encerrar uma questão. Gostaria de saber por que @ akre54 fechou isso. Os modelos de backbone são deliberadamente primitivos e sem opinião; no entanto, a falta de Model # reset expressa uma opinião sobre como os modelos são usados. De http://backbonejs.org/#Getting -started

Filosoficamente, o Backbone é uma tentativa de descobrir o conjunto mínimo de primitivas de estruturação de dados (modelos e coleções) e interface de usuário (visualizações e URLs) que geralmente são úteis ao construir aplicativos da web com JavaScript.

Pelo que eu posso dizer, o modelo de dados do Backbone é incompatível com REST
porque não há maneira de modelar com segurança o estado do servidor no cliente após um
modelo foi construído. Construir um novo modelo é a única maneira de obter
um novo estado sem reset.

Na terça-feira, 5 de julho de 2016, pgifford [email protected] escreveu:

@thesmart https://github.com/thesmart , Model # set não desativa ausente
atributos que é porque eu acho que Model # reset é necessário.

Deve ser obrigatório fornecer um motivo ao encerrar uma questão. Eu iria
gostaria de saber por que @ akre54 https://github.com/akre54 fechou isso.
Modelos de backbone são deliberadamente primitivos e sem opinião, no entanto, o
a falta de Model # reset expressa uma opinião sobre como os modelos são usados. De
http://backbonejs.org/#Getting -started

Filosoficamente, o Backbone é uma tentativa de descobrir o conjunto mínimo de
estruturação de dados (modelos e coleções) e interface do usuário (visualizações e
URLs) primitivos que geralmente são úteis ao criar aplicativos da web
com JavaScript.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/jashkenas/backbone/issues/3253#issuecomment -230586424,
ou silenciar o tópico
https://github.com/notifications/unsubscribe/AARJLQ8-BGeV0X_owHVpyPQAdeiMweNMks5qSriDgaJpZM4CTBHH
.

Eu precisava de um modelo reset e fui em frente e o fiz no _spirit_ da Backbone. Eu uso isso na minha extensão pessoal do Backbone.

É melhor do que um simples .clear seguido por um .set porque ele mescla os padrões de volta no modelo, permitindo que quaisquer atributos passados ​​os substituam como na inicialização.

/**
 * Clears the model's attributes and sets the default attributes.
 * <strong i="10">@param</strong> {Object} attributes to overwrite defaults
 * <strong i="11">@param</strong> {Object} options  to pass with the "set" call.
 * <strong i="12">@return</strong> {Backbone.Model}  this object, to chain function calls.
 */
reset: function(attributes, options) {
    options = _.extend({ reset: true }, options);

    // ensure default params
    var defaults = _.result(this, 'defaults'),
        attrs = _.defaults(_.extend({}, defaults, attributes || {}), defaults);

    // apply
    this._reset(attrs, options);

    // triggers a custom event, namespaced to model in order
    // to avoid collision with collection's native reset event
    // when listening to a collection.
    if (!options.silent) this.trigger('model:reset', this, options);

    return this;
},

/**
 * Private method to help wrap reset with a custom behavior in child
 * classes.
 * <strong i="13">@param</strong>  {Object} attributes to overwrite defaults
 * <strong i="14">@param</strong>  {Object} options  to pass with the "set" call.
 */
_reset: function(attrs, options) {
    this.clear({ silent: true }).set(attrs, options);
},

Ele dispara eventos de mudança ( change e change:attribute ) além do evento model:reset , se não silent: true . Ele poderia ser facilmente personalizado para acionar apenas o evento model:reset , mas acho que os eventos de mudança sempre devem ser acionados ao redefinir um modelo.

Eu ajustei a solução do lennerd https://github.com/jashkenas/backbone/issues/3253#issuecomment -58789524 um pouco.

Backbone.Model.prototype.reset = function(attrs, options) {
    var missing = {};
    for (var key in this.attributes) {
        if (key in attrs) continue;
        attrs[key] = undefined;
        missing[key] = true;
    }
    // trigger all change events at the same time
    this.set(attrs, options);
    // remove missing attributes completely
    for (var key in missing) {
        // no silent option here in case  attributes changed again meanwhile (edge case)
        this.unset(key)
    }
    return this;
};
Esta página foi útil?
0 / 5 - 0 avaliações