diff --git a/README.md b/README.md index f4df662..0a31521 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,20 @@ computed: { } ``` +`computed` can also be a function returning an object. + +```js +computed: function() { + return { + grossPrice: { + get: function () { + return 105; + } + } + }; +} +``` + Each property that declares `get` or `set` method is treated as computed. Get the value of computed property, diff --git a/src/backbone.computedfields.js b/src/backbone.computedfields.js index 175b09f..bb81c04 100644 --- a/src/backbone.computedfields.js +++ b/src/backbone.computedfields.js @@ -1,5 +1,14 @@ Backbone.ComputedFields = (function(Backbone, _){ + var _isFunction = function(obj) { + // == instead of === and || false are optimizations + // to go around nasty bugs in IE11, Safari 8 and old v8 + // see underscore#isFunction + /* jshint eqeqeq: false */ + return typeof obj == 'function' || false; + /* jshint eqeqeq: true */ + }; + var ComputedFields = function (model) { this.model = model; this._computedFields = []; @@ -29,8 +38,10 @@ Backbone.ComputedFields = (function(Backbone, _){ }, _lookUpComputedFields: function () { - for (var obj in this.model.computed) { - var field = this.model.computed[obj]; + var computed = _isFunction(this.model.computed) ? this.model.computed() : this.model.computed; + + for (var obj in computed) { + var field = computed[obj]; if (field && (field.set || field.get)) { this._computedFields.push({name: obj, field: field}); diff --git a/test/spec/backbone.computedfields.spec.js b/test/spec/backbone.computedfields.spec.js index 8cac582..c6a165b 100644 --- a/test/spec/backbone.computedfields.spec.js +++ b/test/spec/backbone.computedfields.spec.js @@ -59,6 +59,69 @@ describe('Backbone.ComputedFields spec', function() { }); + describe('when ComputedFields initialized and computed is a function', function () { + + var model; + + beforeEach(function () { + var Model = Backbone.Model.extend({ + initialize: function () { + this.computedFields = new Backbone.ComputedFields(this); + }, + + computed: function() { + return { + grossPrice: { + get: function () { + return 100; + } + } + }; + } + }); + + model = new Model({ netPrice: 100, vatRate: 5}); + }); + + it ('should be initialized', function () { + expect(model.computedFields).to.exist; + expect(model.computedFields._computedFields.length).to.equal(1); + }); + + it ('should access model attributes', function () { + expect(model.get('netPrice')).to.equal(100); + expect(model.get('vatRate')).to.equal(5); + }); + + describe('when initialize with empty', function () { + beforeEach(function () { + var Model = Backbone.Model.extend({ + initialize: function () { + this.getHasBeenCalled = false; + this.computedFields = new Backbone.ComputedFields(this); + }, + + computed: function() { + return { + grossPrice: { + get: function () { + this.getHasBeenCalled = true; + } + } + }; + } + }); + + model = new Model(); + }); + + it ('should not call computed field getter', function () { + expect(model.getHasBeenCalled).to.equal(false); + }); + }); + + }); + describe('when ComputedFields are used', function () { beforeEach(function () { var Model = Backbone.Model.extend({ @@ -587,4 +650,4 @@ describe('Backbone.ComputedFields spec', function() { }); -}); \ No newline at end of file +});