horizon/horizon/static/framework/util/promise-toggle/hz-promise-toggle.directive.js

147 lines
4.8 KiB
JavaScript

/**
* (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function () {
'use strict';
angular
.module('horizon.framework.util.promise-toggle')
.directive('hzPromiseToggleTemplate', hzPromiseToggleTemplate);
/**
* @ngdoc directive
* @name horizon.framework.util.promise-toggle:directive:hzPromiseToggleTemplate
* @module horizon.framework.util.promise-toggle
* @description
*
* A template directive that can be used for creating additional directives
* which will only compile their transcluded content if all of the promises
* associated with the directive resolve.
*
* This supports declarative directives that compile in their
* content only after the related promises resolve. This is typically
* intended to be used for very fast checks (often cached) of whether or
* not a particular setting or required service is enabled. This should not
* be used for checks that have frequently changing content, because this
* is evaluated once per page load.
*
* The actual name of whatever directive this is after being extended
* (angular.extend) should be set to the input that will be passed
* into the promise resolver. The promise resolver will either resolve
* or reject the promise. If it resolves, the content inside of the
* element will be linked in. Otherwise, it will be removed completely.
* When the input is an array, each element of the array will be treated
* as a distinct input and a single promise resolver will be invoked for
* each input element. When that is done, all promises must resolve in
* order for the content to be linked in. If any of them are rejected,
* the element will be removed.
*
* When extending, the name and singlePromiseResolver must be specified.
* Other properties may also be overridden for additional customization.
*
* @example
*
* To use this, simply create a concrete directive using angular.extend:
*
* angular
* .module('horizon.framework.util.promise-toggle')
* .directive('hzPromiseToggleMock', hzPromiseToggleMock);
*
* hzPromiseToggleMock.$inject = [
* 'hzPromiseToggleTemplateDirective',
* 'mockService'
* ];
*
* function hzPromiseToggleMock(hzPromiseToggleTemplateDirective, mockService) {
* return angular.extend(
* hzPromiseToggleTemplateDirective[0],
* {
* singlePromiseResolver: mockService.mockResolver,
* name: 'hzPromiseToggleMock'
* }
* );
* }
*
* Then in the HMTL:
*
* <div hz-promize-toggle-mock='["Hello"]'>Your content </div>
* For single input / single promise resolution:
*
* <div hz-promize-toggle-mock='["Hello", "World"]'>Your content </div>
* For multiple input / multiple promise resolution:
*
*/
hzPromiseToggleTemplate.$inject = ['$q', '$parse'];
function hzPromiseToggleTemplate($q, $parse) {
var directive = {
name: null,
singlePromiseResolver: null,
transclude: 'element',
priority: 2000,
terminal: true,
restrict: 'A',
compile: compile,
$$tlb: true //eslint-disable-line angular/no-private-call
};
return directive;
////////////////
function compile(element, attrs, linker) {
var input = $parse(attrs[this.name]);
var singlePromiseResolver = this.singlePromiseResolver;
return resolvePromises;
////////////////
function resolvePromises(scope, iterStartElement) {
var resolvedInput = input(scope);
var promiseResolver = angular.isArray(resolvedInput)
? multiPromiseResolver(singlePromiseResolver, resolvedInput)
: singlePromiseResolver(resolvedInput);
promiseResolver.then(linkContent, removeContent);
function linkContent() {
linker(scope, function (clone) {
iterStartElement.after(clone);
});
}
function removeContent() {
element.remove();
}
function multiPromiseResolver(resolver, arrayInput) {
// Resolves each individual input against the promise resolver.
// If any fail, all will fail.
return $q.all(
arrayInput.map(function (singleInput) {
return resolver(singleInput);
})
);
}
}
}
}
})();