aboutsummaryrefslogtreecommitdiffstats
path: root/src/service/resource.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/service/resource.js')
-rw-r--r--src/service/resource.js204
1 files changed, 204 insertions, 0 deletions
diff --git a/src/service/resource.js b/src/service/resource.js
new file mode 100644
index 00000000..9e86caa7
--- /dev/null
+++ b/src/service/resource.js
@@ -0,0 +1,204 @@
+/**
+ * @workInProgress
+ * @ngdoc service
+ * @name angular.service.$resource
+ * @requires $xhr.cache
+ *
+ * @description
+ * Is a factory which creates a resource object that lets you interact with
+ * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
+ *
+ * The returned resource object has action methods which provide high-level behaviors without
+ * the need to interact with the low level {@link angular.service.$xhr $xhr} service or
+ * raw XMLHttpRequest.
+ *
+ * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
+ * `/user/:username`.
+ *
+ * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
+ * `actions` methods.
+ *
+ * Each key value in the parameter object is first bound to url template if present and then any
+ * excess keys are appended to the url search query after the `?`.
+ *
+ * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
+ * URL `/path/greet?salutation=Hello`.
+ *
+ * If the parameter value is prefixed with `@` then the value of that parameter is extracted from
+ * the data object (useful for non-GET operations).
+ *
+ * @param {Object.<Object>=} actions Hash with declaration of custom action that should extend the
+ * default set of resource actions. The declaration should be created in the following format:
+ *
+ * {action1: {method:?, params:?, isArray:?, verifyCache:?},
+ * action2: {method:?, params:?, isArray:?, verifyCache:?},
+ * ...}
+ *
+ * Where:
+ *
+ * - `action` – {string} – The name of action. This name becomes the name of the method on your
+ * resource object.
+ * - `method` – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`,
+ * and `JSON` (also known as JSONP).
+ * - `params` – {object=} – Optional set of pre-bound parameters for this action.
+ * - isArray – {boolean=} – If true then the returned object for this action is an array, see
+ * `returns` section.
+ * - verifyCache – {boolean=} – If true then whenever cache hit occurs, the object is returned and
+ * an async request will be made to the server and the resources as well as the cache will be
+ * updated when the response is received.
+ *
+ * @returns {Object} A resource "class" object with methods for the default set of resource actions
+ * optionally extended with custom `actions`. The default set contains these actions:
+ *
+ * { 'get': {method:'GET'},
+ * 'save': {method:'POST'},
+ * 'query': {method:'GET', isArray:true},
+ * 'remove': {method:'DELETE'},
+ * 'delete': {method:'DELETE'} };
+ *
+ * Calling these methods invoke an {@link angular.service.$xhr} with the specified http method,
+ * destination and parameters. When the data is returned from the server then the object is an
+ * instance of the resource class `save`, `remove` and `delete` actions are available on it as
+ * methods with the `$` prefix. This allows you to easily perform CRUD operations (create, read,
+ * update, delete) on server-side data like this:
+ * <pre>
+ var User = $resource('/user/:userId', {userId:'@id'});
+ var user = User.get({userId:123}, function(){
+ user.abc = true;
+ user.$save();
+ });
+ </pre>
+ *
+ * It is important to realize that invoking a $resource object method immediately returns an
+ * empty reference (object or array depending on `isArray`). Once the data is returned from the
+ * server the existing reference is populated with the actual data. This is a useful trick since
+ * usually the resource is assigned to a model which is then rendered by the view. Having an empty
+ * object results in no rendering, once the data arrives from the server then the object is
+ * populated with the data and the view automatically re-renders itself showing the new data. This
+ * means that in most case one never has to write a callback function for the action methods.
+ *
+ * The action methods on the class object or instance object can be invoked with the following
+ * parameters:
+ *
+ * - HTTP GET "class" actions: `Resource.action([parameters], [callback])`
+ * - non-GET "class" actions: `Resource.action(postData, [parameters], [callback])`
+ * - non-GET instance actions: `instance.$action([parameters], [callback])`
+ *
+ *
+ * @example
+ *
+ * # Credit card resource
+ *
+ * <pre>
+ // Define CreditCard class
+ var CreditCard = $resource('/user/:userId/card/:cardId',
+ {userId:123, cardId:'@id'}, {
+ charge: {method:'POST', params:{charge:true}}
+ });
+
+ // We can retrieve a collection from the server
+ var cards = CreditCard.query();
+ // GET: /user/123/card
+ // server returns: [ {id:456, number:'1234', name:'Smith'} ];
+
+ var card = cards[0];
+ // each item is an instance of CreditCard
+ expect(card instanceof CreditCard).toEqual(true);
+ card.name = "J. Smith";
+ // non GET methods are mapped onto the instances
+ card.$save();
+ // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
+ // server returns: {id:456, number:'1234', name: 'J. Smith'};
+
+ // our custom method is mapped as well.
+ card.$charge({amount:9.99});
+ // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
+ // server returns: {id:456, number:'1234', name: 'J. Smith'};
+
+ // we can create an instance as well
+ var newCard = new CreditCard({number:'0123'});
+ newCard.name = "Mike Smith";
+ newCard.$save();
+ // POST: /user/123/card {number:'0123', name:'Mike Smith'}
+ // server returns: {id:789, number:'01234', name: 'Mike Smith'};
+ expect(newCard.id).toEqual(789);
+ * </pre>
+ *
+ * The object returned from this function execution is a resource "class" which has "static" method
+ * for each action in the definition.
+ *
+ * Calling these methods invoke `$xhr` on the `url` template with the given `method` and `params`.
+ * When the data is returned from the server then the object is an instance of the resource type and
+ * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
+ * operations (create, read, update, delete) on server-side data.
+
+ <pre>
+ var User = $resource('/user/:userId', {userId:'@id'});
+ var user = User.get({userId:123}, function(){
+ user.abc = true;
+ user.$save();
+ });
+ </pre>
+ *
+ * It's worth noting that the callback for `get`, `query` and other method gets passed in the
+ * response that came from the server, so one could rewrite the above example as:
+ *
+ <pre>
+ var User = $resource('/user/:userId', {userId:'@id'});
+ User.get({userId:123}, function(u){
+ u.abc = true;
+ u.$save();
+ });
+ </pre>
+
+ * # Buzz client
+
+ Let's look at what a buzz client created with the `$resource` service looks like:
+ <doc:example>
+ <doc:source>
+ <script>
+ function BuzzController($resource) {
+ this.Activity = $resource(
+ 'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
+ {alt:'json', callback:'JSON_CALLBACK'},
+ {get:{method:'JSON', params:{visibility:'@self'}}, replies: {method:'JSON', params:{visibility:'@self', comments:'@comments'}}}
+ );
+ }
+
+ BuzzController.prototype = {
+ fetch: function() {
+ this.activities = this.Activity.get({userId:this.userId});
+ },
+ expandReplies: function(activity) {
+ activity.replies = this.Activity.replies({userId:this.userId, activityId:activity.id});
+ }
+ };
+ BuzzController.$inject = ['$resource'];
+ </script>
+
+ <div ng:controller="BuzzController">
+ <input name="userId" value="googlebuzz"/>
+ <button ng:click="fetch()">fetch</button>
+ <hr/>
+ <div ng:repeat="item in activities.data.items">
+ <h1 style="font-size: 15px;">
+ <img src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
+ <a href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
+ <a href ng:click="expandReplies(item)" style="float: right;">Expand replies: {{item.links.replies[0].count}}</a>
+ </h1>
+ {{item.object.content | html}}
+ <div ng:repeat="reply in item.replies.data.items" style="margin-left: 20px;">
+ <img src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
+ <a href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>: {{reply.content | html}}
+ </div>
+ </div>
+ </div>
+ </doc:source>
+ <doc:scenario>
+ </doc:scenario>
+ </doc:example>
+ */
+angularServiceInject('$resource', function($xhr){
+ var resource = new ResourceFactory($xhr);
+ return bind(resource, resource.route);
+}, ['$xhr.cache']);