2012-03-06 44 views
6

Có cách nào đơn giản để chèn một mục mô hình mới vào giữa một backbone.js Collection và sau đó cập nhật bộ sưu tập của View để bao gồm mục mới ở đúng vị trí không?Chèn một mục vào bộ sưu tập backbone.js

Tôi đang làm việc trên một điều khiển để thêm/xóa các mục khỏi danh sách. Mỗi mục danh sách có ModelView riêng của mình và tôi cũng có một số View cho toàn bộ bộ sưu tập.

Mỗi chế độ xem mục có nút Duplicate sao chép mô hình của mục và chèn vào bộ sưu tập ở vị trí chỉ mục bên dưới mục được nhấp.

Chèn mục vào bộ sưu tập rất đơn giản, nhưng tôi đang gặp khó khăn trong việc tìm cách cập nhật chế độ xem bộ sưu tập. Tôi đã thử một cái gì đó như thế này:

ListView = Backbone.View.extend({ 
    el: '#list-rows', 
    initialize: function() { 
     _.bindAll(this); 
     this.collection = new Items(); 
     this.collection.bind('add', this.addItem); 
     this.render(); 
    }, 
    render: function() { 
     this.collection.each(this.addItems); 
     return this; 
    }, 
    addItem: function (item) { 
     var itemView = new ItemView({ model: item }), 
      rendered = itemView.render().el, 
      index = this.collection.indexOf(item), 
      rows = $('.item-row'); 

     if (rows.length > 1) { 
     $(rows[index - 1]).after(rendered); 
     } else { 
     this.$el.append(rendered); 
     } 
    } 
} 

Triển khai này hoạt động nhưng tôi gặp phải lỗi lạ khi thêm một mục mới. Tôi chắc chắn rằng tôi có thể sắp xếp những người đó, nhưng ...

Có một giọng nói trong đầu tôi nói với tôi rằng có một cách tốt hơn để làm điều này. Có phải tự tìm ra nơi để chèn một ItemView mới có vẻ thực sự hacky - không nên xem bộ sưu tập biết làm thế nào để rerender bộ sưu tập đã?

Mọi đề xuất?

Trả lời

4

Cách thông thường tôi đang làm là để ListView hiển thị mỗi ItemView trong chức năng render. Sau đó, tôi chỉ ràng buộc các sự kiện add đến render chức năng, như thế này:

ListView = Backbone.View.extend({ 
    el: '#list-rows' 
    , initialize: function() { 
    _.bindAll(this); 
    this.collection = new Items(); 
    this.collection.bind('add', this.render); 
    this.render(); 
    } 
    , render: function() { 
    this.$el.empty(); 
    var self = this; 
    this.collection.each(function(item) { 
     self.$el.append(new ItemView({ model: item }).render().el); 
    }); 
    return this; 
    } 
} 

Mỗi lần bạn gọi this.collection.add(someModel, {at: index}), quan điểm sẽ được tái rendered cho phù hợp.

+0

Tôi đã phải thêm một cuộc gọi đến '(.item-row) .remove()' bên trong phương thức 'render' để làm việc này, nhưng đó là một giải pháp tốt. (Nếu không, các mục được hiển thị lại sẽ được thêm vào cuối các mục được hiển thị hiện có.) Cảm ơn bạn đã trợ giúp! –

+0

Ồ, bạn chỉ có thể làm 'điều này. $ El.empty()' để xóa '$ el' trước khi lặp qua bộ sưu tập :) – sntran

+0

Đó chính xác là cách tôi thêm các mục vào bộ sưu tập hiện tại. Nó đặt mục mới vào đúng vị trí trong bộ sưu tập.Tôi nhận thấy rằng tôi có một vài lỗi chính tả trong bình luận của mình ở trên: Tôi đang gọi '$ ('. Item-row'). Remove()' để xóa các mục đã được tạo lần cuối khi bộ sưu tập được hiển thị. Nó chỉ đặt lại chế độ xem. Hãy nghĩ về nó, có một phương pháp 'reset' có thể làm những gì tôi muốn. Tôi sẽ thử gọi nó và sau đó gọi 'render'. –

9

Tôi không nghĩ rằng việc hiển thị lại toàn bộ bộ sưu tập khi thêm phần tử mới là giải pháp tốt nhất. Nó chậm hơn chèn mục mới ở đúng vị trí, đặc biệt nếu danh sách dài.

Ngoài ra, hãy xem xét kịch bản sau đây. Bạn tải một vài mục trong bộ sưu tập của mình và sau đó bạn thêm n mục khác (giả sử người dùng nhấp vào nút "tải thêm"). Để thực hiện điều này, bạn sẽ gọi phương thức fetch() qua add: true làm một trong các tùy chọn. Khi dữ liệu được nhận lại từ máy chủ, sự kiện 'thêm' được kích hoạt n lần và bạn sẽ kết thúc lại hiển thị danh sách của mình n lần.

Tôi đang thực sự sử dụng một biến thể của mã trong câu hỏi của bạn, đây là callback của tôi vào sự kiện 'thêm':

var view, prev, prev_index; 
view = new ItemView({ model: new_item }).render().el; 
prev_index = self.model.indexOf(new_item) - 1; 
prev = self.$el.find('li:eq(' + prev_index + ')'); 
if (prev.length > 0) { 
    prev.after(view); 
} else { 
    self.$el.prepend(view); 
} 

Vì vậy, về cơ bản tôi chỉ sử dụng jQuery selector :eq() thay vì nhận được tất cả các yếu tố như bạn làm, nên sử dụng ít bộ nhớ hơn.

+0

Công trình này tuyệt vời, nhưng tôi đang hiển thị số bên cạnh mỗi mục trong danh sách, vì vậy tôi cần phải hiển thị lại để giữ cho các số được đồng bộ hóa. Không thể nhìn thấy bất kỳ cách nào xung quanh đó. Rất tiếc .... Tôi thích sự đơn giản ở đây. – byron

Các vấn đề liên quan