Wie wird eine gesamte Sammlung in Backbone.js - Backbone.sync oder jQuery.ajax gespeichert?

81

Ich bin mir sehr wohl bewusst, dass dies möglich ist, und habe mir einige Stellen angesehen (einschließlich: Best Practice zum Speichern einer gesamten Sammlung? ). Aber mir ist immer noch nicht klar, "genau wie" es in Code geschrieben ist? (Der Beitrag erklärt es auf Englisch. Es wäre toll, eine Javascript-spezifische Erklärung zu haben :)

Angenommen, ich habe eine Sammlung von Modellen - die Modelle selbst haben möglicherweise verschachtelte Sammlungen. Ich habe die toJSON () -Methode der übergeordneten Auflistung überschrieben und erhalte ein gültiges JSON-Objekt. Ich möchte die gesamte Sammlung (entsprechendes JSON) "speichern", aber das Backbone scheint nicht mit dieser Funktionalität ausgestattet zu sein.

var MyCollection = Backbone.Collection.extend({
model:MyModel,

//something to save?
save: function() {
   //what to write here?
 }

});

Ich weiß, irgendwo muss man sagen:

Backbone.sync = function(method, model, options){
/*
 * What goes in here?? If at all anything needs to be done?
 * Where to declare this in the program? And how is it called?
 */
}

Sobald die 'Ansicht' mit der Verarbeitung fertig ist, ist sie dafür verantwortlich, dass die Sammlung sich selbst auf dem Server "speichern" soll (in der Lage ist, eine Massenaktualisierungs- / Erstellungsanforderung zu verarbeiten).

Fragen, die sich stellen:

  1. Wie / was soll man in Code schreiben, um "alles miteinander zu verbinden"?
  2. Was ist der "richtige" Ort für die Rückrufe und wie kann ein "Erfolg / Fehler" -Rückruf angegeben werden? Ich meine syntaktisch? Ich bin mir nicht sicher, wie man Rückrufe im Backbone registriert ...

Wenn es tatsächlich ein kniffliger Job ist, können wir jQuery.ajax in einer Ansicht aufrufen und das this.successMethododer übergebenthis.errorMethod als Erfolgs- / Fehlerrückrufe übergeben? Wird es funktionieren?

Ich muss mich mit der Denkweise von Backbone synchronisieren - ich weiß, dass mir definitiv etwas fehlt, das ganze Sammlungen synchronisiert.

PhD
quelle
Kann Ihr serverseitiger Code dies als einzelne Anforderung annehmen? Mit anderen Worten, die gesamte Top-Level-Sammlung, alle Modelle und verschachtelten Sammlungen als ein einziges JSON-Paket? Oder müssen Sie jedes Modell einzeln speichern? Bearbeiten: Ah, lesen Sie näher, der Server ist in der Lage, "Bulk-Update / erstellen"
Edward M Smith
@ Edward: Yup! Hatte das explizit gemacht, da es normalerweise ein Problem ist, aber in diesem Fall nicht :)
PhD
Wie ist die Struktur der Daten, die der Server erwartet?
Edward M Smith
@ Edward: Ist die Struktur der Daten wichtig? Die Formatierung in einem Kommentar ist nicht möglich, aber es ist wie folgt: [{postId: 1, Labels: [{id: 1, Name: "a"}, {id: 2, Name: "b"}]}] Grundsätzlich jeder " postId "kann eine Reihe / ein Array von Beschriftungen haben, die selbst Objekte sind. Es kann viele solcher Beiträge geben ... Ich glaube nicht, dass das Datenformat etwas mit dem vorliegenden Problem zu tun hat, es sei denn, ich vermisse etwas
PhD

Antworten:

64

Mein unmittelbarer Gedanke ist, die Methode zum Speichern in Backbone.Collection nicht zu überschreiben, sondern die Sammlung in ein anderes Backbone.Model zu verpacken und die toJSON-Methode dazu zu überschreiben. Dann behandelt Backbone.js das Modell als eine einzige Ressource und Sie müssen nicht hacken, wie backone zu viel denkt.

Beachten Sie, dass Backbone.Collection über eine toJSON-Methode verfügt, sodass der Großteil Ihrer Arbeit für Sie erledigt wird. Sie müssen nur die toJSON-Methode Ihres Wrappers Backbone.Model auf die Backbone.collection übertragen.

var MyCollectionWrapper = Backbone.Model.extend({
url: "/bulkupload",

//something to save?
toJSON: function() {
    return this.model.toJSON(); // where model is the collection class YOU defined above
 }

});
Bradgonesurfing
quelle
3
Ich habe den Quellcode durchgesehen und es scheint, dass Sammlungen keine Speichermethode haben, daher sollte das Überschreiben kein Problem sein (wenn es eine Speichermethodenwelt hätte, wäre das viel einfacher :)
PhD
+1 für die Wrapper-Idee - sauber und süß. Ich habe überhaupt nicht daran gedacht. Ich dachte, dass ich Backboard.sync vielleicht direkt aufrufen und die Sammlung anstelle des Arguments "model" übergeben muss. Müssen Sie eine URL für das Modell angeben, damit es funktioniert ... irgendwelche Gedanken? Da die Synchronisierungsmethode intern nur getURL(model)das Modellargument aufruft und auch keine Art von Vergleichen durchführt ... scheint dies beabsichtigt zu sein
PhD
3
Stimme zu - sehr elegant. Bei dieser Lösung tritt jedoch ein Problem auf: Mein Wrapper-Modell ist immer neu. Wenn ich also () speichere, handelt es sich immer um einen POST. Ich habe meine Daten bereits abgerufen. Wenn ich also () speichere, sollte es sich um einen PUT handeln. Ich nehme an, ich kann isNew () = false hart codieren oder eine gefälschte ID festlegen, aber dies scheint keine elegante Lösung zu sein. Hast du irgendwelche Vorschläge?
Scott Switzer
1
Wirklich saubere Antwort, es wäre schön zu sehen, wie Sie den CollectionWrapper instanziieren würden.
Anthony
Ja, es hat auch bei mir funktioniert und +1 für die gute Anstrengung, aber wenn ich zwei Sammlungen an den Server senden möchte, wie gehe ich vor?
CodeNotFound
25

Eine sehr einfache ...

Backbone.Collection.prototype.save = function (options) {
    Backbone.sync("create", this, options);
};

... gibt Ihren Sammlungen eine Speichermethode. Beachten Sie, dass dadurch immer alle Modelle der Sammlung auf dem Server veröffentlicht werden, unabhängig davon, was sich geändert hat. Optionen sind nur normale jQuery Ajax-Optionen.

Hacklikecrack
quelle
4
Scheint richtig. return Backbone.sync..Möglicherweise ist das Hinzufügen mehr Backbonish.
Yves Amsellem
Ich denke - für den Typ - Update wäre besser als erstellen ... Übrigens. Sie können den toJSON des Modells oder der Sammlung überschreiben, um zu regeln, was an den Server gesendet werden soll ... (normalerweise nur das erforderliche ID-Attribut). In Backbone.Relational können Sie auch festlegen, was dem json-Format hinzugefügt werden soll.
inf3rno
1
Backbone.sync erwartet "create", siehe backbonejs.org/docs/backbone.html#section-141
hacklikecrack
8

Am Ende hatte ich nur eine 'save'-ähnliche Methode und rief $ .ajax darin auf. Es gab mir mehr Kontrolle darüber, ohne dass ich eine Wrapper-Klasse hinzufügen musste, wie von @brandgonesurfing vorgeschlagen (obwohl ich die Idee absolut liebe :) Wie bereits erwähnt, da ich die Methode collection.toJSON () bereits überschrieben hatte, war alles, was ich landete, die Verwendung im Ajax-Anruf ...

Hoffe das hilft jemandem, der darauf stößt ...

PhD
quelle
3
Sie sollten besser Backbone.ajax aufrufen (es steht ohnehin für jQuery, aber es macht es wartbarer)
Developerbmw
5

Dies hängt wirklich davon ab, was der Vertrag zwischen dem Client und dem Server ist. Hier ist ein vereinfachtes CoffeeScript-Beispiel, bei dem ein PUT /parent/:parent_id/childrenmit {"children":[{child1},{child2}]}die Kinder eines Elternteils durch das ersetzt, was im PUT enthalten ist, und zurückgibt {"children":[{child1},{child2}]}:

class ChildElementCollection extends Backbone.Collection
  model: Backbone.Model
  initialize: ->
    @bind 'add', (model) -> model.set('parent_id', @parent.id)

  url: -> "#{@parent.url()}/children" # let's say that @parent.url() == '/parent/1'
  save: ->
    response = Backbone.sync('update', @, url: @url(), contentType: 'application/json', data: JSON.stringify(children: @toJSON()))
    response.done (models) => @reset models.children
    return response

Dies ist ein ziemlich einfaches Beispiel, Sie können viel mehr tun ... es hängt wirklich davon ab, in welchem ​​Zustand sich Ihre Daten befinden, wenn save () ausgeführt wird, in welchem ​​Zustand sie sich befinden müssen, um an den Server gesendet zu werden, und was der Server gibt zurück.

Wenn Ihr Server mit einem PUT von in Ordnung ist [{child1},{child2], kann sich Ihre Backbone.sync-Zeile in ändern response = Backbone.sync('update', @toJSON(), url: @url(), contentType: 'application/json').

Carpeliam
quelle
Attribut der Optionen "URL" ist hier nicht erforderlich =)
Sergey Kamardin
5

Die Antwort hängt davon ab, was Sie mit der Sammlung auf der Serverseite tun möchten.

Wenn Sie zusätzliche Daten mit der Post senden müssen, benötigen Sie möglicherweise ein Wrapper-Modell oder ein relationales Modell .

Beim Wrapper-Modell müssen Sie immer Ihre eigene Analysemethode schreiben :

var Occupants = Backbone.Collection.extend({
    model: Person
});

var House = Backbone.Model.extend({
    url: function (){
        return "/house/"+this.id;
    },
    parse: function(response){
        response.occupants = new Occupants(response.occupants)
        return response;
    }
});

Relationale Modelle sind meiner Meinung nach besser, da Sie sie einfacher konfigurieren und mit derOption includeInJSON festlegen können, welche Attribute inden JSON eingefügt werden sollen, den Sie an Ihren Restdienst senden.

var House = Backbone.RelationalModel.extend({
    url: function (){
        return "/house/"+this.id;
    },
    relations: [
        {
            type: Backbone.HasMany,
            key: 'occupants',
            relatedModel: Person,
            includeInJSON: ["id"],
            reverseRelation: {
                key: 'livesIn'
            }
        }
    ]
});

Wenn Sie keine zusätzlichen Daten senden , können Sie die Sammlung selbst synchronisieren . In diesem Fall müssen Sie Ihrer Sammlung (oder dem Sammlungsprototyp) eine Speichermethode hinzufügen :

var Occupants = Backbone.Collection.extend({
    url: "/concrete-house/occupants",
    model: Person,
    save: function (options) {
        this.sync("update", this, options);
    }
});
inf3rno
quelle
3

Ich war auch überrascht, dass Backbone-Sammlungen keinen integrierten Speicher haben. Hier ist, was ich in meine Backbone-Sammlung aufgenommen habe, um dies zu tun. Ich möchte definitiv nicht jedes Modell in der Sammlung durchlaufen und unabhängig speichern. Außerdem verwende ich Backbone im Backend mithilfe von Node. Daher überschreibe ich den nativen Backbone.syncCode, um ihn in einer kleinen Datei in meinem kleinen Projekt zu speichern. Der Code sollte jedoch weitgehend identisch sein:

    save: function(){                                                                                                                                                                                                                                                                                                                                                     
      Backbone.sync('save', this, {                                                                                                                                                                                                                                                                                                                                     
        success: function(){                                                                                                                                                                                                                                                                                                                                          
          console.log('users saved!');                                                                                                                                                                                                                                                                                                                              
        }                                                                                                                                                                                                                                                                                                                                                             
      });                                                                                                                                                                                                                                                                                                                                                               
    }
Mauvis Ledford
quelle
Es macht auch Sinn, nur Optionen zu übergeben, wiesave: function (options) { Backbone.sync('save', this, options); }
Matt Fletcher
3

Alter Thread, den ich kenne, was ich letztendlich gemacht habe, ist der folgende:

Backbone.Collection.prototype.save = function (options) {
            // create a tmp collection, with the changed models, and the url
            var tmpCollection = new Backbone.Collection( this.changed() );
            tmpCollection.url = this.url;
            // sync
            Backbone.sync("create", tmpCollection, options);
        };
        Backbone.Collection.prototype.changed = function (options) {
            // return only the changed models.
            return this.models.filter( function(m){
                return m.hasChanged()
            });
        };
// and sync the diffs.
self.userCollection.save();

Ziemlich anstrengend vorwärts :)

Rene Weteling
quelle
2

Hier ist ein einfaches Beispiel:

var Books = Backbone.Collection.extend({
model: Book,
url: function() {
  return '/books/';
},
save: function(){
  Backbone.sync('create', this, {
    success: function() {
      console.log('Saved!');
    }
  });
 }
});

Wenn Sie die save () -Methode für Ihre Sammlung aufrufen, wird eine PUT-Methodenanforderung an die definierte URL gesendet.

Gaurav Gupta
quelle
Ich habe mich gefragt, ob Sie helfen könnten, mein Problem beim Speichern von Sammlungen zu lösen. Im Moment aktualisiert jsfiddle.net/kyllle/f1h4cz7f/3 toJSON () jedes Modell, aber dann scheint save keine Daten zu haben.
Styler
1

Ich würde so etwas versuchen wie:

var CollectionSync = function(method, model, [options]) {
    // do similar things to Backbone.sync
}

var MyCollection = Backbone.Collection.extend({
    sync: CollectionSync,
    model: MyModel,
    getChanged: function() {
        // return a list of models that have changed by checking hasChanged()
    },
    save: function(attributes, options) {
        // do similar things as Model.save
    }
});

( https://stackoverflow.com/a/11085198/137067 )

Philfreo
quelle
1

Die akzeptierte Antwort ist ziemlich gut, aber ich kann noch einen Schritt weiter gehen und Ihnen Code geben, der sicherstellt, dass die richtigen Ereignisse für Ihre Listener ausgelöst werden, und Ihnen gleichzeitig die Möglichkeit gibt, Rückrufe für Ajax-Ereignisse zu übergeben:

save: function( options ) {
  var self = this;

  var success = options.success;
  var error = options.error;
  var complete = options.complete;

  options.success = function( response, status, xhr ) {
    self.trigger('sync', self, response, options);
    if (success) return success.apply(this, arguments);
  };

  options.error = function( response, status, xhr ) {
    self.trigger('error', self, response, options);
    if (error) return error.apply(this, arguments);
  };

  options.complete = function( response, status, xhr ) {
    if (complete) return complete.apply(this, arguments);
  }

  Backbone.sync('create', this, options);
}
Drosselklappe
quelle
0

Für alle, die 2017 noch backbone.js verwenden, funktioniert die akzeptierte Antwort nicht.

Entfernen Sie die Überschreibung toJSON () im Wrapper-Modell und rufen Sie toJSON in der Auflistung auf, wenn Sie den Modell-Wrapper instanziieren.

new ModelWrapper(Collection.toJSON());
Nullen und Einsen
quelle