コレクション全体をBackbone.jsに保存する「方法」-Backbone.syncまたはjQuery.ajax?


81

私はそれができることをよく知っており、かなりの数の場所を見てきました(コレクション全体を保存するためのベストプラクティス?)。しかし、それがコードで「正確にどのように」書かれているのかはまだわかりません。(投稿は英語で説明しています。javascript固有の説明があると便利です:)

モデルのコレクションがあるとします。モデル自体にネストされたコレクションがある場合があります。親コレクションのtoJSON()メソッドをオーバーライドし、有効なJSONオブジェクトを取得しています。コレクション全体(対応するJSON)を「保存」したいのですが、バックボーンにはその機能が組み込まれていないようです。

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

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

});

私はあなたが言わなければならないどこかを知っています:

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?
 */
}

'view'が処理を完了すると、コレクションにサーバー上でそれ自体を「保存」するように指示する責任があります(一括更新/作成要求を処理できます)。

発生する質問:

  1. 「すべてを一緒に配線する」ためのコードをどのように/何を書くか?
  2. コールバックの「正しい」場所と、「成功/エラー」コールバックを指定する方法は何ですか?構文的に意味しますか?バックボーンにコールバックを登録する構文がわかりません...

それが本当にトリッキーな仕事である場合、ビュー内でjQuery.ajaxを呼び出して、this.successMethodまたはthis.errorMethod成功/エラーコールバックとして渡すことができますか?それは機能しますか?

バックボーンの考え方と同期する必要があります-コレクション全体の同期という何かが間違いなく欠けていることはわかっています。


サーバー側のコードはこれを単一のリクエストとして受け取ることができますか?言い換えれば、トップレベルのコレクション全体、すべてのモデル、およびネストされたコレクションを単一のJSONパケットとして?または、各モデルを個別に保存する必要がありますか?編集:ああ、よく読んでください、サーバーは「一括更新/作成」が可能です
エドワードMスミス

@エドワード:うん!それは通常懸念事項であるため、それを明示していましたが、この場合はそうではありません:)
PhD

では、サーバーが受信することを期待しているデータの構造は何ですか?
エドワードMスミス

@Edward:データの構造は重要ですか?コメントでフォーマットすることはできませんが、次のようになります:[{postId:1、labels:[{id:1、name: "a"}、{id:2、name: "b"}]}]基本的に各 " postId "は、それ自体がオブジェクトであるラベルのセット/配列を持つことができます。そのような投稿はたくさんあるかもしれません...私が何かを見逃していない限り、データ形式は目前の問題とは何の関係もないと思います
PhD

回答:


64

私の当面の考えは、Backbone.Collectionのsaveメソッドのメソッドをオーバーライドするのではなく、コレクションを別のBackbone.Modelでラップし、その上でtoJSONメソッドをオーバーライドすることです。そうすれば、Backbone.jsはモデルを単一のリソースとして扱い、backoneの考え方をハックする必要はありません。

Backbone.CollectionにはtoJSONメソッドがあるため、ほとんどの作業は自動的に行われることに注意してください。ラッパーBackbone.ModelのtoJSONメソッドを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
 }

});

3
ソースコードを調べたところ、Collectionsにsaveメソッドがないようです。したがって、オーバーライドは問題になりません(saveメソッドの世界があれば、はるかに簡単になります:)
PhD

ラッパーのアイデアの+ 1-清潔で甘い。全く考えていませんでした。Backboard.syncを直接呼び出して、「model」引数の代わりにコレクションを渡す必要があるのではないかと考えていました。モデルが機能するためには、モデルのURLを指定する必要があります...何か考えはありますか?syncメソッドは内部的getURL(model)にモデル引数を呼び出すだけであり、どのタイプの比較も実行しないため...設計上意図的であるように思われます
PhD

3
同意する-非常にエレガント。ただし、このソリューションで問題が発生しています。ラッパーモデルは常に新しいため、save()を実行すると常にPOSTになります。私はすでにデータを取得しているので、save()を実行するとPUTになるはずです。isNew()= falseをハードコーディングするか、偽のIDを設定できると思いますが、これは洗練された解決策のようには思えません。何か提案はありますか?
Scott Switzer 2013年

1
本当にきれいな答えです。CollectionWrapperをどのようにインスタンス化するかを確認するとよいでしょう。
アンソニー

はい、それは私にとってもうまくいき、良い努力のために+1しましたが、2つのコレクションをサーバーに送信したい場合はどうすればよいですか?
codeNotFound 2013

25

とてもシンプルです...

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

...コレクションにsaveメソッドを提供します。これにより、何が変更されたかに関係なく、常にすべてのコレクションのモデルがサーバーに送信されることに注意してください。オプションは通常のjQueryajaxオプションです。


4
正しいようです。追加するのreturn Backbone.sync..はもっとBackbonishかもしれません。
yves amsellem 2013年

タイプの場合、更新は作成よりも優れていると思います...ところで。モデルまたはコレクションのtoJSONをオーバーライドできるため、サーバーに送信するものを調整できます...(通常は必要なid属性のみ)Backbone.Relationalでは、json形式に何を追加するかを設定することもできます。
inf3rno 2013年

1
Backbone.syncは、参照の「作成」期待しているbackbonejs.org/docs/backbone.html#section-141を
hacklikecrack

8

結局、「保存」のようなメソッドがあり、その中で$ .ajaxを呼び出しました。@brandgonesurfingが提案したように、ラッパークラスを追加することなく、より詳細に制御できました(ただし、私はこのアイデアが大好きです:)すでにコレクションをオーバーライドしているので、前述のように、私が着陸したのはそれを使用することだけでした。 ajax呼び出しで...

これがそれにつまずいた誰かを助けることを願っています...


3
あなたは(それはとにかくjQueryのにプロキシが、それはそれはより保守なります)Backbone.ajaxを呼び出したほうが良いでしょう
developerbmw

5

これは、クライアントとサーバー間の契約内容によって異なります。これは、PUT to /parent/:parent_id/childrenwith{"children":[{child1},{child2}]}が親の子をPUTにあるものに置き換えて、次を返す単純化されたCoffeeScriptの例{"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

これは非常に単純な例です。さらに多くのことができます...実際には、save()が実行されたときのデータの状態、サーバーに出荷するために必要な状態、およびサーバーが提供する状態によって異なります。バック。

サーバーがのPUTで問題[{child1},{child2]がない場合、Backbone.sync行はに変更される可能性がありますresponse = Backbone.sync('update', @toJSON(), url: @url(), contentType: 'application/json')


オプション「url」の属性はここでは必要ありません=)
Sergey Kamardin 2013

5

答えは、サーバー側のコレクションで何をしたいかによって異なります。

投稿で追加のデータを送信する必要がある場合は、ラッパーモデルまたはリレーショナルモデルが必要になる場合があります。

ラッパーモデルは、常に独自の記述する必要があり、解析方法を:

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

リレーショナルモデルの方が優れていると思います。構成が簡単で、 includeInJSONオプションを使用して、 RESTサービスに送信するjsonに配置する属性を調整できるからです。

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

追加のデータを送信しない場合は、コレクション自体を同期できます。その場合、コレクション(またはコレクションプロトタイプ)にsaveメソッドを追加する必要があります。

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

3

また、Backboneコレクションに保存機能が組み込まれていないことにも驚きました。これが私がそれをするために私のバックボーンコレクションに置いたものです。コレクション内の各モデルを繰り返し処理して、個別に保存したくはありません。また、Nodeを使用してバックエンドでBackboneを使用しているため、ネイティブBackbone.syncをオーバーライドして小さなプロジェクトのフラットファイルに保存していますが、コードはほぼ同じである必要があります。

    save: function(){                                                                                                                                                                                                                                                                                                                                                     
      Backbone.sync('save', this, {                                                                                                                                                                                                                                                                                                                                     
        success: function(){                                                                                                                                                                                                                                                                                                                                          
          console.log('users saved!');                                                                                                                                                                                                                                                                                                                              
        }                                                                                                                                                                                                                                                                                                                                                             
      });                                                                                                                                                                                                                                                                                                                                                               
    }

次のように、オプションを渡すだけでも意味がありますsave: function (options) { Backbone.sync('save', this, options); }
Matt Fletcher

3

私が知っている古いスレッド、私がやったことは次のとおりです:

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

かなり緊張した前書き:)


2

簡単な例を次に示します。

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

コレクションでsave()メソッドを呼び出すと、定義されたURLにPUTメソッドリクエストが送信されます。


コレクションの保存の問題を解決するのに役立つかどうか疑問に思っていました。現在、jsfiddle.net / kyllle / f1h4cz7f / 3 toJSON()は各モデルを更新していますが、保存にデータがないようです。
スタイラー2015年

1

私は次のようなことを試みます:

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

受け入れられた答えはかなり良いですが、私はさらに一歩進んで、リスナーに対して適切なイベントが発生することを保証すると同時に、オプションの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);
}

0

2017年にまだbackbone.jsを使用している人にとって、受け入れられた答えは機能していません。

モデルラッパーをインスタンス化するときに、ラッパーモデルのtoJSON()オーバーライドを削除し、コレクションでtoJSONを呼び出してみてください。

new ModelWrapper(Collection.toJSON());
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.