Backbone: Quelle est la meilleure façon de modéliser une collection à l'intérieur d'un modèle ?

Créé le 4 nov. 2010  ·  11Commentaires  ·  Source: jashkenas/backbone

Mon équipe travaille avec un schéma de données intéressant. Fondamentalement, nous avons un document qui a un nom et une version et un tableau d'éléments qui lui appartiennent. Les éléments eux-mêmes n'ont pas d'identifiants associés car ils appartiennent au modèle plus large, mais les éléments peuvent être ajoutés/modifiés/supprimés du document principal. Le modèle ressemble à ceci :
{
nom : "Test",
version 1,
éléments : [
{nom : "Article 1",
poste : 0},
{nom : "Article 2",
poste : 1}]
}

Ce serait formidable d'utiliser une collection pour les éléments sous-jacents du modèle, mais chaque fois que la collection est mise à jour, le modèle doit être publié sur le serveur avec son URL. Quelle est la meilleure façon de modéliser cela dans Backbone ? Je serai heureux de poster plus dans un résumé si plus d'informations sont nécessaires.

question

Commentaire le plus utile

@eranation : non, items hors de mes attributs de hachage afin de ne pas avoir à les synchroniser. Cela nécessite que vous extrayiez l'objet items de la réponse pour l'analyse et que vous le rajoutiez pour la sérialisation (voir ci-dessous). En dehors de cela, vous voudrez peut-être mettre la logique que @rsim a mise dans le initialize dans votre méthode constructor place, et utiliser on au lieu de bind (qui est semi-obsolète).

Je trouve qu'il est beaucoup plus facile d'avoir des attributs qui ne sont qu'un hachage superficiel (pas de collections imbriquées, de modèles, etc.), dans la mesure du possible.

var Document = Backbone.Model.extend({
  constructor: function() {
    this.items = new ItemSet(null, {document: this});
    this.items.on('change', this.save, this);
    Backbone.Model.apply(this, arguments);
  },
  parse: function(resp) {
    this.items.set(resp.items, {parse: true, remove: false});
    delete resp.items;
    return resp;
  },
  toJSON: function() {
    var attrs = _.clone(this.attributes);
    attrs.items = this.items.toJSON();
    return attrs;
  }
});
var ItemSet = Backbone.Collection.extend({
  model: Item,
  initialize: function(models, options) {
    this.document = options.document;
  }
});
var Item = Backbone.Model.extend({
  // access document with this.collection.document
});
var document1 = new Document({
  name: "Test",
  version: 1,
  items: [
    {name : "Item 1", position : 0},
    {name : "Item 2", position : 1}
  ]
});

Cela semble bien fonctionner même pour les schémas profondément imbriqués. (voir la doc )

Tous les 11 commentaires

Je dirais que vous avez deux options principales... La première est de laisser les éléments en tant qu'attribut vanille. Backbone utilise une vérification d'égalité approfondie lorsque les attributs changent, donc si un élément interne est mis à jour, le document le saura.

L'autre option consiste à extraire les éléments du document et à les attacher en tant que modèles à part entière, à l'intérieur d'une collection collée sur le document (nous faisons quelque chose dans ce sens chez DocumentCloud). Par exemple (en gros) :

var Document = Backbone.Model.extend({
  initialize: function() {
    this.items = new ItemSet();
    this.items.bind('change', this.save);
  }
});

Est-ce que l'un de ceux-ci fonctionne bien pour vous?

Je recommanderais également d'ajouter la propriété "document" à l'objet Document auquel on pourrait accéder en cas de besoin à partir d'éléments :

var Document = Backbone.Model.extend({
  initialize: function() {
    this.items = new ItemSet(this.get('items'), {document: this});
    this.items.bind('change', this.save);
  }
});
var ItemSet = Backbone.Collection.extend({
  initialize: function(models, options) {
    this.document = options.document;
  }
});
var Item = Backbone.Model.extend({
  // access document with this.collection.document
});
var document1 = new Document({
  name: "Test",
  version: 1,
  items: [
    {name : "Item 1", position : 0},
    {name : "Item 2", position : 1}
  ]
});

Je pense que la liaison de l'événement de modification des éléments à la sauvegarde du document est la pièce manquante. Cependant, l'ajout du document à l'ItemSet semble également très utile. Nous allons essayer cela aujourd'hui et je vous ferai savoir comment cela se passe.

Cela a bien fonctionné. Le plus gros problème que nous avons maintenant est avec mongoid. Merci pour l'aide les gars.

Comment dois-je procéder pour enregistrer tous les éléments à la fois ? Je ne veux pas faire de demande à chaque fois qu'un élément est modifié mais je veux tous les enregistrer avec une seule demande sur l'action de l'utilisateur. Je pensais à tout collecter lorsque le document est enregistré et à mettre à jour l'attr « éléments » du document. Est-ce une bonne solution ?

Tant que vos éléments sont définis dans le document, lorsque vous effectuez un document.save(), les éléments seront également envoyés au serveur.

Mais disons que si j'ajoute un élément à la collection document1.items au moment de l'exécution, il ne sera pas ajouté automatiquement à l'attribut 'items' de document1. Donc, si je fais ensuite un document1.save(), le nouveau modèle que j'ai ajouté à la collection ne sera pas envoyé au serveur. Je ne vois pas comment les modifications apportées à la collection pourraient se propager dans les attributs du modèle envoyés avec save.

Alors, voici comment nous le gérons : le document a un tableau d'éléments par défaut. Lors de l'initialisation, dans une méthode set surchargée, je crée une nouvelle collection d'éléments à partir des attributs et la définis sur le document.

class Document extends Backbone.Model
  defaults:
    items: []

  set: (attrs, options) ->
    items = attrs['items']
    if _( items ).isArray()
      if _( items ).isEmpty()
        attrs['items'] = new DocumentItemsCollection
        newItem = new Item
        attrs['items'].add(newItem, { silent: true })
      else
        attrs['items'] = new DocumentItemsCollection items

À ce stade, vous traitez simplement les méthodes de collecte d'éléments avec 'get', 'set', 'add' et 'remove'. Vous ne jouez pas avec la notation par points. J'ai même des méthodes sur ma classe Document appelées addItem et deleteItem pour déclencher des événements de modification sur le document lui-même. Lorsque vous effectuez une sauvegarde () sur le document, il appelle toJSON sur votre collection d'éléments.

Honnêtement, ce n'est qu'un cas simple pour nos documents et nous avons des sous-documents encore plus profonds. Faire face à cette complexité avec l'épine dorsale et surcharger plusieurs méthodes sur les modèles est un vrai casse-tête. Nous envisageons maintenant de remplacer le backbone par sproutcore à l'avenir.

Si vous devez traiter des documents très complexes, je vous suggère de regarder ExtJS ou sproutcore. Backbone est idéal pour un petit projet avec des modèles simples, mais se désagrège assez rapidement lorsque les objets/interactions commencent à augmenter.

Existe-t-il de nouvelles "meilleures pratiques" pour cela pour la 1.0 ?

@eranation : non, items hors de mes attributs de hachage afin de ne pas avoir à les synchroniser. Cela nécessite que vous extrayiez l'objet items de la réponse pour l'analyse et que vous le rajoutiez pour la sérialisation (voir ci-dessous). En dehors de cela, vous voudrez peut-être mettre la logique que @rsim a mise dans le initialize dans votre méthode constructor place, et utiliser on au lieu de bind (qui est semi-obsolète).

Je trouve qu'il est beaucoup plus facile d'avoir des attributs qui ne sont qu'un hachage superficiel (pas de collections imbriquées, de modèles, etc.), dans la mesure du possible.

var Document = Backbone.Model.extend({
  constructor: function() {
    this.items = new ItemSet(null, {document: this});
    this.items.on('change', this.save, this);
    Backbone.Model.apply(this, arguments);
  },
  parse: function(resp) {
    this.items.set(resp.items, {parse: true, remove: false});
    delete resp.items;
    return resp;
  },
  toJSON: function() {
    var attrs = _.clone(this.attributes);
    attrs.items = this.items.toJSON();
    return attrs;
  }
});
var ItemSet = Backbone.Collection.extend({
  model: Item,
  initialize: function(models, options) {
    this.document = options.document;
  }
});
var Item = Backbone.Model.extend({
  // access document with this.collection.document
});
var document1 = new Document({
  name: "Test",
  version: 1,
  items: [
    {name : "Item 1", position : 0},
    {name : "Item 2", position : 1}
  ]
});

Cela semble bien fonctionner même pour les schémas profondément imbriqués. (voir la doc )

@ akre54 merci, c'est un excellent exemple, très apprécié

Cette page vous a été utile?
0 / 5 - 0 notes