Ich bin mir bewusst, dass dies möglich ist, und ich habe mir einige Orte angesehen (einschließlich: Best Practice zum Speichern einer gesamten Sammlung? ). Aber mir ist immer noch nicht klar "wie genau" wird es im Code geschrieben? (Der Beitrag erklärt es auf Englisch. Es wäre großartig, 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 "speichern" (entsprechend JSON), aber das Backbone scheint nicht mit dieser Funktionalität eingebaut 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 abgeschlossen ist, ist sie dafür verantwortlich, dass die Sammlung sich auf dem Server "speichert" (die eine Bulk-Aktualisierungs-/Erstellungsanforderung verarbeiten kann).
Fragen, die sich stellen:
Wenn es sich tatsächlich um einen kniffligen Job handelt, können wir jQuery.ajax innerhalb einer Ansicht aufrufen und den this.successMethod
oder this.errorMethod
als Erfolgs-/Fehlerrückrufe übergeben? Wird es funktionieren?
Ich muss mich mit der Denkweise von Backbone synchronisieren - ich weiß, dass ich definitiv etwas verpasse, was die Synchronisierung der gesamten Sammlungen betrifft.
Mein erster Gedanke ist, die Methode in der save-Methode in Backbone.Collection nicht zu überschreiben, sondern die Auflistung in ein anderes Backbone.Model zu verpacken und die toJSON-Methode darauf zu überschreiben. Dann wird Backbone.js das Modell als eine einzige Ressource behandeln, und Sie müssen nicht den Weg, den Backone denkt, zu sehr hacken.
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 lediglich die toJSON-Methode Ihres Wrappers Backbone.Model an die Backbone.collection übergeben.
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
}
});
Ein sehr einfaches ...
Backbone.Collection.prototype.save = function (options) {
Backbone.sync("create", this, options);
};
... gibt Ihren Sammlungen eine sichere Methode. Beachten Sie, dass dies immer alle Modelle der Sammlung auf dem Server bereitstellt, unabhängig davon, was sich geändert hat. Optionen sind nur normale jQuery-Ajax-Optionen.
Am Ende hatte ich nur eine "save" -Methode und rief $ .ajax an. Es gab mir mehr Kontrolle darüber, ohne eine Wrapper-Klasse hinzufügen zu müssen, wie @brandgonesurfing vorschlug (obwohl ich die Idee absolut liebe.) im Ajax-Anruf ...
Hoffe, das hilft jemandem, der darauf stößt ...
Dies hängt wirklich davon ab, wie der Vertrag zwischen dem Client und dem Server ist. Hier ist ein vereinfachtes CoffeeScript-Beispiel, bei dem ein PUT für /parent/:parent_id/children
mit {"children":[{child1},{child2}]}
die Kinder eines Elternteils durch den Inhalt des PUT ersetzt und {"children":[{child1},{child2}]}
zurückgibt:
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 noch viel mehr tun ... es hängt wirklich davon ab, in welchem Status sich Ihre Daten befinden, wenn save () ausgeführt wird, in welchem Status sie sich befinden muss, um an den Server zu senden, und was der Server angibt zurück.
Wenn Ihr Server mit einem PUT von [{child1},{child2]
in Ordnung ist, kann sich Ihre Backbone.sync-Zeile in response = Backbone.sync('update', @toJSON(), url: @url(), contentType: 'application/json')
ändern.
Die Antwort hängt davon ab, was Sie mit der Sammlung auf der Serverseite tun möchten.
Wenn Sie zusätzliche Daten mit dem Post senden müssen, benötigen Sie möglicherweise ein wrapper-Modell oder ein relationales Modell.
Mit dem wrapper model müssen Sie immer Ihre eigene parse -Methode 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 besser Ich denke, weil Sie einfacher konfigurieren können und Sie können mit der Option includeInJSON festlegen, welche Attribute Sie in den von Ihnen gesendeten Json einfügen möchten zu Ihrem Rastdienst.
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 Prototyp der Sammlung) eine save - Methode hinzufügen:
var Occupants = Backbone.Collection.extend({
url: "/concrete-house/occupants",
model: Person,
save: function (options) {
this.sync("update", this, options);
}
});
Ein alter Thread, den ich kenne, ist folgendes:
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 abzulehnen :)
Ich war auch überrascht, dass Backbone-Kollektionen keinen eingebauten Save haben. Hier ist, was ich in meine Backbone-Kollektion stecke, um es zu tun. Ich möchte definitiv nicht jedes Modell in der Sammlung durchlaufen und unabhängig speichern. Außerdem verwende ich Backbone auf dem Backend mit Node. Daher überschreibe ich den nativen Code Backbone.sync
, um ihn in einer flachen Datei in meinem kleinen Projekt zu speichern. Der Code sollte jedoch ziemlich identisch sein:
save: function(){
Backbone.sync('save', this, {
success: function(){
console.log('users saved!');
}
});
}
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 Ihrer Sammlung aufrufen, wird eine PUT-Methodenanforderung an die definierte URL gesendet.
Ich würde so etwas versuchen:
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
}
});
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 Zuhörer ausgelöst werden, und Sie können auch Option ajax-Ereignisrückrufe ü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);
}
Für alle, die noch 2017 backbone.js verwenden, funktioniert die akzeptierte Antwort nicht.
Entfernen Sie die toJSON () - Überschreibung im Wrapper-Modell und rufen Sie toJSON für die Auflistung auf, wenn Sie den Modell-Wrapper instanziieren.
new ModelWrapper(Collection.toJSON());