X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=js%2Fangular-crm-util.js;h=3a1a8fd54ff177827d12d023028c6dbe7c0deb58;hb=8935eb8546c4710a52602f0d8f931e03391dafef;hp=b889346dcfd0e4cbee68d33be71a29c03965b5b0;hpb=fa92b4af1c4abf280689cea6445c7a9b99858831;p=civicrm-core.git diff --git a/js/angular-crm-util.js b/js/angular-crm-util.js index b889346dcf..3a1a8fd54f 100644 --- a/js/angular-crm-util.js +++ b/js/angular-crm-util.js @@ -2,19 +2,28 @@ (function (angular, $, _) { angular.module('crmUtil', []); + // usage: + // crmApi('Entity', 'action', {...}).then(function(apiResult){...}) + // + // Note: To mock API results in unit-tests, override crmApi.backend, e.g. + // var apiSpy = jasmine.createSpy('crmApi'); + // crmApi.backend = apiSpy.and.returnValue(crmApi.val({ + // is_error: 1 + // })); angular.module('crmUtil').factory('crmApi', function($q) { - return function(entity, action, params, message) { + var crmApi = function(entity, action, params, message) { // JSON serialization in CRM.api3 is not aware of Angular metadata like $$hash, so use angular.toJson() var deferred = $q.defer(); var p; + var backend = crmApi.backend || CRM.api3; if (_.isObject(entity)) { // eval content is locally generated. /*jshint -W061 */ - p = CRM.api3(eval('('+angular.toJson(entity)+')'), message); + p = backend(eval('('+angular.toJson(entity)+')'), message); } else { // eval content is locally generated. /*jshint -W061 */ - p = CRM.api3(entity, action, eval('('+angular.toJson(params)+')'), message); + p = backend(entity, action, eval('('+angular.toJson(params)+')'), message); } // CRM.api3 returns a promise, but the promise doesn't really represent errors as errors, so we // convert them @@ -32,6 +41,113 @@ ); return deferred.promise; }; + crmApi.backend = null; + crmApi.val = function(value) { + var d = $.Deferred(); + d.resolve(value); + return d.promise(); + }; + return crmApi; + }); + + // Get and cache the metadata for an API entity. + // usage: + // $q.when(crmMetadata.getFields('MyEntity'), function(fields){ + // console.log('The fields are:', options); + // }); + angular.module('crmUtil').factory('crmMetadata', function($q, crmApi) { + + // Convert {key:$,value:$} sequence to unordered {$key: $value} map. + function convertOptionsToMap(options) { + var result = {}; + angular.forEach(options, function(o) { + result[o.key] = o.value; + }); + return result; + } + + var cache = {}; // cache[entityName+'::'+action][fieldName].title + var deferreds = {}; // deferreds[cacheKey].push($q.defer()) + var crmMetadata = { + // usage: $q.when(crmMetadata.getField('MyEntity', 'my_field')).then(...); + getField: function getField(entity, field) { + return $q.when(crmMetadata.getFields(entity)).then(function(fields){ + return fields[field]; + }); + }, + // usage: $q.when(crmMetadata.getFields('MyEntity')).then(...); + // usage: $q.when(crmMetadata.getFields(['MyEntity', 'myaction'])).then(...); + getFields: function getFields(entity) { + var action = '', cacheKey; + if (_.isArray(entity)) { + action = entity[1]; + entity = entity[0]; + cacheKey = entity + '::' + action; + } else { + cacheKey = entity; + } + + if (_.isObject(cache[cacheKey])) { + return cache[cacheKey]; + } + + var needFetch = _.isEmpty(deferreds[cacheKey]); + deferreds[cacheKey] = deferreds[cacheKey] || []; + var deferred = $q.defer(); + deferreds[cacheKey].push(deferred); + + if (needFetch) { + crmApi(entity, 'getfields', {action: action, sequential: 1, options: {get_options: 'all'}}) + .then( + // on success: + function(fields) { + cache[cacheKey] = _.indexBy(fields.values, 'name'); + angular.forEach(cache[cacheKey],function (field){ + if (field.options) { + field.optionsMap = convertOptionsToMap(field.options); + } + }); + angular.forEach(deferreds[cacheKey], function(dfr) { + dfr.resolve(cache[cacheKey]); + }); + delete deferreds[cacheKey]; + }, + // on error: + function() { + cache[cacheKey] = {}; // cache nack + angular.forEach(deferreds[cacheKey], function(dfr) { + dfr.reject(); + }); + delete deferreds[cacheKey]; + } + ); + } + + return deferred.promise; + } + }; + + return crmMetadata; + }); + + // usage: + // var block = $scope.block = crmBlocker(); + // $scope.save = function() { return block(crmApi('MyEntity','create',...)); }; + // + angular.module('crmUtil').factory('crmBlocker', function() { + return function() { + var blocks = 0; + var result = function(promise) { + blocks++; + return promise.finally(function() { + blocks--; + }); + }; + result.check = function() { + return blocks > 0; + }; + return result; + }; }); angular.module('crmUtil').factory('crmLegacy', function() { @@ -101,7 +217,11 @@ // example: crmStatus('Saving', crmApi(...)).then(function(result){...}) angular.module('crmUtil').factory('crmStatus', function($q){ return function(options, aPromise){ - return CRM.toAPromise($q, CRM.status(options, CRM.toJqPromise(aPromise))); + if (aPromise) { + return CRM.toAPromise($q, CRM.status(options, CRM.toJqPromise(aPromise))); + } else { + return CRM.toAPromise($q, CRM.status(options)); + } }; });