“Làm thế nào” để lưu toàn bộ bộ sưu tập trong Backbone.js - Backbone.sync hoặc jQuery.ajax?


81

Tôi biết rõ điều đó có thể được thực hiện và tôi đã xem xét khá nhiều nơi (bao gồm: Phương pháp hay nhất để lưu toàn bộ bộ sưu tập? ). Nhưng tôi vẫn không rõ "chính xác" nó được viết như thế nào trong mã? (bài đăng giải thích nó bằng tiếng Anh. Thật tuyệt nếu có một lời giải thích cụ thể về javascript :)

Giả sử tôi có một bộ sưu tập các mô hình - bản thân các mô hình có thể có các bộ sưu tập lồng nhau. Tôi đã ghi đè phương thức toJSON () của tập hợp mẹ và tôi đang nhận được một đối tượng JSON hợp lệ. Tôi muốn "lưu" toàn bộ bộ sưu tập (JSON tương ứng), nhưng xương sống dường như không được tích hợp sẵn chức năng đó.

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

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

});

Tôi biết ở đâu đó bạn phải nói:

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

Khi 'chế độ xem' được xử lý xong, nó có trách nhiệm ra lệnh cho bộ sưu tập tự "lưu" trên máy chủ (có khả năng xử lý yêu cầu cập nhật / tạo hàng loạt).

Các câu hỏi phát sinh:

  1. Làm thế nào / những gì để viết trong mã để "kết nối tất cả lại với nhau"?
  2. Vị trí 'bên phải' của các lệnh gọi lại và cách chỉ định lệnh gọi lại "thành công / lỗi" là gì? Ý tôi là về mặt cú pháp? Tôi không rõ về cú pháp đăng ký các cuộc gọi lại trong đường trục ...

Nếu đó thực sự là một công việc khó khăn thì chúng ta có thể gọi jQuery.ajax trong một chế độ xem và chuyển this.successMethodhoặcthis.errorMethod gọi lại thành công / lỗi không ?? Nó sẽ hoạt động?

Tôi cần phải đồng bộ với cách suy nghĩ của backbone - tôi biết chắc chắn tôi đang thiếu một thứ gì đó wrt, đồng bộ hóa toàn bộ bộ sưu tập.


Mã phía máy chủ của bạn có thể coi đây là một yêu cầu duy nhất không? Nói cách khác, toàn bộ bộ sưu tập cấp cao nhất, tất cả các mô hình và bộ sưu tập lồng nhau dưới dạng một gói JSON duy nhất? Hoặc, bạn cần lưu từng mô hình riêng lẻ? Edit: Ah, đọc gần hơn, các máy chủ có khả năng "cập nhật hàng loạt / tạo ra"
Edward M Smith

@Edward: Đúng vậy! Đã đưa ra là rõ ràng vì nó thường là một điểm đáng quan tâm, nhưng không phải trong trường hợp này :)
tiến sĩ

Vì vậy, cấu trúc của dữ liệu mà máy chủ mong đợi nhận được là gì?
Edward M Smith,

@Edward: Cấu trúc của dữ liệu có quan trọng không? Không thể định dạng trong một nhận xét nhưng nó như thế này: [{postId: 1, label: [{id: 1, name: "a"}, {id: 2, name: "b"}]}] Về cơ bản là mỗi " postId "có thể có một tập hợp / mảng nhãn mà bản thân chúng là các đối tượng. Có thể có nhiều bài viết như vậy ... Tôi không nghĩ rằng các định dạng dữ liệu đã có bất cứ điều gì để làm với vấn đề ở bàn tay, trừ khi tôi đang thiếu một cái gì đó
tiến sĩ

Câu trả lời:


64

Suy nghĩ ngay lập tức của tôi là không ghi đè phương thức trên phương thức lưu trên Backbone.Collection mà bọc bộ sưu tập trong một Backbone.Model khác và ghi đè phương thức toJSON trên đó. Sau đó, Backbone.js sẽ coi mô hình như một tài nguyên duy nhất và bạn không phải hack cách backone suy nghĩ quá nhiều.

Lưu ý rằng Backbone.Collection có phương thức toJSON nên hầu hết công việc của bạn đều được thực hiện cho bạn. Bạn chỉ cần ủy quyền phương thức toJSON của trình bao bọc Backbone.Model của bạn tới 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
Tôi nhìn qua mã nguồn và có vẻ như bộ sưu tập không có một phương pháp tiết kiệm, vì vậy trọng không phải là một vấn đề (nếu nó đã có một tiết kiệm phương pháp trên thế giới sẽ là một LOT dễ dàng hơn :)
tiến sĩ

+1 cho ý tưởng về trình bao bọc - sạch sẽ và ngọt ngào. Hoàn toàn không nghĩ ra. Tôi đã nghĩ rằng có lẽ tôi sẽ phải gọi Backboard.sync trực tiếp và chỉ chuyển bộ sưu tập thay cho đối số "model". Có cần phải chỉ định một url cho mô hình để nó hoạt động mặc dù ... bất kỳ suy nghĩ nào? Kể từ khi phương pháp đồng bộ trong nội bộ chỉ cần gọi getURL(model)vào luận điểm mô hình và không thực hiện bất kỳ sự so sánh typeof hoặc ... có vẻ là có chủ ý bởi thiết kế
Tiến sĩ

3
Đồng ý - rất thanh lịch. Tuy nhiên, tôi đang gặp phải một vấn đề với giải pháp này: mô hình trình bao bọc của tôi luôn mới, do đó khi tôi lưu (), nó luôn là một ĐĂNG. Tôi đã truy xuất dữ liệu của mình, vì vậy khi tôi lưu (), nó phải là PUT. Tôi cho rằng tôi có thể viết mã isNew () = false hoặc đặt ID giả, nhưng điều này có vẻ không phải là một giải pháp tốt. Bạn có đề nghị nào không?
Scott Switzer

1
Câu trả lời thực sự rõ ràng, sẽ rất tuyệt khi biết cách bạn khởi tạo CollectionWrapper.
Anthony

Có, Nó cũng hoạt động với tôi và +1 vì nỗ lực tốt nhưng nếu tôi muốn gửi hai bộ sưu tập đến máy chủ, tôi phải làm thế nào?
CodeNotFound,

25

Rất đơn giản ...

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

... sẽ cung cấp cho bộ sưu tập của bạn một phương pháp lưu. Hãy nhớ rằng điều này sẽ luôn đăng tất cả các mô hình của bộ sưu tập lên máy chủ bất kể điều gì đã thay đổi. các tùy chọn chỉ là các tùy chọn ajax jQuery bình thường.


4
Có vẻ đúng. Có thể thêm return Backbone.sync..là nhiều Backbonish.
yves amsellem

Tôi nghĩ - đối với loại - cập nhật sẽ tốt hơn là tạo ... Btw. bạn có thể ghi đè toJSON của mô hình hoặc bộ sưu tập, vì vậy bạn có thể điều chỉnh những gì cần gửi đến máy chủ ... (thường chỉ là thuộc tính id cần thiết) Trong Backbone.Relational, bạn cũng có thể đặt những gì cần thêm vào định dạng json.
inf3rno

1
Backbone.sync đang mong đợi "tạo", xem backbonejs.org/docs/backbone.html#section-141
hacklikecrack

8

Tôi đã kết thúc chỉ có một phương thức 'lưu' like và được gọi là $ .ajax bên trong nó. Nó cho tôi nhiều quyền kiểm soát hơn mà không cần thêm lớp wrapper như @brandgonesurfing đã đề xuất (mặc dù tôi thực sự thích ý tưởng này :) Như đã đề cập vì tôi đã có phương thức collection.toJSON () ghi đè tất cả những gì tôi bắt đầu làm là sử dụng nó trong cuộc gọi ajax ...

Hy vọng điều này sẽ giúp những người tình cờ gặp nó ...


3
Bạn sẽ khấm khá hơn gọi Backbone.ajax (nó proxy để jQuery dù sao, nhưng nó làm cho nó dễ bảo trì hơn)
developerbmw

5

Điều này thực sự phụ thuộc vào hợp đồng giữa máy khách và máy chủ. Dưới đây là một ví dụ CoffeeScript được đơn giản hóa trong đó PUT /parent/:parent_id/childrenvới {"children":[{child1},{child2}]}sẽ thay thế con cái của cha mẹ bằng những gì trong PUT và trả về {"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

Đây là một ví dụ khá đơn giản, bạn có thể làm được nhiều việc hơn nữa ... nó thực sự phụ thuộc vào trạng thái dữ liệu của bạn khi save () được thực thi, trạng thái nó cần ở trạng thái nào để gửi đến máy chủ và máy chủ cung cấp thông tin gì trở lại.

Nếu máy chủ của bạn ổn với PUT [{child1},{child2], thì dòng Backbone.sync của bạn có thể thay đổi thành response = Backbone.sync('update', @toJSON(), url: @url(), contentType: 'application/json').


Thuộc tính của tùy chọn "url" là không cần thiết ở đây =)
Sergey Kamardin

5

Câu trả lời phụ thuộc vào những gì bạn muốn làm với bộ sưu tập ở phía máy chủ.

Nếu bạn phải gửi thêm dữ liệu với bài đăng, bạn có thể cần mô hình trình bao bọc hoặc mô hình quan hệ .

Với mô hình trình bao bọc, bạn luôn phải viết phương thức phân tích cú pháp của riêng mình :

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

Tôi nghĩ các mô hình quan hệ tốt hơn, vì bạn có thể định cấu hình chúng dễ dàng hơn và bạn có thể điều chỉnh vớitùy chọn includeInJSON các thuộc tính nào cần đưa vào json mà bạn gửi đến dịch vụ nghỉ ngơi của mình.

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

Nếu bạn không gửi thêm dữ liệu , bạn có thể đồng bộ hóa chính bộ sưu tập . Bạn phải thêm một phương thức lưu vào bộ sưu tập của mình (hoặc nguyên mẫu bộ sưu tập) trong trường hợp đó:

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

3

Tôi cũng ngạc nhiên rằng các bộ sưu tập Backbone không có phần mềm lưu tích hợp. Đây là những gì tôi đưa vào bộ sưu tập xương sống của mình để làm điều đó. Tôi chắc chắn không muốn lặp lại từng mô hình trong bộ sưu tập và lưu một cách độc lập. Ngoài ra, tôi đang sử dụng Backbone trên chương trình phụ trợ bằng cách sử dụng Node, vì vậy tôi đang ghi đè bản gốc Backbone.syncđể lưu vào một tệp phẳng trong dự án nhỏ của mình — nhưng mã sẽ khá giống nhau:

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

Làm cho tinh thần quá để chỉ cần vượt qua tùy chọn trong, giống nhưsave: function (options) { Backbone.sync('save', this, options); }
Matt Fletcher

3

Chủ đề cũ mà tôi biết, những gì tôi đã làm như sau:

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

Khá căng về phía trước :)


2

Đây là một ví dụ đơn giản:

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

Khi bạn gọi phương thức save () trên bộ sưu tập của mình, nó sẽ gửi một yêu cầu phương thức PUT đến URL đã xác định.


Tôi tự hỏi liệu bạn có thể giúp giải quyết vấn đề lưu bộ sưu tập của tôi không, ngay bây giờ jsfiddle.net/kyllle/f1h4cz7f/3 toJSON () cập nhật từng kiểu máy nhưng sau đó lưu dường như không có bất kỳ dữ liệu nào?
máy tạo kiểu tóc

1

Tôi sẽ thử một cái gì đó như:

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

Câu trả lời được chấp nhận là khá tốt, nhưng tôi có thể tiến thêm một bước nữa và cung cấp cho bạn mã sẽ đảm bảo các sự kiện thích hợp được kích hoạt cho người nghe của bạn đồng thời cho phép bạn chuyển các lệnh gọi lại sự kiện 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

Đối với bất kỳ ai vẫn đang sử dụng backbone.js vào năm 2017, câu trả lời được chấp nhận là không hoạt động.

Thử xóa ghi đè toJSON () trong mô hình trình bao bọc và gọi toJSON trên tập hợp khi bạn khởi tạo trình bao bọc mô hình.

new ModelWrapper(Collection.toJSON());
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.