adzialocha / angular-watch-resource

Model Layer for AngularJS based on $watch pattern
MIT License
14 stars 1 forks source link

angular-watch-resource

Model Layer for large angular >v1.3 applications with some niceties - based on $watch pattern inspired by Jason Dobry's Presentation.

Features

Installation

Please note: This model layer service was built for an specific API which follows a strict stateless REST architecture. Read carefully if it fits your needs or make a pull request!

Configuration

Use the ResourceConfigurationProvider to set up the Resource service before runtime:

Example:

app.config(function(ResourceConfigurationProvider) {
  ResourceConfigurationProvider.setBasePath('https://api.yourserver.com/');
  ResourceConfigurationProvider.setDefaultParams({ 'app_id': '120321321' });
});

Documentation

The Resource service exposes the methods one, all, collection, send, reset and debug. In most of the cases only the first four are needed.

Resource Service Resource Type Allowed HTTP methods
one object (single resource) GET*, HEAD
all array GET*, HEAD
collection array GET*, HEAD
send (object) POST*, PUT, DELETE

*) default HTTP method when none given

Resource

A resource is basically a pointer to your data. Every unique path ("/users/212", "/group/111/followers" etc.) is a pointer. In the background the service handles the state of your data, collects all needed pieces from the cache and looks for optimized server requests (to avoid fetching data twice).

Path, Path Variables and Pointers

The syntax to create a Resource is:

The Resource Object

In nearly all of the cases the service returns an Resource object which exposes the following properties:

Meta Information

These $meta-properties are being ignored by angulars digest cycle:

Data
Functions

Retrieval methods

All of these methods return a initial Resource instance we can watch in our controllers (read more about it above).

Update and manipulation methods

Different from the retrieval methods the manipulation methods return a promise instead of a Resource instance - since we dont want to watch single server requests like these.

Other methods

Resource Options

Default Options:

{
  interval: 0,             // interval in ms for frequent resource update requests
  silent: false,           // true to enable silent mode, does not send a request on init

  dataKey: '',             // resource data is inside a key (example: "data")

  cacheKey: undefined,     // use a custom cache key

  sideload: {},            // read -> sideloading resources option (below)
  sideloadKey: '',         // sideload resources are collected in an own object (example: "included")
  nested: {},              // read -> nested resources option (below)

  withCredentials: false,  // request with credentials
  responseType: 'json',    // response type like 'json', 'text', 'blob' etc.
  method: 'GET',           // possible methods: GET, POST, HEADER, PUT

  data: {},                // data of this request
  params: {},              // parameters of this request
  headers: {}              // headers of this request
}

for send methods the default method is 'POST'.

Sideloading Resources Option

Resources can hold bundles of other resources (for example for initial requests). With the sideload option you can populate your cache correctly after fetching sideload resources.

Example:

/* /continents returns the following JSON:

  {
    users: [{ id: 1 }, { id: 2 }, { id: 3 }],
    all_cities: [{ id: 12 }, { id: 13 }, { id: 14 }],
    countries: [{ id: 22 }, { id: 81 }, { id: 91 }]
  }
*/

Resource('/continents').one('continents', {
  sideload: {
    'users': 'users',
    'all_cities': 'cities',
    'countries': 'countries'
  }
});

// this resource exists without a new server request

Resource('/cities/:id', { id: 912 } ).one('cities');

Nested Resources Option

Sometimes your app requires additional info from other resources before it can process a single object or array with resource data. This options is useful to fetch your main object and all related resources to it (read above for another example).

Your Resource pointer will only be ready when all these additional resources are ready as well. This is very useful since you want your data to be ready to be available for the controllers and the view.

The nested option takes an object with resource name properties and related key values.

Example:

/*

1) Data on server looks like this:

  continents: [
    { id: 1, country_ids: [ 12, 13 ], rivers: [ 82, 12 ] },
    { id: 2, country_ids: [ 17, 19, 20 ], rivers: [ 12 ] },
    { id: 3, country_ids: [ 8 ], rivers: [ 82, 16 ] }
  ]

  rivers: [
    { id: 12 },
    { id: 16 },
    { id: 82 }
  ]

  countries: [
    { id: 8 },
    { id: 12 },
    { id: 13 },
    { id: 17 },
    { id: 19 },
    { id: 20 }
  ]

2) we make a nested request with the client, this results in
the following requests (given that the cache is empty)

- GET /continents
- GET /rivers?id[]=12&id[]=16&id[]=82
- GET /countries?id[]=8&id[]=12&id[]=13&id[]=17&id[]=18&id[]=20

*/

Resource('/continents').one('continents', {
  nested: {
    'countries': 'country_ids',
    'rivers': 'rivers_ids'
  }
});

// 3) this resource exists without a new server request

Resource('/countries/:id', { id: 19 } ).one('countries');

Development

This code is part of an large app and written for an individual case but please feel free to send pull and feature requests to make it more generic!

Fetch respository and set up environment

git clone git@github.com:marmorkuchen-net/angular-watch-resource.git
npm install && bower install

Start a server on localhost:9000 which is checking your js syntax and running the tests in background after every save. You can also open a browser and check the examples here.

grunt serve

To build the source (in dist folder) just run

grunt