- Notifications
You must be signed in to change notification settings - Fork157
A lightweight model library for Ember.js
License
ebryn/ember-model
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Ember Model (EM) is a simple and lightweight model library for Ember. It intentionally supports a limited feature set. The main goal is to provide primitives on top of $.ajax that are required by Ember.
EM is still a work in progress, but it's flexible and stable enough to beused in production apps today. It was extracted out of an Ember app. Please see the issues section for a list of bugs and planned features.
ember install ember-model
bower install ember-model --save
Need more help getting started? Join us in #ember-model on Freenode.
- BYO$A (bring your own $.ajax)
- Relationships (hasMany/belongsTo)
- Focused on performance
- Automatic coalescing of multiple findById calls into a single findMany
- Fixtures
- Identity map (per class)
- Promises everywhere
- Customizable RESTAdapter
If you want more features than Ember Model provides, file an issue. Feature requests/contributions are welcome but the goal is to keep things simple and fast.
// app/models/user.jsimport{Model,attr,hasMany}from'ember-model';varUser=Model.extend({id:attr(),name:attr(),comments:hasMany("comment",{key:'comment_ids'})}).reopenClass({url:"/users"});exportdefaultUser;
// app/models/comment.jsimport{Model,attr,hasMany}from'ember-model';varComment=Model.extend({id:attr(),text:attr()}).reopenClass({url:"/comments"});exportdefaultComment;
// create examplevarnewUser=this.store.createRecord('user',{name:"Erik"});newUser.save();// POST to /users// hasMany examplevarcomments=newUser.get('comments');comments.create({text:"hello!"});comments.save();// POST to /comments// find & update examplethis.store.find('user',1).then(user=>{// GET /users/1user.set('name','Kris');user.get('isDirty');// => trueuser.save();// PUT /users/1});
varattr=Ember.attr,hasMany=Ember.hasMany;// Model definitionsApp.User=Ember.Model.extend({id:attr(),name:attr(),comments:hasMany("App.Comment",{key:'comment_ids'})});App.User.url="/users";App.User.adapter=Ember.RESTAdapter.create();App.Comment=Ember.Model.extend({id:attr(),text:attr()});App.Comment.url="/comments";App.Comment.adapter=Ember.RESTAdapter.create();// create examplevarnewUser=App.User.create({name:"Erik"});newUser.save();// POST to /users// hasMany examplevarcomments=newUser.get('comments');comments.create({text:"hello!"});comments.save();// POST to /comments// find & update examplevarexistingUser=App.User.find(1);// GET /users/1existingUser.set('name','Kris');existingUser.get('isDirty');// => trueexistingUser.save();// PUT /users/1
Store#find(<type>)
- find all records by type, returns a promise
Store#find(<type>, <id>)
- find by primary key (multiple calls within a single run loop can coalesce to a findMany), returns a promise
Store#find(<type>, <object>)
- find query - object gets passed directly to your adapter, returns a promise
Store#createRecord(<type>, <object>)
- create a new record
Model.create
- create a new record
Model#save
- save or update record
Model#deleteRecord
- delete a record
Model#load(<id>, <object>)
- load JSON into the record (typically used inside adapter definition)
Model#toJSON
- serialize the record to JSON
Model.find()
- find all records
Model.find(<String|Number>)
- find by primary key (multiple calls within a single run loop can coalesce to a findMany)
Model.find(<object>)
- find query - object gets passed directly to your adapter
Model.fetch()
- find all records, returns a promise
Model.fetch(<String|Number>)
- find by primary key (multiple calls within a single run loop can coalesce to a findMany), returns a promise
Model.fetch(<object>)
- find query - object gets passed directly to your adapter, returns a promise
Model.load(<array>)
- load an array of model data (typically used when preloading / sideloading data)
Ember.Adapter=Ember.Object.extend({find:function(record,id){},// find a single recordfindAll:function(klass,records){},// find all recordsfindMany:function(klass,records,ids){},// find many records by primary key (batch find)findQuery:function(klass,records,params){},// find records using a querycreateRecord:function(record){},// create a new record on the serversaveRecord:function(record){},// save an existing record on the serverdeleteRecord:function(record){}// delete a record on the server});
Attributes by default have no type and are not typecast from the representationprovided in the JSON format.
Ember Model has built inDate
andNumber
types. TheDate
type will deserializestrings into a javascript Date object, and will serialize dates intoISO 8601 format. TheNumber
type willcast into a numeric type on serialization and deserialization.
App.Post=Ember.Model.extend({date:attr(Date),comment_count:attr(Number)});
To provide custom attribute serialization and deserialization, create an object thathas serialize and deserialize functions, and pass it into the attr helper:
varTime={serialize:function(time){returntime.hour+":"+time.min;},deserialize:function(string){vararray=string.split(":");return{hour:parseInt(array[0],10),min:parseInt(array[1],10)};}};varPost=Ember.Model.extend({time:attr(Time)});
Attributes can have a default value.
App.Post=Ember.Model.extend({tags:attr(Array,{defaultValue:[]})});
Ember Model provides two types of relationshipshasMany
andbelongsTo
. Both types of relationships can either be embedded or referenced by ids.
Relationships are defined by using relationship computed property macros in place ofEmber.attr
. There are two macros available, one for each type of relationship.
Ember.belongsTo(type, options)
- Provides access to a single related object.
Ember.hasMany(type, options)
- Provides access to an array of related objects.
Both relationships take two arguments.
type
- Class of the related model or string representation (eg. App.Comment or 'App.Comment').options
- An object with two properties,key
which is required andembedded
which is optional and defaults tofalse
.key
- indicates what property of the JSON backing the model will be accessed to access the relationshipembedded
- Iftrue
the related objects are expected to be present in the data backing the model. Iffalse
only the primaryKeys are present in the data backing the model. These keys will be used to load the correct model.
// Embedded Relationship ExamplepostJson={id:99,title:'Post Title',body:'Post Body',comments:[{id:1,body:'comment body one',},{id:2,body:'comment body two'}]};App.Post=Ember.Model.extend({id:Ember.attr(),title:Ember.attr(),body:Ember.attr(),comments:Ember.hasMany('App.Comment',{key:'comments',embedded:true})});App.Comment=Ember.Model.extend({id:Ember.attr(),body:Ember.attr()});
// ID-based Relationship ExamplepostJson={id:99,title:'Post Title',body:'Post Body',comment_ids:[1,2]};commentsJson=[{id:1,body:'Comment body one',post_id:99},{id:2,body:'Comment body two',post_id:99}];App.Post=Ember.Model.extend({id:Ember.attr(),title:Ember.attr(),body:Ember.attr(),comments:Ember.hasMany('App.Comment',{key:'comment_ids'})});App.Comment=Ember.Model.extend({id:Ember.attr(),body:Ember.attr(),post:Ember.belongsTo('App.Post',{key:'post_id'})})
Working with abelongsTo
relationship is just like working any otherEmber.Model
. AnEmber.Model
instance is returned when accessing abelongsTo
relationship, so anyModel
methods can be used such assave()
orreload()
.
comment.get('post').reload();// Reloads the comments postpost.get('comments.lastObject').save();// Saves the last comment associated to post
Accessing ahasMany
relationship returns aHasManyArray
or anEmbeddedHasManyArray
which have useful methods for working with the collection of records. On any type ofhasMany
relationship you can callsave()
and all the dirty records in the collection will have theirsave()
methods called. When working with an embeddedhasMany
relationship you can use thecreate(attrs)
method to add a new record to the collection.
post.get('comments').save();// Saves all dirty comments on post// Below only works on embedded relationshipspost.get('comments').create({body:'New Comment Body'});// Creates a new comment associated to post
There are a few properties you can set on your class to customize how eitherEmber.Model
orEmber.RESTAdapter
work:
The property Ember Model uses for a per-record unique value (default: "id").
App.User=Ember.Model.extend({token:attr(),name:attr()});App.User.primaryKey='token';
GET /users/a4bc81f90{"token": "a4bc81f90", "name": "Brian"}
WhenRESTAdapter
creates a record from data loaded from the server it willuse the data from this property instead of the whole response body.
App.User=Ember.Model.extend({name:attr()});App.User.rootKey='user';
GET /users/1{"user": {"id": 1, "name": "Brian"}}
WhenRESTAdapter
creates multiple records from data loaded from the server itwill use the data from this property instead of the whole response body.
App.User=Ember.Model.extend({name:attr()});App.User.collectionKey='users';
GET /users{"users": [{"id": 1, "name": "Brian"}]}
If the server sends keys with underscores (ex:created_at
),rather than camelized (ex:createdAt
), setting this option totrue
makes Ember Model automatically camelize the keys.
App.User=Ember.Model.extend({firstName:attr()});App.User.camelizeKeys=true;
GET /users/1{"id": 1, "first_name": "Brian"}
user.get('firstName')// => Brian
By default no suffix is added to the url. You may want to specifically add one if using the same url for html and json requests.
App.User=Ember.Model.extend({first_name:attr()});App.User.urlSuffix='.json';
GET /users/1.json{"id": 1, "first_name": "Brian"}
When usingRESTAdapter
custom headers and ajax settings can be applied by extendingRESTAdapter
and definingajaxSettings
App.CustomAdapter = Ember.RESTAdapter.extend({ ajaxSettings: function(url, method) { return { url: url, type: method, headers: { "authentication": "xxx-yyy" }, dataType: "json" }; }});
or it can be done at create time of the RESTAdapter
App.User.adapter = Ember.RESTAdapter.create({ ajaxSettings: function(url, method) { return { url: url, type: method, headers: { "authentication": "xxx-yyy" }, dataType: "json" }; }});
Ember Model usesnode.js andgrunt as a build system,These two libraries will need to be installed before building.
To build Ember Model, clone the repository, and runnpm install
to install build dependenciesandgrunt
to build the library.
Unminified and minified builds of Ember Model will be placed in thedist
directory.
Ember Model usesnode.js andgrunt as a build systemand test runner, andbower for dependency management.
If you have not used any of these tools before, you will need to runnpm install -g bower
andnpm install -g grunt-cli
to be able to use them.
To test Ember Model runnpm install
to install build dependencies,bower install
to install theruntime dependencies andgrunt test
to execute the test suite headlessly via phantomjs.
If you prefer to run tests in a browser, you may start a development server usinggrunt develop
. Tests are available athttp://localhost:8000/tests
Are you using Ember Model? Submit a pull request to add your project to this list!
Yehuda Katz (@wycats), Tom Dale (@tomdale), Igor Terzic (@igorT), and company for their amazing work on Ember Data. I believe it's the most ambitious JS project today. The goal is someday everyone's JSON APIs will be conventional enough that Ember Data will be the best choice of data library for Ember. Until then, Ember Model will make it easy to get up and running quickly with Ember.
About
A lightweight model library for Ember.js