2012-01-27 29 views
8

Đây là một ví dụ giả tạo, nhưng tôi tin rằng nó sẽ vượt qua được.Backbone.js - Cách tạo một bộ sưu tập có các thuộc tính có thể ràng buộc

Cho phép nói rằng tôi có bộ sưu tập xe xương sống. Và đối với bộ sưu tập tôi muốn một tài sản được gọi là isValid. Tôi muốn các đối tượng khác có thể liên kết với isValid và kích hoạt một hàm khi isValid thay đổi. Các bộ sưu tập isValid tài sản sẽ được thay đổi dựa trên các mô hình trong một bộ sưu tập như một toàn thể.

Ví dụ: nếu tất cả các cửa bị khóa trên mọi xe, thì isValid sẽ thay đổi thành true. Khi isValid thay đổi, tất cả các hàm được ràng buộc với sự kiện thay đổi isValid sẽ được kích hoạt.

Câu hỏi của tôi là, làm cách nào để tạo bộ sưu tập có thuộc tính có thể ràng buộc hoạt động tương tự như thuộc tính mô hình?

Đây là mã mà tôi muốn làm việc.

var Car = Backbone.Model.extend({}); 
var Cars = Backbone.Collection.extend({ 
    model: Car, 
    url: "Cars", 
    isValid: false, //Here's the property that I want bindable 
        //but do not know how to accomplish this. 
}); 

var cars = new Cars(); 

//Call a function when cars.isValid changes 
cars.bind("change:isValid", carsIsValidChanged, cars) 

//Not sure if this what the function would look like 
//but would need access to cars.isValid or just the car collection 
function carsIsValidChanged(isValid){ 
    console.log(isValid); 
} 


Cập nhật

Xem giải pháp hoàn chỉnh của tôi như là một câu trả lời dưới đây.

Trả lời

5

Vì vậy, không có cách 'tích hợp' để phản hồi các thay đổi thuộc tính trên Bộ sưu tập, vì thực sự không phải là cách được hỗ trợ (mà tôi biết) có thuộc tính trên bộ sưu tập. Tuy nhiên, nó vẫn hoàn toàn có thể như thế này, tôi nghĩ. (chưa được kiểm tra, nhưng phải khá gần)

var Car = Backbone.Model.extend({}); 
var Cars = Backbone.Collection.extend({ 
    model: Car, 
    url: "Cars", 
    initialize: function() { 
     var self = this; 
     this.isValid = false; 
     this.bind('add', function() { 
      var curValid = true; 
      self.each(function(car) { 
      if(car.get('is_locked') != true) { 
       curValid = false; 
      } 
      }); 
      if(curValid != self.isValid) { 
      self.isValid = curValid; 
      self.trigger('change:isValid', self.isValid, self); 
      } 
     }); 
    }     
}); 

// instantiate a new Cars collection 
var cars = new Cars(); 


function carsIsValidChanged(isValid){ 
    console.log(isValid); 
} 

//Call a function when cars.isValid changes 
cars.bind("change:isValid", carsIsValidChanged) 

Một lưu ý: bạn không muốn isValid: được liệt kê là thuộc tính như bạn sẽ tạo mô hình hoặc url. Điều này dường như làm cho xương sống trở nên kỳ lạ, và isValid của bạn có thể kéo dài tất cả các trường hợp của bộ sưu tập. Nó tốt hơn để xác định chúng như là một initializer, sau đó mỗi khi bạn nhanh chóng mà bộ sưu tập bạn sẽ đúng có một thể hiện của isValid truy cập bởi this.isValid.

+0

Cảm ơn câu trả lời. Tôi tin rằng bạn đã chỉ cho tôi theo đúng hướng. Đánh giá cao thông tin đó làValid nên được tạo trong khởi tạo. Khi tôi kết thúc với giải pháp làm việc của mình, tôi sẽ đăng nó. –

2

Đây là giải pháp hoàn chỉnh của tôi cho câu hỏi này: jsFiddle Complete example

@spotmat đưa cho tôi định hướng đúng, nhưng tôi cần phải thêm chức năng bổ sung. Dưới đây là một vài vật phẩm cần thiết để được giải quyết:

  • Collection contructor cần để xử lý dữ liệu pased để nó
  • Khi một mô hình sở hữu IsLocked được cập nhật các bộ sưu tập cần phải được tái xác nhận

tôi 'không chắc chắn nếu thêm thuộc tính ràng buộc vào một bộ sưu tập là một ý tưởng tuyệt vời, nhưng là một cái gì đó thú vị để tìm ra, và tôi đã học được rất nhiều.

var log = {};//this is for debugging 
_.extend(log, Backbone.Events); 

var Car = Backbone.Model.extend({}); 

var Cars = Backbone.Collection.extend({ 
    model: Car, 
    url: "scripts/data/Cars.json", 
initialize: function() { 
    var _this = this; 

    this.isValid = false; //user should bind to "change:isValid" 
    this._isPending = false; //This is so that 

    this.bind("add", this.modelAdded, this); 
    this.bind("reset", this.modelsAdded, this); 

    if (this.length > 0) { 
     //Model passed in though the constructor will not be binded 
     //so call modelsAdded 
     this.modelsAdded(this); 
    } 
}, 

modelsAdded: function (collection) { 
    this._isPending = true 
    log.trigger("log", ["modelsAdded: " + collection.length]); 
    collection.each(this.modelAdded, this); //Do nut remove "this" param. It need when modelAdded gets called 
    this._isPending = false 
    this._validate(); 
}, 

modelAdded: function (model) { 
    log.trigger("log", ["modelAdded: " + model.get("Model") + "; IsLocked: " + model.get("IsLocked")]); 
    //!!!for each model added to the colleciton bind 
    //its IsLocked property to the collection modelIsLockedChange function 
    model.bind("change:IsLocked", this.modelIsLockedChanged, this); 
    if (this._isPending == false) { 
     this._validate(); 
    } 
}, 

modelIsLockedChanged: function (model) { 
    log.trigger("log", ["modelIsLockedChanged:" + model.get("Model") + "; IsLocked: " + model.get("IsLocked")]); 
    this._validate(); 
}, 

_validate: function() { 
    var isValid = true; 
    this.each(function (model) { 
     if (model.get("IsLocked") == false) { 
      isValid = false 
     } 
    }); 

    if (this.isValid != isValid) { 
     this.isValid = isValid 
     cars.trigger("change:isValid", [this.isValid]) 
    } 
    }, 
}); 

Quan điểm này là dành cho mục đích gỡ lỗi chỉ

var Logger = Backbone.View.extend({ 
    el: $("#output"), 

    initialize: function(){ 
     log.bind("log", this.logMessage, this) 
    }, 

    render: function(){ 
     return $(this.el).html(""); 
    }, 

    logMessage: function(message){ 
     $(this.el).find("ul").append("<li>" + message[0] + "</li>"); 
    } 
}) 

Các mã sau đây được sử dụng để kiểm tra bộ sưu tập

var logger = new Logger(); 
log.bind("log", function(message){ 
    console.log(message[0]); 
}); 

var carsData = [ 
    { "Model": "Chevy Camero", "Year": 1982, "IsLocked": true }, 
    { "Model": "Ford F-150", "Year": 2011, "IsLocked": false } 
] 

var cars = new Cars(carsData); 
cars.bind("change:isValid", function(isValid){ 
    log.trigger("log", ["change:isValid: " + isValid]); 
}); 

cars.add({ "Model": "Toyota Tacoma", "Year": 2006, "IsLocked": true }); 
cars.at(1).set({ "IsLocked": true }); 
5

Bạn có thể sử dụng một cái gì đó như thế này;

Backbone.Collection.prototype.bindableProperty = function (key, val) { 
    var self = this; 
    Object.defineProperty(this, key, { 
     configurable: false, 
     get: function() { return val; }, 
     set: function (v) { 
      val = v; 
      self.trigger('change:'+key, val) 
     } 
    }); 
}; 

Chỉ cần làm điều này để thêm thuộc tính;

var Cars = Backbone.Collection.extend({ 
    model: Car, 
    url: "Cars", 
    initialize: function() { 
    this.bindableProperty('isValid', false); 
    } 
}); 

Cảnh báo; các phiên bản cũ của IE không hỗ trợ Object.defineProperty.

+0

Tôi sẽ thử buổi tối này. –

+1

Cảm ơn Stephen Belanger đã trả lời. Các giải pháp mà bạn cung cấp có lẽ là sạch nhất, nhưng như bạn đã đề cập nó không hoạt động với IE8. Mặc dù tôi đã phải viết thêm một chút mã để có được ví dụ của tôi để làm việc với đề xuất của bạn, tôi không còn cần phải tự gọi mã sau đây, điều này rất hay: 'cars.trigger (" change: isValid " , [this.isValid]) ' Bây giờ bất cứ khi nào thuộc tính isValid thay đổi, sự kiện sẽ tự động được kích hoạt. [Ví dụ jsFiddle] (http://jsfiddle.net/BarDev/ae63d/) –

+0

Dành cho bất kỳ ai có thể đọc điều này trong tương lai; nó làm việc trong IE9 +, và một phần trong IE8, như được giải thích ở đây; https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/defineProperty#ie8-specific –

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