web-dev-qa-db-fra.com

"Comment" enregistrer une collection entière dans Backbone.js - Backbone.sync ou jQuery.ajax?

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:

  1. Comment/quoi écrire dans le code pour "câbler le tout ensemble"?
  2. Quel est le "bon" emplacement des rappels et comment spécifier un rappel "succès/erreur"? Je ne parle pas syntaxiquement de la syntaxe d'enregistrement des rappels dans le réseau principal ...

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.

81
PhD

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
 }

});
64
bradgonesurfing

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.

25
hacklikecrack

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 ...

8
PhD

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').

5
carpeliam

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);
    }
});
5
inf3rno

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 :)

3
Rene Weteling

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!');                                                                                                                                                                                                                                                                                                                              
        }                                                                                                                                                                                                                                                                                                                                                             
      });                                                                                                                                                                                                                                                                                                                                                               
    }
3
Mauvis Ledford

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.

2
Gaurav Gupta

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

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

1
philfreo

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);
}
1
Throttlehead

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());
0
zeros-and-ones