Service Encapsulation

  • page
jquery.model.encapsulate
 

Models encapsulate your application's raw data. This promotes reuse and provide a standard interface for widgets to talk to services.

The majority of the time, the raw data comes from services your server provides. For example, if you make a request to:

GET /contacts.json

The server might return something like:

[{
  'id': 1,
  'name' : 'Justin Meyer',
  'birthday': '1982-10-20'
},
{
  'id': 2,
  'name' : 'Brian Moschel',
  'birthday': '1983-11-10'
}]

In most jQuery code, you'll see something like the following to retrieve contacts data:

$.get('/contacts.json',
      {type: 'tasty'}, 
      successCallback,
      'json')

Instead, model encapsulates (wraps) this request so you call it like:

Contact.findAll({type: 'old'}, successCallback);

And instead of raw data, findAll returns contact instances that let you do things like:

// destroy the contact
contact.destroy() 

// update the contact
contact.update({name: "Jeremy"})

// create a contact
new Contact({name: "Alex"}).save();

Encapsulation Demo

The Grid demo shows using two different models with the same widget.

How to Encapsulate

Think of models as a contract for creating, reading, updating, and deleting data.

By filling out a model, you can pass that model to a widget and the widget will use the model as a proxy for your data.

The following chart shows the methods most models provide:

Create
Contact.create(attrs, success, error)
Read
Contact.findAll(params,success,error)
Contact.findOne(params, success, error)
Update
Contact.update(id, attrs, success, error)
Delete
Contact.destroy(id, success, error)

By filling out these methods, you get the benefits of encapsulation, AND all the other magic Model provides.

There are two ways to fill out these methods:

  • providing templated service urls
  • implementing the method

Using Templated Service URLS

If your server is REST-ish, you can simply provide urls to your services.

The following shows filling out a Task model's urls. For each method it shows calling the function, how the service request is made, and what the server's response should look like:

$.Model("Task",{

  // Task.findAll({foo: "bar"})
  // -> GET /tasks.json?foo=bar
  // <- [{id: 1, name: "foo"},{ ... }]
  findAll : "/tasks.json",    

  // Task.findOne({id: 5})
  // -> GET /tasks/5.json
  findOne : "/tasks/{id}.json", 

  // new Task({name: 'justin'}).save()
  // -> POST /tasks.json id=5
  // <- {id : 5}
  create : "/tasks.json",

  // task.update({name: 'justin'})
  // -> PUT /tasks/5.json name=justin
  // <- {}
  update : "/tasks/{id}.json",

  // task.destroy()
  // -> DESTROY /tasks/5.json
  // <- {}
  destroy : "/tasks/{id}.json"
},{})

You can change the HTTP request type by putting a GET, POST, DELETE, PUT like:

$.Model("Todo",{
  destroy: "POST /task/delete/{id}.json
},{})

Note: Even if your server doesn't respond with service data in the same way, it's likely that $.Model will be able to figure it out. If not, you can probably overwrite [jQuery.Model.static.models models] or [jQuery.Model.static.model model]. If that doesn't work, you can always implement it yourself.

Implement Service Methods

If providing a url doesn't work for you, you might need to fill out the service method yourself. Before doing this, it's good to have an understanding of jQuery's Ajax converters and deferreds.

Lets see how we might fill out the Contact.findAll function to work with JSONP:

$.Model('Contact',
{
  findAll : function(params, success, error){

    // do the ajax request
    return $.get('/contacts.jsonp',
      params, 
      success,
      'jsonp contact.models');
  }
},
{
  // Prototype properties of Contact.
  // We'll learn about this soon!
});

If we wanted to make a list of contacts, we could do it like:

Contact.findAll({},function(contacts){
  var html = [];
  for(var i =0; i < contacts.length; i++){
    html.push('<li>'+contacts[i].name + '</li>')
  }
  $('#contacts').html( html.join('') );
});