The first thing to do is to create a model class using the factory Model()
.
var Project = Model("project")
This allows you to create instances of "project" models and also contains an internal collection of all "projects" which can be used for querying.
Now you can create and manipulate instances of your new model. Attributes are read and set with the attr()
method which works in a similar way to jQuery on the DOM.
var project = new Project({ id: 1, title: "stuff" })
project.attr("title", "nonsense")
project.save()
After calling save()
on a model it is added to the class's "collection" and can be retrieved again by calling find()
(or first()
as it is the first model in the collection).
Project.find(1)
// => project
Project.first()
// => project
You can retrieve all models from the collection with all()
.
Project.all()
// => [project]
For more ways to query the collection check out detect()
and select()
.
You might need to give your model custom methods and properties. There are two parts to a model which can be extended, and these are akin to class and instance methods on an ORM such as ActiveRecord.
When creating a model you can pass a function as the optional second argument and "extend" the class by adding methods to it.
var Project = Model("project", function() {
this.extend({
find_by_title: function(title) {
return this.detect(function() {
return this.attr("title") == title
})
}
})
})
Project.find_by_title("stuff")
// => "stuff" project model
You can also "include" instance methods on the model's prototype. These are often used to link objects together in a way that mimics the relationships the data might have in the remote database ("has many" etc). However, they can be pretty much anything and can overwrite the defaults.
var Project = Model("project", function() {
this.include({
markAsDone: function() {
this.attr("done", true)
}
})
})
Project.find(1).markAsDone()
// "stuff" project marked as done
Simple associations can be mimicked by adding a couple of instance methods. Here a Cat
"belongs to" a Mat
and a Mat
"has many" Cat
s.
var Cat = Model("cat", function() {
this.include({
mat: function() {
var mat_id = this.attr("mat_id")
return Mat.detect(function() {
return this.id() == mat_id
})
}
})
})
var Mat = Model("mat", function() {
this.include({
cats: function() {
var id = this.id()
return Cat.select(function() {
return this.attr("mat_id") == id
})
}
})
})
js-model allows you to listen to the lifecycle of objects based on the events they trigger at different points. Typically you'll use this to link your data objects to UI elements.
It is possible to bind to an event occurring when adding and removing an object to a collection.
Post.bind("add", function(new_object) {
add_object_to_ui(new_object)
})
Post.bind("remove", function(removed_object) {
remove_object_from_ui(removed_object)
})
Parts of your application can be bound to changes which happen to a specific instance:
post.bind("update", function() {
my_ui_elem.text(this.attr("name"))
})
Including when the instance is destroyed:
post.bind("remove", function() {
my_ui_elem.remove()
})
You might also want to have custom events on objects which might be linked up to a UI element.
post.bind("turn_blue", function() {
my_ui_elem.css("background", "blue")
})
post.trigger("turn_blue")
To add your own validations you should define a custom validate()
method on your model that adds error messages to the errors
object. valid()
is called on save()
and checks that there are no errors. Validations are useful when using localStorage persistence but can also help you avoid hitting your server unnecessarily if you're using REST persistence.