Je suis bien conscient que cela peut être fait et j'ai regardé pas mal d'endroits (notamment: La meilleure pratique pour sauvegarder une collection entière? ). Mais je ne comprends toujours pas "exactement comment" est-il écrit dans le code? (L'article explique en anglais. Ce serait formidable d'avoir une explication spécifique à javascript :)
Supposons que j'ai une collection de modèles - les modèles eux-mêmes peuvent avoir des collections imbriquées. J'ai remplacé la méthode toJSON () de la collection parente et j'obtiens un objet JSON valide. Je souhaite "enregistrer" l'intégralité de la collection (JSON correspondant), mais le backbone ne semble pas être construit avec cette fonctionnalité.
var MyCollection = Backbone.Collection.extend({
model:MyModel,
//something to save?
save: function() {
//what to write here?
}
});
Je sais que vous devez dire quelque part:
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?
*/
}
Une fois que la «vue» est terminée avec le traitement, il est chargé de demander à la collection de se «sauvegarder» sur le serveur (capable de gérer une demande de mise à jour/création en bloc).
Questions qui se posent:
S'il s'agit bien d'un travail délicat, pouvons-nous appeler jQuery.ajax dans une vue et transmettre le this.successMethod
ou le this.errorMethod
en tant que rappels de succès/d'erreur? Est-ce que ça marchera?
Je dois être en phase avec la façon de penser de Backbone - je sais que quelque chose me manque, la synchronisation de collections entières.
Ma pensée immédiate n’est pas de remplacer la méthode sur la méthode de sauvegarde sur Backbone.Collection mais d’envelopper la collection dans un autre Backbone.Model et d’écraser la méthode toJSON sur cette méthode. Ensuite, Backbone.js considérera le modèle comme une ressource unique et vous n’aurez pas à modifier votre façon de penser.
Notez que Backbone.Collection a une méthode toJSON donc la plupart de votre travail est fait pour vous. Il vous suffit de connecter par proxy la méthode toJSON de votre wrapper Backbone.Model à Backbone.collection.
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
}
});
Un très simple ...
Backbone.Collection.prototype.save = function (options) {
Backbone.sync("create", this, options);
};
... donnera une méthode de sauvegarde à vos collections. N'oubliez pas que tous les modèles de la collection seront toujours publiés sur le serveur, indépendamment de ce qui a changé. les options ne sont que des options jQuery ajax normales.
Je me suis retrouvé avec une méthode semblable à la méthode "save" et j'ai appelé $ .ajax. Cela me donnait plus de contrôle sans la nécessité d'ajouter une classe wrapper, comme @brandgonesurfing le suggérait (même si j'adore l'idée :) Comme je l'ai déjà mentionné, la méthode collection.toJSON () a été ignorée dans l'appel ajax ...
J'espère que cela aidera quelqu'un qui trébuche dessus ...
Cela dépend vraiment du contrat entre le client et le serveur. Voici un exemple CoffeeScript simplifié dans lequel un PUT à /parent/:parent_id/children
avec {"children":[{child1},{child2}]}
remplacera les enfants d'un parent par le contenu du PUT et renverra {"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
C’est un exemple assez simple, vous pouvez en faire beaucoup plus… Cela dépend vraiment de l’état de vos données lorsque save () est exécuté, de l’état qu’il doit avoir pour être envoyé au serveur et de ce que le serveur donne retour.
Si votre serveur accepte un PUT de [{child1},{child2]
, votre ligne Backbone.sync pourrait alors devenir response = Backbone.sync('update', @toJSON(), url: @url(), contentType: 'application/json')
.
La réponse dépend de ce que vous voulez faire avec la collection côté serveur.
Si vous devez envoyer des données supplémentaires avec le message, vous aurez peut-être besoin d'un modèle wrapper ou d'un modèle relationnel.
Avec le modèle wrapper, vous devez toujours écrire votre propre méthode parse:
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;
}
});
Les modèles relationnels sont meilleurs, je pense, parce que vous pouvez les configurer plus facilement et que vous pouvez régler avec l'option includeInJSON quels attributs attribuer au json que vous envoyez à votre service de repos.
var House = Backbone.RelationalModel.extend({
url: function (){
return "/house/"+this.id;
},
relations: [
{
type: Backbone.HasMany,
key: 'occupants',
relatedModel: Person,
includeInJSON: ["id"],
reverseRelation: {
key: 'livesIn'
}
}
]
});
Si vous n'envoyez pas de données supplémentaires, vous pouvez synchroniser la collection elle-même. Vous devez ajouter une méthode save à votre collection (ou au prototype de la collection) dans ce cas:
var Occupants = Backbone.Collection.extend({
url: "/concrete-house/occupants",
model: Person,
save: function (options) {
this.sync("update", this, options);
}
});
Vieux fil que je sais, ce que j'ai fini par faire est le suivant:
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();
Joli straint en avant :)
J'ai également été surpris que les collections Backbone ne disposent pas d'une sauvegarde intégrée. Voici ce que je mets sur ma collection de base pour le faire. Je ne veux certainement pas parcourir chaque modèle de la collection et enregistrer de manière indépendante. De plus, j'utilise Backbone sur le backend à l'aide de Node, je remplace donc le Backbone.sync
natif pour l'enregistrer dans un fichier plat sur mon petit projet — mais le code devrait être à peu près le même:
save: function(){
Backbone.sync('save', this, {
success: function(){
console.log('users saved!');
}
});
}
Voici un exemple simple:
var Books = Backbone.Collection.extend({
model: Book,
url: function() {
return '/books/';
},
save: function(){
Backbone.sync('create', this, {
success: function() {
console.log('Saved!');
}
});
}
});
Lorsque vous appelez la méthode save () sur votre collection, une requête de méthode PUT sera envoyée à l'URL définie.
Je voudrais essayer quelque chose comme:
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
}
});
La réponse acceptée est plutôt bonne, mais je peux aller un peu plus loin et vous donner un code qui garantira que les événements appropriés sont déclenchés pour vos auditeurs tout en vous permettant de passer des rappels d’événements optionels ajax:
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);
}
Pour ceux qui utilisent toujours backbone.js en 2017, la réponse acceptée ne fonctionne pas.
Essayez de supprimer le remplacement toJSON () dans le modèle d'encapsuleur et d'appeler toJSON sur la collection lorsque vous instanciez l'encapsuleur de modèle.
new ModelWrapper(Collection.toJSON());