2016-06-23 09:47:54 -07:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
|
*/
|
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
import {Directive, DoCheck, ElementRef, EventEmitter, Inject, OnChanges, OnInit, SimpleChange, SimpleChanges, Type} from '@angular/core';
|
|
|
|
|
|
2015-10-26 20:17:46 -07:00
|
|
|
import * as angular from './angular_js';
|
2016-06-08 16:38:52 -07:00
|
|
|
import {NG1_COMPILE, NG1_CONTROLLER, NG1_HTTP_BACKEND, NG1_SCOPE, NG1_TEMPLATE_CACHE} from './constants';
|
|
|
|
|
import {controllerKey} from './util';
|
2015-10-04 09:33:20 -07:00
|
|
|
|
|
|
|
|
const CAMEL_CASE = /([A-Z])/g;
|
|
|
|
|
const INITIAL_VALUE = {
|
|
|
|
|
__UNINITIALIZED__: true
|
|
|
|
|
};
|
2015-10-11 11:18:11 -07:00
|
|
|
const NOT_SUPPORTED: any = 'NOT_SUPPORTED';
|
2015-10-04 09:33:20 -07:00
|
|
|
|
|
|
|
|
|
2015-10-12 21:32:41 -07:00
|
|
|
export class UpgradeNg1ComponentAdapterBuilder {
|
2016-08-10 18:21:28 -07:00
|
|
|
type: Type<any>;
|
2015-10-04 09:33:20 -07:00
|
|
|
inputs: string[] = [];
|
|
|
|
|
inputsRename: string[] = [];
|
|
|
|
|
outputs: string[] = [];
|
|
|
|
|
outputsRename: string[] = [];
|
|
|
|
|
propertyOutputs: string[] = [];
|
|
|
|
|
checkProperties: string[] = [];
|
|
|
|
|
propertyMap: {[name: string]: string} = {};
|
2015-10-11 11:18:11 -07:00
|
|
|
linkFn: angular.ILinkFn = null;
|
|
|
|
|
directive: angular.IDirective = null;
|
|
|
|
|
$controller: angular.IControllerService = null;
|
2015-10-04 09:33:20 -07:00
|
|
|
|
|
|
|
|
constructor(public name: string) {
|
2016-11-12 14:08:58 +01:00
|
|
|
const selector = name.replace(
|
2016-06-08 16:38:52 -07:00
|
|
|
CAMEL_CASE, (all: any /** TODO #9100 */, next: string) => '-' + next.toLowerCase());
|
2016-11-12 14:08:58 +01:00
|
|
|
const self = this;
|
2015-10-04 09:33:20 -07:00
|
|
|
this.type =
|
|
|
|
|
Directive({selector: selector, inputs: this.inputsRename, outputs: this.outputsRename})
|
|
|
|
|
.Class({
|
|
|
|
|
constructor: [
|
2016-06-08 16:38:52 -07:00
|
|
|
new Inject(NG1_SCOPE), ElementRef,
|
2015-10-11 11:18:11 -07:00
|
|
|
function(scope: angular.IScope, elementRef: ElementRef) {
|
|
|
|
|
return new UpgradeNg1ComponentAdapter(
|
|
|
|
|
self.linkFn, scope, self.directive, elementRef, self.$controller, self.inputs,
|
|
|
|
|
self.outputs, self.propertyOutputs, self.checkProperties, self.propertyMap);
|
2015-10-04 09:33:20 -07:00
|
|
|
}
|
|
|
|
|
],
|
2016-03-14 07:51:04 +01:00
|
|
|
ngOnInit: function() { /* needs to be here for ng2 to properly detect it */ },
|
refactor(lifecycle): prefix lifecycle methods with "ng"
BREAKING CHANGE:
Previously, components that would implement lifecycle interfaces would include methods
like "onChanges" or "afterViewInit." Given that components were at risk of using such
names without realizing that Angular would call the methods at different points of
the component lifecycle. This change adds an "ng" prefix to all lifecycle hook methods,
far reducing the risk of an accidental name collision.
To fix, just rename these methods:
* onInit
* onDestroy
* doCheck
* onChanges
* afterContentInit
* afterContentChecked
* afterViewInit
* afterViewChecked
* _Router Hooks_
* onActivate
* onReuse
* onDeactivate
* canReuse
* canDeactivate
To:
* ngOnInit,
* ngOnDestroy,
* ngDoCheck,
* ngOnChanges,
* ngAfterContentInit,
* ngAfterContentChecked,
* ngAfterViewInit,
* ngAfterViewChecked
* _Router Hooks_
* routerOnActivate
* routerOnReuse
* routerOnDeactivate
* routerCanReuse
* routerCanDeactivate
The names of lifecycle interfaces and enums have not changed, though interfaces
have been updated to reflect the new method names.
Closes #5036
2015-11-16 17:04:36 -08:00
|
|
|
ngOnChanges: function() { /* needs to be here for ng2 to properly detect it */ },
|
2016-11-18 14:46:49 -07:00
|
|
|
ngDoCheck: function() { /* needs to be here for ng2 to properly detect it */ },
|
|
|
|
|
ngOnDestroy: function() { /* needs to be here for ng2 to properly detect it */ },
|
2015-10-04 09:33:20 -07:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-26 20:17:46 -07:00
|
|
|
extractDirective(injector: angular.IInjectorService): angular.IDirective {
|
2016-11-12 14:08:58 +01:00
|
|
|
const directives: angular.IDirective[] = injector.get(this.name + 'Directive');
|
2015-10-04 09:33:20 -07:00
|
|
|
if (directives.length > 1) {
|
|
|
|
|
throw new Error('Only support single directive definition for: ' + this.name);
|
|
|
|
|
}
|
2016-11-12 14:08:58 +01:00
|
|
|
const directive = directives[0];
|
2015-10-11 11:18:11 -07:00
|
|
|
if (directive.replace) this.notSupported('replace');
|
|
|
|
|
if (directive.terminal) this.notSupported('terminal');
|
2016-11-12 14:08:58 +01:00
|
|
|
const link = directive.link;
|
2015-10-11 11:18:11 -07:00
|
|
|
if (typeof link == 'object') {
|
|
|
|
|
if ((<angular.IDirectivePrePost>link).post) this.notSupported('link.post');
|
|
|
|
|
}
|
|
|
|
|
return directive;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private notSupported(feature: string) {
|
|
|
|
|
throw new Error(`Upgraded directive '${this.name}' does not support '${feature}'.`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extractBindings() {
|
2016-11-12 14:08:58 +01:00
|
|
|
const btcIsObject = typeof this.directive.bindToController === 'object';
|
2015-11-13 18:55:40 +01:00
|
|
|
if (btcIsObject && Object.keys(this.directive.scope).length) {
|
|
|
|
|
throw new Error(
|
|
|
|
|
`Binding definitions on scope and controller at the same time are not supported.`);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-12 14:08:58 +01:00
|
|
|
const context = (btcIsObject) ? this.directive.bindToController : this.directive.scope;
|
2015-11-13 18:55:40 +01:00
|
|
|
|
|
|
|
|
if (typeof context == 'object') {
|
2016-11-12 14:08:58 +01:00
|
|
|
for (const name in context) {
|
2015-11-13 18:55:40 +01:00
|
|
|
if ((<any>context).hasOwnProperty(name)) {
|
2016-11-12 14:08:58 +01:00
|
|
|
let localName = context[name];
|
|
|
|
|
const type = localName.charAt(0);
|
|
|
|
|
const typeOptions = localName.charAt(1);
|
2016-09-29 18:45:28 +02:00
|
|
|
localName = typeOptions === '?' ? localName.substr(2) : localName.substr(1);
|
|
|
|
|
localName = localName || name;
|
|
|
|
|
|
2016-11-12 14:08:58 +01:00
|
|
|
const outputName = 'output_' + name;
|
|
|
|
|
const outputNameRename = outputName + ': ' + name;
|
|
|
|
|
const outputNameRenameChange = outputName + ': ' + name + 'Change';
|
|
|
|
|
const inputName = 'input_' + name;
|
|
|
|
|
const inputNameRename = inputName + ': ' + name;
|
2015-10-04 09:33:20 -07:00
|
|
|
switch (type) {
|
|
|
|
|
case '=':
|
|
|
|
|
this.propertyOutputs.push(outputName);
|
|
|
|
|
this.checkProperties.push(localName);
|
|
|
|
|
this.outputs.push(outputName);
|
2015-10-10 19:56:22 -07:00
|
|
|
this.outputsRename.push(outputNameRenameChange);
|
2015-10-04 09:33:20 -07:00
|
|
|
this.propertyMap[outputName] = localName;
|
2016-06-22 15:57:24 -07:00
|
|
|
this.inputs.push(inputName);
|
|
|
|
|
this.inputsRename.push(inputNameRename);
|
|
|
|
|
this.propertyMap[inputName] = localName;
|
|
|
|
|
break;
|
2015-10-04 09:33:20 -07:00
|
|
|
case '@':
|
2016-03-14 07:51:04 +01:00
|
|
|
// handle the '<' binding of angular 1.5 components
|
|
|
|
|
case '<':
|
2015-10-04 09:33:20 -07:00
|
|
|
this.inputs.push(inputName);
|
|
|
|
|
this.inputsRename.push(inputNameRename);
|
|
|
|
|
this.propertyMap[inputName] = localName;
|
|
|
|
|
break;
|
|
|
|
|
case '&':
|
|
|
|
|
this.outputs.push(outputName);
|
|
|
|
|
this.outputsRename.push(outputNameRename);
|
|
|
|
|
this.propertyMap[outputName] = localName;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2016-11-12 14:08:58 +01:00
|
|
|
let json = JSON.stringify(context);
|
2015-10-04 09:33:20 -07:00
|
|
|
throw new Error(
|
|
|
|
|
`Unexpected mapping '${type}' in '${json}' in '${this.name}' directive.`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
compileTemplate(
|
|
|
|
|
compile: angular.ICompileService, templateCache: angular.ITemplateCacheService,
|
2016-08-05 13:32:04 -07:00
|
|
|
httpBackend: angular.IHttpBackendService): Promise<angular.ILinkFn> {
|
2015-12-10 13:23:47 +01:00
|
|
|
if (this.directive.template !== undefined) {
|
2016-06-08 16:38:52 -07:00
|
|
|
this.linkFn = compileHtml(
|
|
|
|
|
typeof this.directive.template === 'function' ? this.directive.template() :
|
|
|
|
|
this.directive.template);
|
2015-10-11 11:18:11 -07:00
|
|
|
} else if (this.directive.templateUrl) {
|
2016-11-12 14:08:58 +01:00
|
|
|
const url = typeof this.directive.templateUrl === 'function' ? this.directive.templateUrl() :
|
|
|
|
|
this.directive.templateUrl;
|
|
|
|
|
const html = templateCache.get(url);
|
2015-10-11 11:18:11 -07:00
|
|
|
if (html !== undefined) {
|
|
|
|
|
this.linkFn = compileHtml(html);
|
|
|
|
|
} else {
|
|
|
|
|
return new Promise((resolve, err) => {
|
2016-06-08 16:38:52 -07:00
|
|
|
httpBackend(
|
|
|
|
|
'GET', url, null,
|
|
|
|
|
(status: any /** TODO #9100 */, response: any /** TODO #9100 */) => {
|
|
|
|
|
if (status == 200) {
|
|
|
|
|
resolve(this.linkFn = compileHtml(templateCache.put(url, response)));
|
|
|
|
|
} else {
|
|
|
|
|
err(`GET ${url} returned ${status}: ${response}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
2015-10-11 11:18:11 -07:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
throw new Error(`Directive '${this.name}' is not a component, it is missing template.`);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
2016-06-08 15:45:15 -07:00
|
|
|
function compileHtml(html: any /** TODO #9100 */): angular.ILinkFn {
|
2016-11-12 14:08:58 +01:00
|
|
|
const div = document.createElement('div');
|
2015-10-11 11:18:11 -07:00
|
|
|
div.innerHTML = html;
|
|
|
|
|
return compile(div.childNodes);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-05 13:32:04 -07:00
|
|
|
/**
|
|
|
|
|
* Upgrade ng1 components into Angular 2.
|
|
|
|
|
*/
|
2016-06-08 16:38:52 -07:00
|
|
|
static resolve(
|
|
|
|
|
exportedComponents: {[name: string]: UpgradeNg1ComponentAdapterBuilder},
|
2016-08-05 13:32:04 -07:00
|
|
|
injector: angular.IInjectorService): Promise<angular.ILinkFn[]> {
|
2016-11-12 14:08:58 +01:00
|
|
|
const promises: Promise<angular.ILinkFn>[] = [];
|
|
|
|
|
const compile: angular.ICompileService = injector.get(NG1_COMPILE);
|
|
|
|
|
const templateCache: angular.ITemplateCacheService = injector.get(NG1_TEMPLATE_CACHE);
|
|
|
|
|
const httpBackend: angular.IHttpBackendService = injector.get(NG1_HTTP_BACKEND);
|
|
|
|
|
const $controller: angular.IControllerService = injector.get(NG1_CONTROLLER);
|
|
|
|
|
for (const name in exportedComponents) {
|
2015-10-09 21:19:00 -07:00
|
|
|
if ((<any>exportedComponents).hasOwnProperty(name)) {
|
2016-11-12 14:08:58 +01:00
|
|
|
const exportedComponent = exportedComponents[name];
|
2015-10-11 11:18:11 -07:00
|
|
|
exportedComponent.directive = exportedComponent.extractDirective(injector);
|
|
|
|
|
exportedComponent.$controller = $controller;
|
|
|
|
|
exportedComponent.extractBindings();
|
2016-11-12 14:08:58 +01:00
|
|
|
const promise: Promise<angular.ILinkFn> =
|
2016-08-05 13:32:04 -07:00
|
|
|
exportedComponent.compileTemplate(compile, templateCache, httpBackend);
|
2015-10-26 20:17:46 -07:00
|
|
|
if (promise) promises.push(promise);
|
2015-10-04 09:33:20 -07:00
|
|
|
}
|
|
|
|
|
}
|
2015-10-11 11:18:11 -07:00
|
|
|
return Promise.all(promises);
|
2015-10-04 09:33:20 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-06 14:13:44 -08:00
|
|
|
class UpgradeNg1ComponentAdapter implements OnInit, OnChanges, DoCheck {
|
2015-10-11 11:18:11 -07:00
|
|
|
destinationObj: any = null;
|
2015-10-04 09:33:20 -07:00
|
|
|
checkLastValues: any[] = [];
|
2016-01-06 14:13:44 -08:00
|
|
|
componentScope: angular.IScope;
|
|
|
|
|
element: Element;
|
2016-05-17 14:53:59 -07:00
|
|
|
$element: any = null;
|
2015-10-04 09:33:20 -07:00
|
|
|
|
2016-06-08 16:38:52 -07:00
|
|
|
constructor(
|
|
|
|
|
private linkFn: angular.ILinkFn, scope: angular.IScope, private directive: angular.IDirective,
|
|
|
|
|
elementRef: ElementRef, private $controller: angular.IControllerService,
|
|
|
|
|
private inputs: string[], private outputs: string[], private propOuts: string[],
|
|
|
|
|
private checkProperties: string[], private propertyMap: {[key: string]: string}) {
|
2016-01-06 14:13:44 -08:00
|
|
|
this.element = elementRef.nativeElement;
|
|
|
|
|
this.componentScope = scope.$new(!!directive.scope);
|
2016-05-17 14:53:59 -07:00
|
|
|
this.$element = angular.element(this.element);
|
2016-11-12 14:08:58 +01:00
|
|
|
const controllerType = directive.controller;
|
2016-05-17 14:53:59 -07:00
|
|
|
if (directive.bindToController && controllerType) {
|
|
|
|
|
this.destinationObj = this.buildController(controllerType);
|
|
|
|
|
} else {
|
|
|
|
|
this.destinationObj = this.componentScope;
|
2015-10-11 11:18:11 -07:00
|
|
|
}
|
2015-10-04 09:33:20 -07:00
|
|
|
|
2016-11-12 14:08:58 +01:00
|
|
|
for (let i = 0; i < inputs.length; i++) {
|
2016-06-08 15:45:15 -07:00
|
|
|
(this as any /** TODO #9100 */)[inputs[i]] = null;
|
2015-10-04 09:33:20 -07:00
|
|
|
}
|
2016-11-12 14:08:58 +01:00
|
|
|
for (let j = 0; j < outputs.length; j++) {
|
|
|
|
|
const emitter = (this as any /** TODO #9100 */)[outputs[j]] = new EventEmitter();
|
2016-06-08 16:38:52 -07:00
|
|
|
this.setComponentProperty(
|
|
|
|
|
outputs[j], ((emitter: any /** TODO #9100 */) => (value: any /** TODO #9100 */) =>
|
|
|
|
|
emitter.emit(value))(emitter));
|
2015-10-04 09:33:20 -07:00
|
|
|
}
|
2016-11-12 14:08:58 +01:00
|
|
|
for (let k = 0; k < propOuts.length; k++) {
|
2016-06-08 15:45:15 -07:00
|
|
|
(this as any /** TODO #9100 */)[propOuts[k]] = new EventEmitter();
|
2015-10-04 09:33:20 -07:00
|
|
|
this.checkLastValues.push(INITIAL_VALUE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-14 07:51:04 +01:00
|
|
|
ngOnInit() {
|
2016-05-17 14:53:59 -07:00
|
|
|
if (!this.directive.bindToController && this.directive.controller) {
|
2016-05-26 13:33:53 -07:00
|
|
|
this.buildController(this.directive.controller);
|
2016-05-17 14:53:59 -07:00
|
|
|
}
|
2016-11-12 14:08:58 +01:00
|
|
|
let link = this.directive.link;
|
2016-05-17 14:53:59 -07:00
|
|
|
if (typeof link == 'object') link = (<angular.IDirectivePrePost>link).pre;
|
|
|
|
|
if (link) {
|
2016-11-12 14:08:58 +01:00
|
|
|
const attrs: angular.IAttributes = NOT_SUPPORTED;
|
|
|
|
|
const transcludeFn: angular.ITranscludeFunction = NOT_SUPPORTED;
|
|
|
|
|
const linkController = this.resolveRequired(this.$element, this.directive.require);
|
2016-06-08 16:38:52 -07:00
|
|
|
(<angular.IDirectiveLinkFn>this.directive.link)(
|
|
|
|
|
this.componentScope, this.$element, attrs, linkController, transcludeFn);
|
2016-05-17 14:53:59 -07:00
|
|
|
}
|
|
|
|
|
|
2016-11-12 14:08:58 +01:00
|
|
|
const childNodes: Node[] = [];
|
|
|
|
|
let childNode: any /** TODO #9100 */;
|
2016-01-06 14:13:44 -08:00
|
|
|
while (childNode = this.element.firstChild) {
|
|
|
|
|
this.element.removeChild(childNode);
|
|
|
|
|
childNodes.push(childNode);
|
|
|
|
|
}
|
2016-10-19 21:41:04 +01:00
|
|
|
this.linkFn(this.componentScope, (clonedElement, scope) => {
|
2016-11-12 14:08:58 +01:00
|
|
|
for (let i = 0, ii = clonedElement.length; i < ii; i++) {
|
2016-01-06 14:13:44 -08:00
|
|
|
this.element.appendChild(clonedElement[i]);
|
|
|
|
|
}
|
2016-06-08 16:38:52 -07:00
|
|
|
}, {
|
|
|
|
|
parentBoundTranscludeFn: (scope: any /** TODO #9100 */,
|
|
|
|
|
cloneAttach: any /** TODO #9100 */) => { cloneAttach(childNodes); }
|
|
|
|
|
});
|
2016-03-14 07:51:04 +01:00
|
|
|
if (this.destinationObj.$onInit) {
|
|
|
|
|
this.destinationObj.$onInit();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-09 15:45:04 -07:00
|
|
|
ngOnChanges(changes: SimpleChanges) {
|
2016-11-18 14:46:49 -07:00
|
|
|
const ng1Changes: any = {};
|
|
|
|
|
Object.keys(changes).forEach(name => {
|
|
|
|
|
const change: SimpleChange = changes[name];
|
|
|
|
|
this.setComponentProperty(name, change.currentValue);
|
|
|
|
|
ng1Changes[this.propertyMap[name]] = change;
|
|
|
|
|
});
|
|
|
|
|
if (this.destinationObj.$onChanges) {
|
|
|
|
|
this.destinationObj.$onChanges(ng1Changes);
|
2015-10-04 09:33:20 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-18 14:46:49 -07:00
|
|
|
ngDoCheck() {
|
2016-11-12 14:08:58 +01:00
|
|
|
const destinationObj = this.destinationObj;
|
|
|
|
|
const lastValues = this.checkLastValues;
|
|
|
|
|
const checkProperties = this.checkProperties;
|
|
|
|
|
for (let i = 0; i < checkProperties.length; i++) {
|
|
|
|
|
const value = destinationObj[checkProperties[i]];
|
|
|
|
|
const last = lastValues[i];
|
2015-10-04 09:33:20 -07:00
|
|
|
if (value !== last) {
|
|
|
|
|
if (typeof value == 'number' && isNaN(value) && typeof last == 'number' && isNaN(last)) {
|
|
|
|
|
// ignore because NaN != NaN
|
|
|
|
|
} else {
|
2016-11-12 14:08:58 +01:00
|
|
|
const eventEmitter: EventEmitter<any> = (this as any /** TODO #9100 */)[this.propOuts[i]];
|
2015-11-15 23:58:59 -08:00
|
|
|
eventEmitter.emit(lastValues[i] = value);
|
2015-10-04 09:33:20 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-11-18 14:46:49 -07:00
|
|
|
if (this.destinationObj.$doCheck && this.directive.controller) {
|
|
|
|
|
this.destinationObj.$doCheck();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ngOnDestroy() {
|
|
|
|
|
if (this.destinationObj.$onDestroy && this.directive.controller) {
|
|
|
|
|
this.destinationObj.$onDestroy();
|
|
|
|
|
}
|
2015-10-04 09:33:20 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setComponentProperty(name: string, value: any) {
|
2015-10-11 11:18:11 -07:00
|
|
|
this.destinationObj[this.propertyMap[name]] = value;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-08 15:45:15 -07:00
|
|
|
private buildController(controllerType: any /** TODO #9100 */) {
|
2016-11-12 14:08:58 +01:00
|
|
|
const locals = {$scope: this.componentScope, $element: this.$element};
|
|
|
|
|
const controller: any =
|
2016-06-08 16:38:52 -07:00
|
|
|
this.$controller(controllerType, locals, null, this.directive.controllerAs);
|
2016-05-17 14:53:59 -07:00
|
|
|
this.$element.data(controllerKey(this.directive.name), controller);
|
|
|
|
|
return controller;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-19 21:41:04 +01:00
|
|
|
private resolveRequired(
|
|
|
|
|
$element: angular.IAugmentedJQuery, require: angular.DirectiveRequireProperty): any {
|
2015-10-11 11:18:11 -07:00
|
|
|
if (!require) {
|
|
|
|
|
return undefined;
|
|
|
|
|
} else if (typeof require == 'string') {
|
2016-11-12 14:08:58 +01:00
|
|
|
let name: string = <string>require;
|
|
|
|
|
let isOptional = false;
|
|
|
|
|
let startParent = false;
|
|
|
|
|
let searchParents = false;
|
2015-10-11 11:18:11 -07:00
|
|
|
if (name.charAt(0) == '?') {
|
|
|
|
|
isOptional = true;
|
|
|
|
|
name = name.substr(1);
|
|
|
|
|
}
|
|
|
|
|
if (name.charAt(0) == '^') {
|
|
|
|
|
searchParents = true;
|
|
|
|
|
name = name.substr(1);
|
|
|
|
|
}
|
|
|
|
|
if (name.charAt(0) == '^') {
|
|
|
|
|
startParent = true;
|
|
|
|
|
name = name.substr(1);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-12 14:08:58 +01:00
|
|
|
const key = controllerKey(name);
|
2015-10-11 11:18:11 -07:00
|
|
|
if (startParent) $element = $element.parent();
|
2016-11-12 14:08:58 +01:00
|
|
|
const dep = searchParents ? $element.inheritedData(key) : $element.data(key);
|
2015-10-11 11:18:11 -07:00
|
|
|
if (!dep && !isOptional) {
|
|
|
|
|
throw new Error(`Can not locate '${require}' in '${this.directive.name}'.`);
|
|
|
|
|
}
|
|
|
|
|
return dep;
|
|
|
|
|
} else if (require instanceof Array) {
|
2016-11-12 14:08:58 +01:00
|
|
|
const deps: any[] = [];
|
|
|
|
|
for (let i = 0; i < require.length; i++) {
|
2015-10-11 11:18:11 -07:00
|
|
|
deps.push(this.resolveRequired($element, require[i]));
|
|
|
|
|
}
|
|
|
|
|
return deps;
|
|
|
|
|
}
|
|
|
|
|
throw new Error(
|
|
|
|
|
`Directive '${this.directive.name}' require syntax unrecognized: ${this.directive.require}`);
|
2015-10-04 09:33:20 -07:00
|
|
|
}
|
|
|
|
|
}
|