Over a million developers have joined DZone.

Introducing Backbone.ComputedFields

· Web Dev Zone

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

Recently, I've been working on small project which I want to share here. It's called Backbone.ComputedFields and it's small plugin that extends Backbone.Model functionality a bit.

I needed to have a model with 'virtual' fields. Namely, fields that does not belong to model directly, but being computed based on some other fields values.

The easiest solution would be simply introduce some model methods, say model.getComputedField() / model.setComputedField() and store the value inside the model object. But that turns out to be bad idea, for several reasons. First, we are breaking usual Backbone interface for getting and setting values - model.get('computed') / model.set('computed', 100). Also, if model is binded to a view, we are responsible for raising events manually, in case of computed or depended field changing.

So, after few iterations Backbone.ComputedFields was born. The design goals: to be simple, to be declarative, to be friendly to model binding (read, respect the events).

Use cases

Typical use cases for Backbone.ComputedFields are: calculating the prices; concatenating several fields; encapsulating the logic of retrieving object by reference.

It's fairly important, that computed field could change. Based on it's value, dependent fields should be updated.

Examples

Let's take a look on few examples. The models here are very simplified. But, it shows the main application of Backbone.ComputedFields.

Calculating prices

The model represents the product, which contains net price and VAT rate.

var Produc = Backbone.Model.extend({
    initialize: function () {
        this.computedFields = new Backbone.ComputedFields(this);
    },
 
    computed: {
        grossPrice: {
            depends: ['netPrice', 'vatRate'],
            get: function (fields) {
                return fields.netPrice * (1 + fields.vatRate / 100);
            },
            set: function (value, fields) {
                fields.netPrice = value / (1 + fields.vatRate / 100);
            }
        }
    }
});

So, we have grossPrice as computed field. That field depends on 'netPrice' and 'vatRate' and being calculated by simple formulas.

var product = new Product({ netPrice: 100, vatRate: 20 });
var grossPrice = product.get('grossPrice');


In this case, gross price would be 120.
product.set({grossPrice: 300});
var netPrice = product.get('netPrice');

After gross price is update, netPrice will be recalculated and netPrice will be 250.

Concatenating fields

Let's have a model to represent the person with first name and last name.

var Person = Backbone.Model.extend({
    initialize: function () {
        this.computedFields = new Backbone.ComputedFields(this);
    },
 
    computed: {
        fullName: {
            depends: ['firstName', 'lastName'],
            get: function (fields) {
                return fields.firstName + ' ' + fields.lastName;
            }
        }
    }
});

I'm skipping the setter cause we don't need to set full name here.

var person = new Person({firstName: 'Alexander', lastName: 'Beletsky'});
var fullName = person.get('fullName');

Full name 'Alexander Beletsky' is returned here.

Referenced objects

Sometimes we have a models that only contains a reference to another model. All the time, we need to get referenced object we have to create some piece of code, which it typically copy-n-pasted thought the code base. Computed field could be a good idea to encapsulate that.

var Invoice = Backbone.Model.extend({
    initialize: function (attrs, options) {
        this.customers = options.customers;
        this.computedFields = new Backbone.ComputedFields(this);
    },
 
    computed: {
        customer: {
            depends: ['customerId'],
            get: function (fields) {
                return fields.customerId && this.customers.get(fields.customerId);
            },
            set: function(customer, fields) {
                fields.customerId = customer.get('id');
            }
        }
    }
});

Here we have customer field, which is computed. 

So, I'm able to get customer model even if invoice is just holding the invoice Id.

invoice.set({customer: anotherCustomer});
var customerId = invoice.get('customerId');

If I'm changing the customer of invoice, the 'customerId' would be initialized with id of 'anotherCustomer'.

Conclusions

Backbone.ComputedFields is still pre-mature, but I already successfully used that in one of projects. The github page contains pretty much documentation, so you should have to problems of adopting it for personal needs.


Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:

Published at DZone with permission of Alexander Beletsky, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}