UNPKG

angular-scaffold

Version:

Angular Scaffold is a collection of convenience wrappers around angular-model collections.

413 lines (342 loc) 9.43 kB
/** * Angular Scaffold is a collection of convenience wrappers around angular-model collections. * * {@copyright 2015, Radify, Inc (http://radify.io/)} * {@link https://github.com/radify/angular-model#readme} * * @license BSD-3-Clause */ (function(window, angular, undefined) { 'use strict'; /** * @ngdoc overview * @name ur.scaffold * @requires angular * @requires ur.model * @description * Angular Scaffold is a collection of convenience wrappers around angular-model collections. */ angular.module('ur.scaffold', ['ur.model']) /** * @ngdoc object * @name ur.scaffold * @requires angular * @requires ur.model * @description * Provider for angular-scaffold * * var s = scaffold("Dogs", { * paginate: { size: 1, strategy: 'infinite' } * }); */ .provider('scaffold', function() { var modelClass, q; var registry = {}; function ScaffoldClass(options) { var self = this, config = angular.extend({ query: {} }, options), paginate = { size: 10, page: 1, strategy: 'paged' }, total = 0; if (angular.isObject(options.paginate)) { config.paginate = angular.extend({}, paginate, options.paginate); } if (options.paginate === true) { config.paginate = paginate; } function paginateHeaders() { if (config.paginate) { var size = config.paginate.size, page = config.paginate.page, first = size * (page - 1), last = (size * page) - 1; return { 'Range': 'resources=' + first + '-' + last }; } return null; } function getPages(headers) { if(!config.paginate || !headers['content-range']) { return []; } var regex = /^resources \d+-\d+\/(\d+|\*)$/, matches = headers['content-range'].match(regex); if (matches === null || angular.isUndefined(matches[1]) || matches[1] === '*') { return null; } total = matches[1]; return Math.ceil(total / config.paginate.size); } angular.extend(this, { /** * @ngdoc number * @name ur.scaffold:pages * @propertyOf ur.scaffold * @description * the count of pages that the API reported to the scaffold */ pages: null, /** * @ngdoc object * @name ur.scaffold:$ui * @propertyOf ur.scaffold * @description * User interface related convenience properties, so in your UI, you can show saving and loading states */ $ui: { loading: false, saving: false, }, /** * @ngdoc function * @name ur.scaffold:$config * @methodOf ur.scaffold * @description * Get the configuration for this angular-scaffold instance * @returns {object} Configuration of this angular-scaffold instance */ $config: function() { return config; }, /** * @ngdoc function * @name ur.scaffold:$init * @methodOf ur.scaffold * @description * Initialise this scaffold * @returns {object} Returns this object, supporting method chaining */ $init: function() { if (modelClass && !angular.isObject(config.model)) { config.model = modelClass(config.model); } return this.refresh(); }, /** * @ngdoc function * @name ur.scaffold:model * @methodOf ur.scaffold * @description * Returns the configuration for this angular-scaffold instance * @returns {string} The underlying model this object is configured with */ model: function() { return config.model; }, /** * @ngdoc function * @name ur.scaffold:query * @methodOf ur.scaffold * @description * Get the configuration for this angular-scaffold instance * * @param {object} query e.g. { name: 'name to search for' } * @param {number=} page If supplied, used to determine which page of results to show * @returns {object} Configuration of this angular-scaffold instance */ query: function(query, page) { config.query = query; if (page) { return this.page(page); } return this.refresh(); }, /** * @ngdoc function * @name ur.scaffold:page * @methodOf ur.scaffold * @description * Select a page * * @param {number} page Page of results to show * @returns {object} Configuration of this angular-scaffold instance */ page: function(page) { config.paginate.page = page; if (config.paginate.strategy === 'infinite') { return this.refresh({append: true}); } return this.refresh(); }, /** * @ngdoc function * @name ur.scaffold:total * @methodOf ur.scaffold * @description * How many results in total are in the scaffold (not just the current page) * * @returns {number} Total results */ total: function() { return total; }, /** * @ngdoc function * @name ur.scaffold:refresh * @methodOf ur.scaffold * @description * Go to the API and refresh * @param {object=} options Query options to pass to the underlying angular-model `all` query * * @returns {object} Returns this object, supporting method chaining */ refresh: function(options) { options = options || { append: false }; this.$ui.loading = true; var promise = config.model.all(config.query, paginateHeaders()); var success = function(data) { self.pages = getPages(promise.$response.headers()); if (options.append === true) { self.items = self.items.concat(data); return data; } self.items = data; return data; }; var error = function(data) { self.items = []; }; var cleanup = function() { self.$ui.loading = false; }; if (angular.version.minor > 1) { promise.then(success).catch(error).finally(cleanup); } else { promise.then(success, error).always(cleanup); } if (angular.isFunction(config.callback)) { promise.then(config.callback); } return this; }, /** * @ngdoc function * @name create * @methodOf ur.scaffold * @description * Creates a new object when the deferred promise is resolved * @returns {object} Deferred promise */ create: function() { var deferred = q.defer(), defaults = { save: true }, saved = function() { self.items.push(deferred.$instance); self.$ui.saving = false; }; deferred.promise.then(function(options) { self.$ui.saving = true; options = angular.extend({}, defaults, options); if (options.save === false) { return saved(); } deferred.$instance.$save().then(saved); }); deferred.$instance = config.model.create(); return deferred; }, /** * @ngdoc function * @name edit * @methodOf ur.scaffold * @param {(number|object)} index Either the index of the item in the collection to edit, * or the object itself, which will be searched for in the collection * @description * Find `index` and set it up for editing. * * Updates the object when the deferred promise is resolved * @returns {object} Deferred promise */ edit: function(index) { var deferred = q.defer(), defaults = { save: true }, saved = function() { self.items[index] = deferred.$instance; self.$ui.saving = false; }; deferred.promise.then(function(options) { self.$ui.saving = true; options = angular.extend({}, defaults, options); if (options.save === false) { return saved(); } deferred.$instance.$save().then(saved); }); deferred.$instance = angular.copy(this.items[index]); return deferred; }, /** * @ngdoc function * @name delete * @methodOf ur.scaffold * @param {(number|object)} index Either the index of the item in the collection to remove, or the object * itself, which will be searched for in the collection * @description * Find `index` and delete it from the API, then remove it from the collection * @returns {object} Promise */ delete: function(index) { var deferred = q.defer(); deferred.promise.then(function(data) { self.$ui.saving = true; deferred.$instance.$delete().then(function() { self.$ui.saving = false; }); }); deferred.$instance = this.items[index]; return deferred; } }); } function config(name, options) { if (registry[name]) { var current = registry[name].$config(); registry[name] = new ScaffoldClass(angular.extend({}, current, options)); } else { if (angular.isUndefined(options.model)) { options.model = name; } registry[name] = new ScaffoldClass(options); } return registry[name]; } angular.extend(this, { /** * @ngdoc function * @name ur.scaffoldProvider:scaffold * * @description * Configure a scaffold * * @example * var s = scaffold("Dogs", { paginate: true }); * @param {string} name The name * @param {object=} options Configuration object * @returns {object} Created scaffold object */ scaffold: function(name, options) { config(name, options); return this; }, $get: ['$q', 'model', function($q, model) { modelClass = model; q = $q; function ScaffoldClassFactory(name, options) { if (!angular.isUndefined(options)) { config(name, options); } return registry[name].$init() || undefined; } return ScaffoldClassFactory; }] }); }); })(window, window.angular);