-
Notifications
You must be signed in to change notification settings - Fork 1
models
@page Models Models & Retrieving Data @parent Tutorial 2
@body
Models are Observes that connect to a RESTful interface. They come with a set of
methods designed to make it easy to manage changes remotely. To create a Model
class, call can.Model and supply it with specific static properties that tell
it how to interact with the server, along with any instance properties or helper
methods the Model may need. The important static properties are:
- findAll, which describes how to get a group of items.
- findOne, which describes how to get a specific item.
- create, which describes how to save a new item.
- update, which describes how to update an existing item.
- destroy, which describes how to delete an item.
When accessing a straightforward RESTful API, creating a Model class and an instance of that Model class might be as simple as this:
@codestart var Todo = can.Model({ findAll: 'GET /todos', findOne: 'GET /todos/{id}', create: 'POST /todos', update: 'PUT /todos/{id}', destroy: 'DELETE /todos/{id}' }, {});
var dishesTask = new Todo({description: 'Do the dishes.'}); @codeend
Because Models are Observes, don't forget to set all your properties with attr.
By supplying the findAll, findOne, create, update, and destroy
properties, you show a Model class how to communicate with the server. You can
call findAll and findOne on the Model class to retrieve Models and save
and destroy on Models to create, update, and delete them.
can.Model.findAll retrieves a group of Models by making a call to a server.
Here's how you call findAll on our Todo class above:
@codestart Todo.findAll({}, function(todos) { // todos is a can.Model.List of Todo Models. }, function(xhr) { // handle errors }); @codeend
This will make a GET request to /todos, which should return JSON that looks
similar to:
@codestart { "data": [ {"id":1, "description":"Do the dishes."}, {"id":2, "description":"Mow the lawn."}, {"id":3, "description":"Finish the laundry."} ] } @codeend
(findAll will also accept an array from the service, but you probably should not be returning an array from a JSON service.)
When the service has returned, findAll will massage the data into Model
instances, put them in a can.Model.List (which is like a can.Observe.List for
Models), and pass the List to the callback in its second parameter. If there was
an error, findAll will call the callback in its third parameter and pass it the
XmlHttpRequest object used to make the call.
findAll returns a can.Deferred that will resolve to a Model
List of items if the call succeeds and rejects to the XmlHttpRequest object if
there is an error.
can.Model.findOne works similarly to findAll, except that it retrieves a
single Model from a server:
@codestart Todo.findOne({id: 1}, function(todo) { // todo is an instance of Todo }); @codeend
This makes a GET request to /todos/1, which should return JSON that looks
similar to:
@codestart { "id":1, "description":"Do the dishes" } @codeend
findOne returns a Deferred that resolves to the Todo if the call succeeds and
rejects to the XmlHttpRequest object if there is an error.
You can call save on a Model instance to save it
back to the server. If the Model has an id, it will be updated using the
function specified under update. Otherwise, can.Model assumes the Model is new
and creates the item on the server using the function in create.
Either way, the callback in the first parameter will be called on a successful
save with the updated Model; if an error occurs, the callback in the second
parameter will be called with the XmlHttpRequest object. Like findAll, save
returns a Deferred that resolves to the updated Model on success and rejects to
the XmlHttpRequest object on failure.
@codestart var shopping = new Todo({description: "Go grocery shopping."}); shopping.save(function(saved) { // saved is the saved Todo saved.attr('description', 'Remember the milk.'); saved.save(); }); @codeend
In the code above, the first time shopping.save() is called, can.Model will
make a POST request to /todos with a request body of description=Go grocery shopping.. When the response comes back, it should have an id
(say, 5) and that id property will be reflected in todo.
The second time saved.save() is called, saved has an id, so can.Model
will make a PUT request to /todos/5 with a request body of
description=Remember the milk..
When you need to delete a Model's counterpart on the server, just call destroy
on the Model, passing it success and error handlers just like save, except
that the success handler will be passed the Model that has been deleted.
destroy also retuns a Deferred, which resolves to the deleted Model and
rejects to the XmlHttpRequest object.
@codestart var cats = new Todo({description: "Feed the cats."}); cats.save(function(saved) { saved.destroy(function(destroyed) { // destroyed is the Todo that was destroyed }); }); @codeend
When destroy is called in the above code, can.Model makes a DELETE request
to /todos/6.
Because Models are Observes, you can bind to the same events as on any other Observe. In addition to those events, Models emit three new kinds of events:
- created, when an instance is created on the server.
- updated, when an instance is updated on the server.
- destroyed, when an instance is destroyed on the server.
For example, here is how you listen for an instance being created on the server:
@codestart var mop = new Todo({description: 'Mop the floor.'}); mop.bind('created', function(ev, created) { // created is the created Todo }); mop.save(); @codeend
You can also bind directly onto the Model class to listen for any time any instance is created, updated, or destroyed:
@codestart Todo.bind('created', function(ev, created) { // created is the created Todo }); @codeend
Model Lists (provided by can.Model.List) are Lists whose items are Models.
When one of a Model List's elements are destroyed, that element is removed from
the list.
@codestart Todo.findAll({}, function(todos) { todos.length; // 5 todos[0].destroy(function() { todos.length; // 4 } }); @codeend