{"id":13519240,"url":"https://github.com/platanus/angular-restmod","last_synced_at":"2025-05-16T02:08:04.501Z","repository":{"id":10777867,"uuid":"13044294","full_name":"platanus/angular-restmod","owner":"platanus","description":"Rails inspired REST-API ORM for Angular","archived":false,"fork":false,"pushed_at":"2016-11-11T23:05:30.000Z","size":5825,"stargazers_count":1175,"open_issues_count":86,"forks_count":87,"subscribers_count":42,"default_branch":"master","last_synced_at":"2025-04-19T04:14:08.226Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://platanus.github.io/angular-restmod/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/platanus.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-09-23T18:56:33.000Z","updated_at":"2025-04-18T16:59:19.000Z","dependencies_parsed_at":"2022-09-05T16:50:39.733Z","dependency_job_id":null,"html_url":"https://github.com/platanus/angular-restmod","commit_stats":null,"previous_names":["angular-platanus/restmod"],"tags_count":54,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/platanus%2Fangular-restmod","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/platanus%2Fangular-restmod/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/platanus%2Fangular-restmod/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/platanus%2Fangular-restmod/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/platanus","download_url":"https://codeload.github.com/platanus/angular-restmod/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254453655,"owners_count":22073617,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T05:01:56.222Z","updated_at":"2025-05-16T02:08:04.472Z","avatar_url":"https://github.com/platanus.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Web Service"],"sub_categories":[],"readme":"\u003c!-- load: restmod --\u003e\n\u003c!-- provide: $provide --\u003e\n\u003c!-- inject: $httpBackend --\u003e\n\u003c!-- inject: $injector --\u003e\n\u003c!-- inject: restmod --\u003e\n\n\u003c!-- before:\n\t$httpBackend.when('GET', '/bikes/1').respond({ model: 'Slash', brand: 'Trek' })\n\t$httpBackend.when('GET', '/bikes/1?includeParts=true').respond({ model: 'Slash', brand: 'Trek', parts: [] })\n\t$httpBackend.when('GET', '/bikes?brand=trek').respond([ { model: 'Slash' }, { model: 'Remedy' } ])\n\t$httpBackend.when('GET', '/bikes?category=enduro').respond([ { model: 'Slash' }, { model: 'Remedy' } ])\n\n\tmodule = $provide;\n--\u003e\n\nAngular Restmod\n===============\n\nAngular Restmod is open source software kindly sponsored by:\n\n![Supporter](http://placehold.it/350x150)\n\n(We need a sponsor for this library, please write to contact@platan.us if you want to help.)\n\n\n[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/platanus/angular-restmod?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n[![Build Status](https://api.travis-ci.org/platanus/angular-restmod.svg)](https://travis-ci.org/platanus/angular-restmod) [![Code Climate](https://codeclimate.com/github/platanus/angular-restmod/badges/gpa.svg)](https://codeclimate.com/github/platanus/angular-restmod) [![Stories in Ready](https://badge.waffle.io/platanus/angular-restmod.png?label=ready\u0026title=Ready)](https://waffle.io/platanus/angular-restmod) [![Bower version](https://badge.fury.io/bo/angular-restmod.svg)](http://badge.fury.io/bo/angular-restmod)\n\n\nRestmod creates objects that you can use from within Angular to interact with your RESTful API.\n\nSaving bikes on your serverside database would be as easy as:\n\n\u003c!-- section: main example --\u003e\n\n```javascript\nvar newBike = Bike.$build({ brand: 'Trek' });\nnewBike.model = '5200';\nnewBike.$save(); // bike is persisted sending a POST to /bikes\n```\n\n\u003c!-- end --\u003e\n\nIt also supports collections, relations, lifecycle hooks, attribute renaming, side data loading and much more.\nContinue reading for a quick start, check this [presentation](http://www.slideshare.net/MarcinGajda/angular-restmod-46881968) for an overview or check the API Reference for more: http://platanus.github.io/angular-restmod\n\nIf you are working with Ruby on Rails, we recommend [active_model_serializers](https://github.com/rails-api/active_model_serializers) for seamless integration.\n\n## Why Restmod?\n\nRestmod brings Rails ActiveRecord's ease of use to the Angular Framework. It succesfuly combines Angular's encapsulated design with Active Record's opinionated style. There are other alternatives available though:\n\n* **$resource:** Might be enough for small projects, included as an Angular opt-in. It only provides a basic model type layer, with limited features.\n* **Restangular:** very complete library, but does not propose a model layer and does not support linked resource responses as seen on jsonapi.org.\n* **angular-activerecord:** Nice alternative to $resource, still very limited in its functionality.\n* **ModelCore:** Inspired in angular-activerecord, provides a more complete set of features but lacks testing.\n\n**Restmod its thoroughly tested against the same platforms as AngularJS using [SauceLabs awesome OpenSauce service!](https://saucelabs.com/opensauce)**\n\n## Getting Started\n\n#### 1. Get the code\n\nYou can get it straight from the repository\n\n```\ngit clone git@github.com:platanus/angular-restmod.git\n```\n\nbut we recommend you to use bower to retrieve the Restmod package\n\n```\nbower install angular-restmod --save\n```\n\nor if you prefer, a npm package is also available\n\n```\nnpm install angular-restmod -d\n```\n\n#### 2. Include it on your project\n\nMake sure the restmod source is required in your code.\n\n```html\n\u003cscript type=\"text/javascript\" src=\"js/angular-restmod-bundle.min.js\"\u003e\u003c/script\u003e\n```\n\nNext, include angular module as one of your app's dependencies\n\n\u003c!-- before: module = $provide; --\u003e\n\u003c!-- ignore --\u003e\n\n```javascript\nmodule = angular.module('MyApp', ['restmod'])\n```\n\n\u003c!-- end --\u003e\n\n# REST API Integration\n\nRestmod comes bundled with various (well, just one for now) predefined API-style-mixins to choose from depending on your backend configuration.\n\nCheck out the [Style listing](https://github.com/platanus/angular-restmod/blob/master/docs/guides/styles.md) for more information. **We are looking for contributors on this!!**\n\nIf you dont set a base style a `'No API style base was included'` warning will be generated, see the link above for more information.\n\nIf you still need to change some behaviour or if you want to create your own api style, the following configurations are available:\n\n* Common url prefix configuration\n* Primary key name configuration\n* Json root property configuration\n* Json metadata extraction\n* Json side data resolving for jsonapi.org style APIs (for apis using 'links')\n* Request customization\n* Url formatting options\n\nMake sure you read the [Api Integration FAQ](https://github.com/platanus/angular-restmod/blob/master/docs/guides/integration.md) before starting your API integration!\n\n# Basic usage\n\nYou begin by creating a new model using the `restmod.model` method. We recommend you to put each model on a separate factory. The first argument for `model` is the resource URL.\n\n```javascript\nmodule.factory('Bike', function(restmod) {\n\treturn restmod.model('/bikes');\n});\n```\n\n\u003c!-- before: Bike = $injector.get('Bike') --\u003e\n\u003c!-- it: expect(Bike).not.toBeNull() --\u003e\n\nThe generated model type provides basic CRUD operations to interact with the API:\n\n\u003c!-- section: $find --\u003e\n\nTo retrieve an object by ID use `$find`, the returned object will be filled with the response data when the server response is received.\n\nLet's say you have a REST API that responds JSON to a GET REQUEST on /bikes/1\n\n```json\n{\n\t\"id\": 1,\n\t\"brand\": \"Trek\",\n\t\"created_at\": \"2014-05-23\"\n}\n```\n\nThen, on your code you would call\n\n```javascript\nbike = Bike.$find(1);\n```\n\n\u003c!-- section: $then --\u003e\n\nRight after this line executes, the bike object is an empty object. The bike object will be populated as soon as the API returns some data. This works great with Angular's way. Nevertheless, you can use `$then` to do something when data becomes available.\n\n```javascript\nbike.$then(function() {\n\texpect(bike.brand).toBeDefined();\n});\n```\n\n\u003c!-- it: $httpBackend.flush() --\u003e\n\u003c!-- end --\u003e\n\nIf you need to pass additional parameters to `$find`, you can use the second function argument.\n\n```javascript\nbike = Bike.$find(1, { includeParts: true });\n```\n\n\u003c!-- it: $httpBackend.flush(); expect(bike.model).toEqual('Slash') --\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $fetch --\u003e\n\nTo reload an object use `$fetch`. **WARNING:** This will overwrite modified properties.\n\n\u003c!-- before: bike = Bike.$new(1) --\u003e\n\n```javascript\nbike.$fetch();\n```\n\nIf you only want to retrieve an object data if it hasn't been retrieved yet, use `$resolve` instead of `$fetch`:\n\n```javascript\nbike.$resolve();\n```\n\nTo mark an object as unresolved call `$reset`. You can hook to the `before-resolve` event to add some expiration logic\nfor resolved objects, just call `$reset` inside the hook to force the object to be retrieved.\n\nRemember to use `$resolve().$asPromise()` if you are returning inside a resolve function.\n\n\u003c!-- it: $httpBackend.flush(); expect(bike.model).toEqual('Slash') --\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $collection and $search --\u003e\n\nTo retrieve an object collection `$collection` or `$search` can be used.\n\n```javascript\nbikes = Bike.$search({ category: 'enduro' });\n// same as\nbikes = Bike.$collection({ category: 'enduro' }); // server request not yet sent\nbikes.$refresh();\n```\n\n\u003c!-- it: $httpBackend.flush(); expect(bikes.length).toEqual(2) --\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: scoped --\u003e\n\n\u003c!-- before: bikes = Bike.$collection({ category: 'enduro' }); --\u003e\n\n\u003c!-- section: $refresh --\u003e\n\nTo reload a collection use `$refresh`. To append more results use `$fetch`.\n\n\u003c!-- before:\n\t$httpBackend.when('GET', '/bikes?category=enduro\u0026page=1').respond([{ model: 'Slash', brand: 'Trek' }]);\n\t$httpBackend.when('GET', '/bikes?category=enduro\u0026page=2').respond([{ model: 'Meta', brand: 'Commencal' }]);\n\t$httpBackend.when('GET', '/bikes?category=enduro\u0026page=3').respond([{ model: 'Mach 6', brand: 'Pivot' }]);\n--\u003e\n\n```javascript\nbikes = Bike.$collection({ category: 'enduro' });\nbikes.$refresh({ page: 1 }); // clear collection and load page 1\nbikes.$fetch({ page: 2 }); // page 2 is appended to page 1, usefull for infinite scrolls...\nbikes.$refresh({ page: 3 }); // collection is reset, page 3 is loaded on response\n```\n\n\u003c!-- it: $httpBackend.flush(); expect(bikes.length).toEqual(1) --\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $save --\u003e\n\nTo update an object, just modify the properties and call `$save`.\n\n```javascript\nbike = Bike.$find(1);\nbike.brand = 'Trek';\nbike.$save();\n```\n\n\u003c!-- it: $httpBackend.expectPUT('/bikes/1').respond(200, '{}'); $httpBackend.flush(); --\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $save --\u003e\n\nTo create a new object use `$build` and then call `$save`. This will send a POST request to the server.\n\n```javascript\nvar newBike = Bike.$build({ brand: 'Comencal' });\nnewBike.model = 'Meta';\nnewBike.$save(); // bike is persisted\n```\n\n\u003c!-- it: $httpBackend.expectPOST('/bikes').respond(200, '{}'); $httpBackend.flush(); --\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $save patch --\u003e\n\nTo patch an object, just modify the properties and call `$save` passing an array of properties to be patched as first argument.\n\n```javascript\nbike = Bike.$find(1);\nbike.brand = 'Trek';\nbike.model = 'Slash';\nbike.dim = { width: 10.0, height: 10.0 };\nbike.$save(['brand', 'dim']); // will only send brand and dim (every sub property)\n```\n\n\u003c!-- it: $httpBackend.expectPATCH('/bikes/1', { brand: 'Trek', dim: { width: 10.0, height: 10.0 } }).respond(200, '{}'); $httpBackend.flush(); --\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $save patch nested --\u003e\n\nTo specify a single subproperty to be sent in patch, use dot notation:\n\n```javascript\nbike = Bike.$find(1);\nbike.brand = 'Trek';\nbike.model = 'Slash';\nbike.dim = { width: 10.0, height: 10.0 };\nbike.$save(['dim.height']); // will only send dim.height\n```\n\n\u003c!-- it: $httpBackend.expectPATCH('/bikes/1', { dim: { height: 10.0 } }).respond(200, '{}'); $httpBackend.flush(); --\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $create on type --\u003e\n\nOr use `$create`\n\n```javascript\nvar newBike = Bike.$create({ brand: 'Comencal', model: 'Meta' });\n```\n\n\u003c!-- it: $httpBackend.expectPOST('/bikes').respond(200, '{}'); $httpBackend.flush(); --\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $create on collection --\u003e\n\nIf called on a collection, `$build` and `$create` will return a collection-bound object that will be added when saved successfully.\n\n```javascript\nnewBike = bikes.$create({ brand: 'Comencal', model: 'Meta' });\n// after server returns, the 'bikes' collection will contain 'newBike'.\n```\n\n\u003c!-- it:\n\texpect(bikes.length).toEqual(0);\n\t$httpBackend.expectPOST('/bikes').respond(200, '{}');\n\t$httpBackend.flush();\n\texpect(bikes.length).toEqual(1);\n--\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $reveal --\u003e\n\nTo show a non saved object on the bound collection use `$reveal`\n\n```javascript\nvar newBike = bikes.$create({ brand: 'Comencal', model: 'Meta' }).$reveal();\n// 'newBike' is inmediatelly available at 'bikes'\n```\n\n\u003c!-- it: expect(bikes.length).toEqual(1); --\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $destroy --\u003e\n\nFinally, to destroy an object just call `$destroy`.\n\n```javascript\nbike.$destroy();\n```\n\u003c!-- $httpBackend.expectDELETE('/bikes').respond(200, '{}'); $httpBackend.flush(); --\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $destroy on collection --\u003e\n\nAs with $create, calling `$destroy` on a record bound to a collection will also remove it from the collection on server response.\n\n\u003c!-- end --\u003e\n\nAll REST operations described above use `$q` promises that are fulfilled when the operation succeeds or fails. Take a look at the [promises guide](https://github.com/platanus/angular-restmod/blob/master/docs/guides/promises.md) for more details on this.\n\n\u003c!-- end --\u003e\n\n\n# Customizing model behaviour\n\nWhen defining a model, you can pass a **definition object**\n\n```javascript\nBike = restmod.model('api/bikes').mix(\n// This is the definition object:\n{\n\tcreatedAt: { encode: 'date' },\n\towner: { belongsTo: 'User' }\n}\n);\n```\n\n\u003c!-- it:\n\texpect(Bike.$new().owner).toBeDefined();\n\texpect(typeof Bike.$build({ createdAt: new Date() }).$encode().createdAt).toEqual('string');\n--\u003e\n\nThe **definition object** allows you to:\n* Define **relations** between models\n* Customize an attribute's **serialization** and **default values**\n* Set model configuration variables.\n* Add **custom methods**\n* Add lifecycle **hooks**\n\n\n## Relations\n\nRelations are defined like this:\n\n\u003c!-- before:\n\tmodule.factory('User', function() { return restmod.model(); });\n\tmodule.factory('Part', function() { return restmod.model(); });\n\t$httpBackend.when('GET', '/bikes/1/parts').respond([ { id: 1, brand: 'Shimano' }, { id: 2, brand: 'SRAM' } ]);\n\t$httpBackend.when('GET', '/parts/1').respond({ brand: 'Shimano', category: 'brakes' });\n--\u003e\n\n```javascript\nBike = restmod.model('/bikes').mix({\n\tparts: { hasMany: 'Part' },\n\towner: { belongsTo: 'User' }\n});\n```\n\n\u003c!-- it:\n\texpect(Bike.$new().owner).toBeDefined();\n\texpect(Bike.$new().parts).toBeDefined();\n--\u003e\n\nThere are four types of relations:\n\n#### HasMany\n\nLet's say you have the following 'Part' model:\n\n```javascript\nmodule.factory('Part', function() {\n\treturn restmod.model('/parts');\n});\n```\n\nThe HasMany relation allows you to access parts of a specific bike directly from a bike object. In other words, HasMany is a hierarchical relation between a model instance (bike) and a model collection (parts).\n\n```javascript\nBike = restmod.model('/bikes').mix({\n\tparts: { hasMany: 'Part' }\n});\n\nbike = Bike.$new(1); \t\t\t// no request are made to the server yet.\nparts = bike.parts.$fetch(); \t// sends a GET to /bikes/1/parts\n```\n\n\u003c!-- it:\n\t$httpBackend.expectGET('/bikes/1/parts');\n\t$httpBackend.flush();\n\texpect(parts.length).toEqual(2);\n--\u003e\n\n\u003c!-- section: $fetch --\u003e\n\n\u003e Note: this is not necessarily great modeling ... for instance, if you destroy a bike's part `bike.parts[0].$destroy();`, you will be sending a DELETE to the resource in /parts/:id. If this is not what you want, you should consider working with a resource that represents the *relation* between a bike and a Part.\n\nLater on, after 'parts' has already been resolved,\n\n\u003c!-- before: $httpBackend.flush() --\u003e\n\n```javascript\nparts[0].$fetch(); // updates the part at index 0. This will do a GET /parts/:id\n```\n\n\u003c!-- it:\n\t$httpBackend.expectGET('/parts/1');\n\t$httpBackend.flush();\n\texpect(parts[0].category).toEqual('brakes');\n--\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: $create --\u003e\n\nCalling `$create` on the collection will POST to the collection nested url.\n\n```javascript\nvar part = bike.parts.$create({ serialNo: 'XX123', category: 'wheels' }); // sends POST /bikes/1/parts\n```\n\n\u003c!-- it:\n\t$httpBackend.expectPOST('/bikes/1/parts').respond(200, {});\n\t$httpBackend.flush();\n--\u003e\n\u003c!-- end --\u003e\n\n\u003c!-- section: nested --\u003e\n\nIf the child collection model is nested then all CRUD routes for the collection items are bound to the parent.\n\nSo if 'Part' was defined like:\n\n```javascript\nrestmod.model();\n```\n\n\u003c!-- section: $fetch --\u003e\n\nThe example above would behave like this:\n\n\u003c!-- before:\n\tbike = restmod.model('/bikes').mix({ parts: { hasMany: restmod.model() } }).$new(1);\n\tbike.parts.$decode([{ id: 1 }]);\n--\u003e\n\n```javascript\nconsole.log(bike.parts[0].$url())\nbike.parts[0].$fetch();\n```\n\nWill send GET to /bikes/1/parts/:id instead of /parts/:id\n\n\u003c!-- it:\n\t$httpBackend.expectGET('/bikes/1/parts/1').respond(200, {});\n\t$httpBackend.flush();\n--\u003e\n\n\u003c!-- end --\u003e\n\n##### HasMany Options\n\nThe has many relation provides the following options for you to customize its behaviour:\n\n* `path`: will change the relative path used to fetch/create the records. Ex: `{ hasMany: 'Part', path: 'pieces' }`\n* `inverseOf`: adds a property on the child object that points to the parent. Ex: `{ hasMany: 'Part', inverseOf: 'bike' }`.\n* `params`: optional query string parameters to be used when fetching collection. Ex: `{ hasMany: 'Part', params: { foo: 'bar' } }`.\n* `hooks`: you can also define `hasMany` relation hooks. Check the [hooks advanced documentation](https://github.com/platanus/angular-restmod/blob/master/docs/guides/hooks.md) for more information.\n\n\u003c!-- end --\u003e\n\n#### HasOne\n\nThis is a hierarchical relation between one model's instance and another model's instance.\nThe child instance url is bound to the parent url.\nThe child instance is created **at the same time** as the parent, so its available even if the parent is not resolved.\n\nLet's say you have the following 'User' model:\n\n```javascript\nmodule.factory('User', function() {\n\treturn restmod.model('/users');\n});\n```\n\nThat relates to a 'Bike' through a *hasOne* relation:\n\n```javascript\nBike = restmod.model('/bikes').mix({\n\towner: { hasOne: 'User' }\n});\n```\n\n\u003c!-- section: not nested --\u003e\n\nThen a bike's owner data can then be retrieved just by knowing the bike primary key (id):\n\n```javascript\nowner = Bike.$new(1).owner.$fetch();\n```\n\n\u003e will send GET /bikes/1/owner\n\n\u003c!-- it:\n\t$httpBackend.expectGET('/bikes/1/owner').respond(200, {});\n\t$httpBackend.flush();\n--\u003e\n\n\u003c!-- end --\u003e\n\n\u003c!-- section: not nested --\u003e\n\nSince the user resource has its own resource url defined:\n\n\u003c!-- before: owner = Bike.$new(1).owner.$decode({ id: 1 }); --\u003e\n\n```javascript\nowner.name = 'User';\nowner.$save();\n```\n\n\u003c!-- it:\n\t$httpBackend.expectPUT('/users/1').respond(200, {});\n\t$httpBackend.flush();\n--\u003e\n\n\u003e will send PUT /user/X.\n\n\u003c!-- end --\u003e\n\n\u003c!-- section: nested --\u003e\n\nIf 'User' was to be defined like a nested resource:\n\n```javascript\nmodule.factory('User', function() {\n\treturn restmod.model();\n});\n```\n\n\u003c!-- before:\n\towner = restmod.model('/bikes').mix({ owner: { hasOne: 'User' } }).$new(1).owner;\n--\u003e\n\nThen calling:\n\n```javascript\nowner.name = 'User';\nowner.$save();\n```\n\n\u003c!-- it:\n\t$httpBackend.expectPUT('/bikes/1/owner').respond(200, {});\n\t$httpBackend.flush();\n--\u003e\n\n\u003e will send a PUT to /bikes/1/owner\n\n\u003c!-- end --\u003e\n\n##### HasOne Options\n\nThe has many relation provides the following options for you to customize its behaviour:\n\n* `path`: will change the relative path used to fetch/create the records. Ex: `{ hasOne: 'Part', path: 'pieces' }`\n* `inverseOf`: adds a property on the child object that points to the parent. Ex: `{ hasOne: 'Part', inverseOf: 'bike' }`.\n* `hooks`: you can also define `hasOne` relation hooks. Check the [hooks advanced documentation](https://github.com/platanus/angular-restmod/blob/master/docs/guides/hooks.md) for more information.\n\n\u003c!-- ignore --\u003e\n\n#### BelongsTo\n\nThis relation should be used in the following scenarios:\n\n1. The api resource references another resource by id:\n\n```javascript\n{\n\tname: '...',\n\tbrand: '...',\n\towner_id: 20\n}\n```\n\n2. The api resource contanis another resource as an inline property and does not provide the same object as a nested url:\n\n```javascript\n{\n\tname: '...',\n\tbrand: '...',\n\towner: {\n\t\tid: 20,\n\t\tuser: 'extreme_rider_99'\n\t}\n}\n```\n\nWhen applied, the referenced instance is not bound to the host's scope and is **generated after** server responds to a parent's `$fetch`.\n\nLet's say you have the same 'User' model as before:\n\n```javascript\nmodule.factory('User', function() {\n\treturn restmod.model('/users');\n});\n```\n\nThat relates to a 'Bike' through a *belongsTo* relation this time:\n\n```javascript\nBike = restmod.model('/bikes').mix({\n\towner: { belongsTo: 'User', key: 'last_owner_id' } // default key would be 'owner_id'\n});\n```\n\nAlso you have the following bike resource:\n\n```\nGET /bikes/1\n\n{\n\tid: 1,\n\tbrand: 'Transition',\n\tlast_owner_id: 2\n}\n```\n\nThen retrieving the resource:\n\n```javascript\nbike = Bike.$find(1);\n```\n\nWill produce a `bike` object with its owner property initialized to a user with id=2, the owner property will only be available **AFTER** server response arrives.\n\nThen calling\n\n```javascript\nbike.owner.$fetch();\n```\n\nWill send a GET to /users/2 and populate the owner property with the user data.\n\nThis relation also support the child object data to come inlined in the parent object data.\nThe inline property name can be optionally selected using the `map` attribute.\n\nLets redefine the `Bike` model as:\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\towner: { belongsTo: 'User', map: 'last_owner' } // map would default to *owner*\n});\n```\n\nAnd suppose that the last bike resource looks like:\n\n```\nGET /bikes/1\n\n{\n\tid: 1,\n\tbrand: 'Transition',\n\tlast_owner: {\n\t\tid: 2\n\t\tname: 'Juanito'\n\t}\n}\n```\n\nThen retrieving the bike resource:\n\n```javascript\nvar bike = Bike.$find(1);\n```\n\nWill produce a `bike` object with its owner property initialized to a user with id=2 and name=Juanito. As before, the owner property will only be available **AFTER** server response arrives.\n\nWhenever the host object is saved, the reference primary key will be sent in the request using the selected foreign key.\n\nSo given the previous model definition, doing:\n\n```javascript\nvar bike = Bike.$create({ last_owner: User.$find(20) });\n```\n\nWill generate the following request:\n\n```\nPOST /bikes\n\n{\n\towner_id: 20\n}\n```\n\n#### BelongsToMany\n\nThis relation should be used in the following scenarios:\n\n1. The api resource references another resource by id:\n\n```javascript\n{\n\tname: '...',\n\tbrand: '...',\n\tparts_ids: [1,2]\n}\n```\n\n2. The api resource contains another resource as an inline property and does not provide the same object as a nested url:\n\n```javascript\n{\n\tname: '...',\n\tbrand: '...',\n\tparts: [\n\t\t{ id: 1, user: 'handlebar' },\n\t\t{ id: 2, user: 'wheel' }\n\t]\n}\n```\n\nWhen retrieved, the referenced instances will not be bound to the host's scope.\n\nLet's say you have the following 'Part' definition:\n\n```javascript\nmodule.factory('Part', function() {\n\treturn restmod.model('/parts');\n});\n```\n\nThat relates to a 'Bike' through a *belongsToMany* relation this time:\n\n```javascript\nBike = restmod.model('/bikes').mix({\n\tparts: { belongsToMany: 'Part', keys: 'part_keys' } // default key would be 'parts_ids'\n});\n```\n\nAlso you have the following bike resource:\n\n```\nGET /bikes/1\n\n{\n\tid: 1,\n\tbrand: 'Transition',\n\tparts_keys: [1, 2]\n}\n```\n\nThen retrieving the resource:\n\n```javascript\nbike = Bike.$find(1);\n```\n\nWill produce a `bike` object with the `parts` property containing two **Part** objects with $pks set to 1 and 2 (but empty).\n\n\nThis relation also support the childs object data to come inlined in the hosts object data.\nThe inline property name can be optionally selected using the `map` attribute.\n\nGiven the same **Bike** model as before, lets suppose now that the bike API resource looks like this:\n\nAnd suppose that the last bike resource looks like:\n\n```\nGET /bikes/1\n\n{\n\tid: 1,\n\tbrand: 'Transition',\n\tparts: [\n\t\t{ id: 1, user: 'handlebar' },\n\t\t{ id: 2, user: 'wheel' }\n\t]\n}\n```\n\nThen retrieving the bike resource:\n\n```javascript\nvar bike = Bike.$find(1);\n```\n\nWill produce a `bike` object with the `parts` property containing two populated **Part** objects with $pks set to 1 and 2.\n\nWhenever the host object is saved, the references primary keys will be sent in the request using the selected key.\n\nSo given the previous model definition, doing:\n\n```javascript\nvar bike = Bike.$create({ parts: [Part.$find(1), Part.$find(2)] });\n```\n\nWill generate the following request:\n\n```\nPOST /bikes\n\n{\n\tparts_keys: [1, 2] // remember we changed the keys property name before!\n}\n```\n\n\u003c!-- ignore --\u003e\n\n## Serialization, masking and default values.\n\nWhen you communicate with an API, some attribute types require special treatment (like a date, for instance)\n\n### Decode\n\nYou can specify a way of decoding an attribute when it arrives from the server.\n\nLet's say you have defined a filter like this:\n\n```javascript\nAngular.factory('DateParseFilter', function() {\n\treturn function(_value) {\n\t\tdate = new Date();\n\t\tdate.setTime(Date.parse(_value));\n\t\treturn date;\n\t}\n})\n```\n\nthen you use it as a standard decoder like this:\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\tcreatedAt: {decode: 'date_parse' }\n});\n```\n\n### Encode\n\n To specify a way of encoding an attribute before you send it back to the server:\nJust as with the previous example (decode), you use an Angular Filter. In this example we use the built in 'date' filter.\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\tcreatedAt: {encode:'date', param:'yyyy-MM-dd'}\n});\n```\n\nOn both **encode** and **decode** you can use an inline function instead of the filter's name. It is also possible to bundle an encoder and decoder together using a Serializer object, check the [API Reference](http://platanus.github.io/angular-restmod) for more.\n\n### Attribute masking\n\nFollowing the Angular conventions, attributes that start with a '$' symbol are considered private and never sent to the server. Furthermore, you can define a mask that allows you to specify a more advanced behaviour for other attributes:\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\tcreatedAt: { ignore: 'CU' }, // won't send on Create or Update\n\tviewCount: { ignore: 'R' }, // won't load on Read (fetch)\n\topened: { ignore: true }, // will ignore in every request and response\n});\n```\n\n### Default value\n\nYou can define default values for your attributes, both static and dynamic. Dynamic defaults are defined using a function that will be called on record creation.\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\twheels: { init: 2 }, // every new bike will have 2 wheels by default\n\tcreatedAt: { init: function() {\n\t return new Date();\n\t}}\n});\n```\n\n### Explicit attribute mapping\n\nYou can explicitly tell restmod to map a given server attribute to one of the model's attributes:\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\tcreated: { map: 'stats.created_at' }\n});\n```\n\n### Volatile attributes\n\nYou can define volatile attributes, volatile attributes will be deleted from record instance after being sent to server, this is usefull for things like passwords.\n\n```javascript\nvar User = restmod.model('/users').mix({\n\tpassword: { volatile: true } // make password volatile\n});\n```\n\n### Nested properties\n\nSometimes you will need to specify behaviour for nested properties, this is done the same ways as with regular properties using the `.` symbol.\n\nGiven the following json response:\n\n```json\n{\n\t\"id\": 1,\n\t\"serialNo\": {\n\t\t\"issued\": \"2014-05-05\"\n\t}\n}\n```\n\nYou can add a date decoder for the `issued` property using:\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\t'serialNo.issued': { decode: 'date_parse' }\n});\n```\n\nIf the nested property is inside an array, you can reffer to it using the `[]` symbols.\n\nSo if the json response looks like this:\n\n```json\n{\n\t\"id\": 1,\n\t\"tags\": [\n\t\t{ \"name\": \"endurow\", \"weight\": 20 },\n\t\t{ \"name\": \"offroad\", \"weight\": 5 }\n\t]\n}\n```\n\nYou can add a mapping for the `weight` property to the `size` property using:\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\t'tags[].size': { map: 'weight' }\n});\n```\n\n## Custom methods\n\nA restmod object is composed of three main APIs, the Model static API, the record API and the collection API.\n\nEach one of these APis can be extended using the `$extend` block in the object definition:\n\nFor example, the following will add the `pedal` method to every record.\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\t$extend: {\n\t\tRecord: {\n\t\t\tpedal: function() {\n\t\t\t \tthis.strokes += 1;\n\t\t\t}\n\t\t}\n\t}\n});\n```\n\nEven though the `$extend` block is the preferred method to extend a model, for small models it is also possible to\ndirectly define the method in the definition object:\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\t'Record.pedal': function() {\n\t \tthis.strokes += 1;\n\t}\n});\n```\n\nIn the last example the 'Record.' prefix could be omitted because by default methods are added to the record api.\n\nThe following API's are available for extension:\n* Model: the static api.\n* Record: model instances api.\n* Collection: model collection instance api.\n* Scope: Same as extending Model and Collection\n* Resource: Same as extending Record and+ Collection\n* List: special api implemented by any record list, including collections\n\nSo, to add a static method we would use:\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\t$extend: {\n\t\tModel: {\n\t\t\t$searchByTeam: function(_team) {\n\t\t\t \treturn this.$search({ team: _team });\n\t\t\t}\n\t\t}\n\t}\n});\n```\n\nIt is also posible to override an existing method, to refer to the overriden function use `this.$super`:\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\t$extend: {\n\t\tScope: {\n\t\t\t$search: function(_params) {\n\t\t\t \treturn this.$super(angular.extend({ time: SomeService.currentTime() }, _params);\n\t\t\t}\n\t\t}\n\t}\n});\n```\n\n### Custom methods and Lists\n\nA `List` namespace is provided for collections and lists, this enables the creation of chainable list methods.\n\nFor example, lets say you need to be able to filter a collection of records and then do something with the resulting list:\n\n```javascript\nvar Part = restmod.model('/parts').mix({\n\t$extend: {\n\t\tList: {\n\t\t\tfilterByCategory: function(_category) {\n\t\t\t\treturn this.$asList(function(_parts) {\n\t\t\t\t\treturn _.filter(_parts, function(_part) {\n\t\t\t\t\t\treturn _part.category == _category;\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t},\n\t\t\tfilterByBrand: function(_brand) {\n\t\t\t\treturn this.$asList(function(_parts) {\n\t\t\t\t\treturn _.filter(_parts, function(_part) {\n\t\t\t\t\t\treturn _part.brand == _brand;\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t},\n\t\t\tgetTotalWeight: function(_category) {\n\t\t\t\treturn _.reduce(this, function(sum, _part) {\n\t\t\t\t\treturn sum + _part.weight;\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n});\n```\n\nNow, since `List` methods are shared by both collections and lists, you can do:\n\n```javascript\nPart.$search().filterByCategory('wheels').filterByBrand('SRAM').$then(function() {\n\t// use $then because the $asList method will honor promises.\n\tscope.weight = this.getTotalWeight();\n});\n```\n\n## Hooks (callbacks)\n\nJust like you do with ActiveRecord, you can add hooks on certain steps of the object lifecycle, hooks are added in the `$hooks` block of the object definition.\n\n```javascript\nvar Bike = restmod.model('/bikes').mix({\n\t$hooks: {\n\t\t'before-save': function() {\n\t\t\tthis.partCount = this.parts.length;\n\t\t}\n\t}\n});\n\n```\nNote that a hook can be defined for a type, a collection or a record. Also, hooks can also be defined for a given execution context using $decorate. Check the [hooks advanced documentation](https://github.com/platanus/angular-restmod/blob/master/docs/guides/hooks.md).\n\n# Mixins\n\nTo ease up the definition of models, and keep things DRY, Restmod provides you with mixin capabilities. For example, say you already defined a Vehicle model as a factory:\n\n```javascript\nAngular.factory('Vehicle', function() {\n\treturn restmod.model('/vehicle').mix({\n\t\tcreatedAt: {encode:'date', param:'yyyy-MM-dd'}\n\t});\n})\n```\n\nYou can then define your Bike model that inherits from the Vehicle model, and also sets additional functionality.\n\n```javascript\nvar Bike = restmod.model('/bikes').mix('Vehicle', {\n\tpedal: function (){\n\t\talert('pedaling')\n\t}\n});\n\n```\n\n\u003c!-- end --\u003e\n\nSome links:\n\nREST api designs guidelines: https://github.com/interagent/http-api-design\nREST json api standard: http://jsonapi.org\n\n## Contributing\n\n1. Fork it\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create new Pull Request\n\nCheck [CONTRIBUTORS](https://github.com/platanus/angular-restmod/blob/master/CONTRIBUTE.md) for more details\n\n## Credits\n\nThank you [contributors](https://github.com/platanus/angular-restmod/graphs/contributors)!\n\n\u003cimg src=\"http://platan.us/gravatar_with_text.png\" alt=\"Platanus\" width=\"250\"/\u003e\n\nangular-restmod is maintained by [platanus](http://platan.us).\n\n## License\n\nAngular Restmod is © 2015 platanus, spa. It is free software and may be redistributed under the terms specified in the LICENSE file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplatanus%2Fangular-restmod","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplatanus%2Fangular-restmod","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplatanus%2Fangular-restmod/lists"}