Tüm bir koleksiyon Backbone.js - Backbone.sync veya jQuery.ajax'a "nasıl" kaydedilir?


81

Bunun yapılabileceğinin farkındayım ve pek çok yere baktım (dahil: Tüm bir koleksiyonu kurtarmak için en iyi uygulama? ). Ama hala kodla "tam olarak nasıl" yazıldığından emin değilim? (gönderi bunu İngilizce olarak açıklıyor. Javascript'e özgü bir açıklamanın olması harika olurdu :)

Bir model koleksiyonum olduğunu varsayalım - modellerin kendileri iç içe geçmiş koleksiyonlara sahip olabilir. Ana koleksiyonun toJSON () yöntemini geçersiz kıldım ve geçerli bir JSON nesnesi alıyorum. Tüm koleksiyonu (karşılık gelen JSON) "kaydetmek" istiyorum, ancak omurga bu işlevsellikle yerleşik olarak gelmiyor.

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

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

});

Söylemen gereken bir yer biliyorum:

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

İşlemle birlikte 'görünüm' tamamlandıktan sonra, koleksiyona kendisini sunucuya "kaydetmesini" söylemekten sorumludur (toplu güncelleme / oluşturma isteğini yerine getirebilir).

Ortaya çıkan sorular:

  1. "Hepsini birbirine bağlamak" için kodda nasıl / ne yazılır?
  2. Geri aramaların 'doğru' konumu nedir ve bir "başarı / hata" geri araması nasıl belirtilir? Sözdizimsel olarak mı? Omurgaya geri aramaları kaydetmenin sözdiziminden emin değilim ...

Eğer gerçekten zor bir işse, jQuery.ajax'ı bir görünüm içinde çağırabilir ve başarı / hata geri aramalarını this.successMethodveya this.errorMethodolarak iletebilir miyiz ? Çalışacak mı?

Omurganın düşünme biçimiyle uyumlu olmalıyım - tüm koleksiyonların senkronizasyonunda kesinlikle bir şeyleri kaçırdığımı biliyorum.


Sunucu tarafı kodunuz bunu tek bir istek olarak alabilir mi? Başka bir deyişle, tüm üst düzey koleksiyon, tüm modeller ve iç içe geçmiş koleksiyonlar tek bir JSON paketi olarak mı? Yoksa her modeli ayrı ayrı kaydetmeniz mi gerekiyor? Düzenleme: Ah, daha yakından okuyun, sunucu "toplu güncelleme / oluşturma" yeteneğine sahip
Edward M Smith

@Edward: Evet! Bunu, genellikle bir endişe konusu olduğu için açık hale getirmiştim, ancak bu durumda değil :)
PhD

Peki, sunucunun almayı beklediği verilerin yapısı nedir?
Edward M Smith

@Edward: Verinin yapısı önemli mi? Bir yorumda biçimlendirme mümkün değildir, ancak şu şekildedir: [{postId: 1, labels: [{id: 1, name: "a"}, {id: 2, name: "b"}]}] Temelde her biri " postId ", kendileri nesne olan bir dizi / etiket dizisine sahip olabilir. Bu tür pek çok gönderi olabilir ... Bir şeyi kaçırmadığım sürece, veri formatının eldeki sorunla ilgisi olduğunu sanmıyorum
Doktora

Yanıtlar:


64

Benim ilk düşüncem, Backbone.Collection'da kaydetme yöntemini geçersiz kılmak değil, koleksiyonu başka bir Backbone.Model'e sarmak ve bunun üzerinde toJSON yöntemini geçersiz kılmaktır. Ardından Backbone.js, modeli tek bir kaynak olarak ele alır ve omurganın çok fazla düşünme şeklini kesmek zorunda kalmazsınız.

Backbone.Collection'ın toJSON yöntemine sahip olduğunu, bu nedenle işinizin çoğunun sizin için yapıldığını unutmayın. Sadece, Backbone.Model sarmalayıcınızın toJSON yöntemini Backbone.collection'a proxy yapmanız gerekir.

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
Kaynak koduna baktım ve Koleksiyonların bir kaydetme yöntemi yok gibi görünüyor , bu yüzden geçersiz kılma bir sorun olmamalı (eğer bir kaydetme yöntemine sahip olsaydı dünya çok daha kolay olurdu :)
PhD

Sarıcı fikri için +1 - temiz ve tatlı. Hiç düşünmedim. Backboard.sync'i doğrudan çağırmam ve "model" argümanı yerine koleksiyonu iletmem gerektiğini düşünüyordum. Modelin çalışması için bir url belirtmeniz gerekir ... herhangi bir fikriniz var mı? Senkronizasyon yöntemi dahili olarak sadece getURL(model)model argümanını çağırdığından ve herhangi bir tür karşılaştırma yapmadığından ... tasarım gereği kasıtlı gibi görünüyor
Doktora

3
Katılıyorum - çok zarif. Ancak, bu çözümle ilgili bir sorunla karşılaşıyorum: sarmalayıcı modelim her zaman yenidir, bu nedenle kaydettiğimde () her zaman bir POST olur. Verilerimi zaten aldım, bu yüzden kaydettiğimde () bir PUT olmalı. Sanırım isNew () = false olarak sabit kod yazabilirim veya sahte bir kimlik belirleyebilirim, ancak bu zarif bir çözüm gibi görünmüyor. Önerin var mı?
Scott Switzer

1
Gerçekten temiz cevap, CollectionWrapper'ı nasıl başlatacağınızı görmek güzel olurdu.
Anthony

Evet, benim için de çalıştı ve iyi bir çaba için +1 aldı ama sunucuya iki koleksiyon göndermek istersem nasıl devam edebilirim?
CodeNotFound

25

Çok basit ...

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

... koleksiyonlarınıza bir kaydetme yöntemi verecektir. Bunun, neyin değiştiğine bakılmaksızın her zaman tüm koleksiyon modellerini sunucuya göndereceğini unutmayın. seçenekler sadece normal jQuery ajax seçenekleridir.


4
Doğru görünüyor. Ekleyerek Olabilir return Backbone.sync..daha Backbonish olduğunu.
yves amsellem

Bence - tür için - güncelleme oluşturmaktan daha iyi olur ... Btw. modelin veya koleksiyonun toJSON'unu geçersiz kılabilirsiniz, böylece sunucuya neyin gönderileceğini düzenleyebilirsiniz ... (genellikle sadece id niteliği gerekir) Backbone.Relational'de json formatına ne ekleneceğini de ayarlayabilirsiniz.
inf3rno

1
Backbone.sync, bkz "oluşturmak" bekliyor backbonejs.org/docs/backbone.html#section-141
hacklikecrack

8

Sonunda 'kaydetme' benzeri bir yönteme sahip oldum ve içinde $ .ajax olarak adlandırdım. @Brandgonesurfing'in önerdiği gibi bir sarmalayıcı sınıfı eklememe gerek kalmadan üzerinde daha fazla kontrol sağladı (fikri kesinlikle sevmeme rağmen :) Daha önce de belirtildiği gibi collection.toJSON () yöntemi geçersiz kılındı ​​ve yaptığım tek şey onu kullanmaktı ajax çağrısında ...

Umarım bu, üzerine tökezleyen birine yardımcı olur ...


3
Backbone.ajax'ı aramanız daha iyi olur (yine de jQuery için vekildir, ancak onu daha sürdürülebilir hale getirir)
developerbmw

5

Bu gerçekten istemci ile sunucu arasındaki sözleşmenin ne olduğuna bağlıdır. İşte için bir PUT basitleştirilmiş CoffeeScript örnek /parent/:parent_id/childrenile {"children":[{child1},{child2}]}PUT ve karşılığında ne bir ebeveynin çocukları yerini alacak {"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

Bu oldukça basit bir örnek, çok daha fazlasını yapabilirsiniz ... gerçekten, save () çalıştırıldığında verilerinizin hangi durumda olduğuna, sunucuya göndermek için hangi durumda olması gerektiğine ve sunucunun ne verdiğine bağlıdır. geri.

Sunucunuzda bir PUT değeri [{child1},{child2]varsa, Backbone.sync hattınız olarak değişebilir response = Backbone.sync('update', @toJSON(), url: @url(), contentType: 'application/json').


"Url" seçeneklerinin özelliği burada gerekli değildir =)
Sergey Kamardin

5

Cevap, sunucu tarafında koleksiyonla ne yapmak istediğinize bağlıdır.

Gönderi ile ek veri göndermeniz gerekiyorsa, bir sarmalayıcı modeline veya ilişkisel bir modele ihtiyacınız olabilir .

İle sarıcı modeli her zaman kendi yazmak zorunda ayrıştırma yöntemi:

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

İlişkisel modeller daha iyidir, çünkü onları daha kolay yapılandırabilir verest hizmetinize gönderdiğiniz json'a hangi öznitelikleri koyacağınızı includeInJSON seçeneğiile düzenleyebilirsiniz.

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

Ek veri göndermezseniz , koleksiyonun kendisini senkronize edebilirsiniz . Bu durumda koleksiyonunuza (veya koleksiyon prototipine) bir kaydetme yöntemi eklemeniz gerekir :

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

3

Ayrıca Backbone koleksiyonlarının yerleşik bir kurtarmaya sahip olmamasına da şaşırdım. İşte bunu yapmak için omurga koleksiyonuma koyduğum şey. Koleksiyondaki her modeli yinelemek ve bağımsız olarak kaydetmek kesinlikle istemiyorum. Ayrıca, Node kullanarak arka uçta Backbone kullanıyorum, bu nedenle Backbone.syncküçük projemde düz bir dosyaya kaydetmek için yerel olanı geçersiz kılıyorum - ancak kod hemen hemen aynı olmalıdır:

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

Seçenekleri aktarmak da mantıklı, meselasave: function (options) { Backbone.sync('save', this, options); }
Matt Fletcher

3

Bildiğim eski iş parçacığı, sonunda yaptığım şey şudur:

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

İleriye doğru oldukça gergin :)


2

İşte basit bir örnek:

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

Koleksiyonunuzda save () yöntemini çağırdığınızda, tanımlanan URL'ye bir PUT yöntemi isteği gönderir.


Koleksiyon kaydetme sorunumu çözmeye yardımcı olup olamayacağınızı merak ediyordum, şu anda jsfiddle.net/kyllle/f1h4cz7f/3 toJSON () her modeli güncelliyor, ancak daha sonra kaydetme herhangi bir veriye sahip görünmüyor?
styler

1

Şöyle bir şey denerdim:

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

Kabul edilen cevap oldukça iyi, ancak bir adım daha ileri gidip size dinleyicileriniz için uygun olayların tetiklenmesini sağlayacak ve aynı zamanda seçenek ajax olay geri aramalarını iletmenize izin verecek bir kod verebilirim:

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'de hala backbone.js kullanan herkes için kabul edilen cevap çalışmıyor.

Model sarmalayıcıyı başlatırken, sarmalayıcı modelindeki toJSON () geçersiz kılmayı kaldırmayı ve koleksiyonda toJSON'u çağırmayı deneyin.

new ModelWrapper(Collection.toJSON());
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.