Backbone: Was ist der beste Weg, um eine Sammlung innerhalb eines Modells zu modellieren?

Erstellt am 4. Nov. 2010  ·  11Kommentare  ·  Quelle: jashkenas/backbone

Mein Team arbeitet mit einem interessanten Datenschema. Im Grunde haben wir ein Dokument mit einem Namen und einer Version und einem Array von Elementen, die dazu gehören. Den Elementen selbst sind keine IDs zugeordnet, da sie zum größeren Modell gehören, aber die Elemente können dem Hauptdokument hinzugefügt/bearbeitet/gelöscht werden. Das Modell sieht ungefähr so ​​aus:
{
Name: "Test",
Version 1,
Produkte : [
{Name: "Artikel 1",
Position : 0},
{Name: "Artikel 2",
Position : 1}]
}

Es wäre großartig, eine Sammlung für die zugrunde liegenden Elemente im Modell zu verwenden, aber jedes Mal, wenn die Sammlung aktualisiert wird, sollte das Modell mit seiner URL an den Server zurückgesendet werden. Was ist der beste Weg, dies in Backbone zu modellieren? Ich werde gerne mehr in einer Zusammenfassung posten, wenn weitere Informationen benötigt werden.

question

Hilfreichster Kommentar

@eranation : Nein, sollte ziemlich gleich sein. Wenn ich dieses Muster verwende, möchte ich items aus meinem Attribut-Hash heraushalten, damit ich sie nicht synchron halten muss. Dies erfordert, dass Sie das items Objekt zum Parsen aus der Antwort ziehen und zum Serialisieren wieder hinzufügen (siehe unten). Abgesehen davon möchten Sie vielleicht die Logik, die @rsim in initialize eingefügt hat, constructor Methode einfügen und on anstelle von bind (was teilweise veraltet ist).

Ich finde es viel einfacher, wenn Attribute nach Möglichkeit nur ein flacher Hash sind (keine verschachtelten Sammlungen, Modelle usw.).

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}
  ]
});

Dies scheint sogar für tief verschachtelte Schemata gut zu funktionieren. (siehe die Dokumente )

Alle 11 Kommentare

Ich würde sagen, Sie haben zwei Hauptoptionen ... Die erste besteht darin, die Gegenstände als Vanilla-Attribut zu belassen. Backbone verwendet eine tiefe Gleichheitsprüfung, wenn sich Attribute ändern. Wenn also ein inneres Element aktualisiert wird, weiß das Dokument davon.

Die andere Möglichkeit besteht darin, die Elemente aus dem Dokument zu ziehen und sie als eigenständige Modelle innerhalb einer auf das Dokument geklebten Sammlung anzuhängen (wir machen bei DocumentCloud so etwas). Zum Beispiel (grob gesprochen):

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

Funktioniert einer von beiden gut für Sie?

Ich würde auch empfehlen, dem Document-Objekt die Eigenschaft "document" wieder hinzuzufügen, auf die bei Bedarf von Elementen zugegriffen werden kann:

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}
  ]
});

Ich glaube, dass das Binden des Änderungsereignisses der Elemente an die Dokumentspeicherung das fehlende Stück ist. Aber auch das Hinzufügen des Dokuments zum ItemSet scheint sehr hilfreich zu sein. Wir werden das heute versuchen und ich werde euch wissen lassen, wie es ausgeht.

Das hat gut geklappt. Das größte Problem, das wir jetzt haben, ist mit Mongoiden. Danke für die Hilfe Jungs.

Wie würde ich vorgehen, um alle Elemente auf einmal zu speichern? Ich möchte nicht jedes Mal eine Anfrage stellen, wenn ein Element geändert wird, sondern alle mit einer einzigen Anfrage für die Aktion des Benutzers speichern. Ich habe darüber nachgedacht, alles zu sammeln, wenn das Dokument gespeichert wird, und die 'Elemente'-Attribute des Dokuments zu aktualisieren. Ist das eine gute Lösung?

Solange Ihre Elemente im Dokument festgelegt sind, werden die Elemente auch an den Server gesendet, wenn Sie document.save() ausführen.

Aber sagen wir, wenn ich zur Laufzeit ein Element zur Sammlung document1.items hinzufüge, wird es nicht automatisch auch dem 'items'-Attribut von document1 hinzugefügt. Wenn ich dann also document1.save() ausführe, wird das neue Modell, das ich der Sammlung hinzugefügt habe, nicht an den Server gesendet. Ich kann nicht sehen, wie sich die Änderungen an der Sammlung in den Attributen des Modells verbreiten könnten, die mit Speichern gesendet werden.

So gehen wir damit um: Das Dokument hat ein Standard-Items-Array. Bei der Initialisierung erstelle ich in einer überladenen Set-Methode eine neue Elementsammlung aus den Attributen und setze diese auf das Dokument.

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

An diesem Punkt befassen Sie sich nur mit den Methoden zum Sammeln von Elementen mit 'get', 'set', 'add' und 'remove'. Sie verwirren nicht mit der Punktnotation. Ich habe sogar Methoden in meiner Document-Klasse namens addItem und deleteItem, um Änderungsereignisse im Dokument selbst auszulösen. Wenn Sie ein save() für das Dokument ausführen, wird toJSON für Ihre Elementsammlung aufgerufen.

Ehrlich gesagt ist dies nur ein einfacher Fall für unsere Dokumente und wir haben noch tiefere Unterdokumente. Der Umgang mit dieser Komplexität mit Backbone und das Überladen mehrerer Methoden in den Modellen ist eine echte Nervensäge. Wir überlegen nun, in Zukunft Backbone durch Sproutcore zu ersetzen.

Wenn Sie mit wirklich komplexen Dokumenten arbeiten müssen, würde ich vorschlagen, sich ExtJS oder Sproutcore anzusehen. Backbone eignet sich hervorragend für ein kleines Projekt mit einfachen Modellen, fällt aber ziemlich schnell auseinander, wenn die Objekte/Interaktionen ansteigen.

Gibt es dafür neue "Best Practices" für 1.0?

@eranation : Nein, sollte ziemlich gleich sein. Wenn ich dieses Muster verwende, möchte ich items aus meinem Attribut-Hash heraushalten, damit ich sie nicht synchron halten muss. Dies erfordert, dass Sie das items Objekt zum Parsen aus der Antwort ziehen und zum Serialisieren wieder hinzufügen (siehe unten). Abgesehen davon möchten Sie vielleicht die Logik, die @rsim in initialize eingefügt hat, constructor Methode einfügen und on anstelle von bind (was teilweise veraltet ist).

Ich finde es viel einfacher, wenn Attribute nach Möglichkeit nur ein flacher Hash sind (keine verschachtelten Sammlungen, Modelle usw.).

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}
  ]
});

Dies scheint sogar für tief verschachtelte Schemata gut zu funktionieren. (siehe die Dokumente )

@akre54 danke, das ist ein tolles Beispiel, sehr geschätzt

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

azizZaben picture azizZaben  ·  5Kommentare

jashkenas picture jashkenas  ·  7Kommentare

cueedee picture cueedee  ·  3Kommentare

rafde picture rafde  ·  9Kommentare

alundiak picture alundiak  ·  7Kommentare