feat(UpgradeComponent): add support for `require`
This commit also adds/improves/fixes some `UpgradeComponent` tests.
This commit is contained in:
parent
469010ea8e
commit
fe1d0e29c5
|
@ -12,6 +12,8 @@ export interface IAnnotatedFunction extends Function { $inject?: Ng1Token[]; }
|
|||
|
||||
export type IInjectable = (Ng1Token | Function)[] | IAnnotatedFunction;
|
||||
|
||||
export type SingleOrListOrMap<T> = T | T[] | {[key: string]: T};
|
||||
|
||||
export interface IModule {
|
||||
name: string;
|
||||
requires: (string|IInjectable)[];
|
||||
|
@ -44,6 +46,7 @@ export interface IRootScopeService {
|
|||
$apply(): any;
|
||||
$apply(exp: string): any;
|
||||
$apply(exp: Function): any;
|
||||
$digest(): any;
|
||||
$evalAsync(): any;
|
||||
$on(event: string, fn?: (event?: any, ...args: any[]) => void): Function;
|
||||
$$childTail: IScope;
|
||||
|
@ -72,7 +75,7 @@ export interface IDirective {
|
|||
terminal?: boolean;
|
||||
transclude?: boolean|'element'|{[key: string]: string};
|
||||
}
|
||||
export type DirectiveRequireProperty = Ng1Token[] | Ng1Token | {[key: string]: Ng1Token};
|
||||
export type DirectiveRequireProperty = SingleOrListOrMap<string>;
|
||||
export interface IDirectiveCompileFn {
|
||||
(templateElement: IAugmentedJQuery, templateAttributes: IAttributes,
|
||||
transclude: ITranscludeFunction): IDirectivePrePost;
|
||||
|
|
|
@ -11,6 +11,7 @@ export const INJECTOR_KEY = '$$angularInjector';
|
|||
|
||||
export const $INJECTOR = '$injector';
|
||||
export const $PARSE = '$parse';
|
||||
export const $ROOT_SCOPE = '$rootScope';
|
||||
export const $SCOPE = '$scope';
|
||||
|
||||
export const $COMPILE = '$compile';
|
||||
|
|
|
@ -14,6 +14,7 @@ import {controllerKey} from '../util';
|
|||
|
||||
import {$COMPILE, $CONTROLLER, $HTTP_BACKEND, $INJECTOR, $SCOPE, $TEMPLATE_CACHE} from './constants';
|
||||
|
||||
const REQUIRE_PREFIX_RE = /^(\^\^?)?(\?)?(\^\^?)?/;
|
||||
const NOT_SUPPORTED: any = 'NOT_SUPPORTED';
|
||||
const INITIAL_VALUE = {
|
||||
__UNINITIALIZED__: true
|
||||
|
@ -101,7 +102,16 @@ export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
|
|||
ngOnInit() {
|
||||
const attrs: angular.IAttributes = NOT_SUPPORTED;
|
||||
const transcludeFn: angular.ITranscludeFunction = NOT_SUPPORTED;
|
||||
const linkController = this.resolveRequired(this.$element, this.directive.require);
|
||||
const directiveRequire = this.getDirectiveRequire(this.directive);
|
||||
let requiredControllers =
|
||||
this.resolveRequire(this.directive.name, this.$element, directiveRequire);
|
||||
|
||||
if (this.directive.bindToController && isMap(directiveRequire)) {
|
||||
const requiredControllersMap = requiredControllers as{[key: string]: IControllerInstance};
|
||||
Object.keys(requiredControllersMap).forEach(key => {
|
||||
this.controllerInstance[key] = requiredControllersMap[key];
|
||||
});
|
||||
}
|
||||
|
||||
this.callLifecycleHook('$onInit', this.controllerInstance);
|
||||
|
||||
|
@ -109,7 +119,7 @@ export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
|
|||
const preLink = (typeof link == 'object') && (link as angular.IDirectivePrePost).pre;
|
||||
const postLink = (typeof link == 'object') ? (link as angular.IDirectivePrePost).post : link;
|
||||
if (preLink) {
|
||||
preLink(this.$componentScope, this.$element, attrs, linkController, transcludeFn);
|
||||
preLink(this.$componentScope, this.$element, attrs, requiredControllers, transcludeFn);
|
||||
}
|
||||
|
||||
var childNodes: Node[] = [];
|
||||
|
@ -126,7 +136,7 @@ export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
|
|||
this.linkFn(this.$componentScope, attachElement, {parentBoundTranscludeFn: attachChildNodes});
|
||||
|
||||
if (postLink) {
|
||||
postLink(this.$componentScope, this.$element, attrs, linkController, transcludeFn);
|
||||
postLink(this.$componentScope, this.$element, attrs, requiredControllers, transcludeFn);
|
||||
}
|
||||
|
||||
this.callLifecycleHook('$postLink', this.controllerInstance);
|
||||
|
@ -187,6 +197,24 @@ export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
|
|||
return directive;
|
||||
}
|
||||
|
||||
private getDirectiveRequire(directive: angular.IDirective): angular.DirectiveRequireProperty {
|
||||
const require = directive.require || (directive.controller && directive.name);
|
||||
|
||||
if (isMap(require)) {
|
||||
Object.keys(require).forEach(key => {
|
||||
const value = require[key];
|
||||
const match = value.match(REQUIRE_PREFIX_RE);
|
||||
const name = value.substring(match[0].length);
|
||||
|
||||
if (!name) {
|
||||
require[key] = match[0] + key;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return require;
|
||||
}
|
||||
|
||||
private initializeBindings(directive: angular.IDirective) {
|
||||
const btcIsObject = typeof directive.bindToController === 'object';
|
||||
if (btcIsObject && Object.keys(directive.scope).length) {
|
||||
|
@ -266,9 +294,47 @@ export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
|
|||
return controller;
|
||||
}
|
||||
|
||||
private resolveRequired(
|
||||
$element: angular.IAugmentedJQuery, require: angular.DirectiveRequireProperty) {
|
||||
// TODO
|
||||
private resolveRequire(
|
||||
directiveName: string, $element: angular.IAugmentedJQuery,
|
||||
require: angular.DirectiveRequireProperty): angular.SingleOrListOrMap<IControllerInstance> {
|
||||
if (!require) {
|
||||
return null;
|
||||
} else if (Array.isArray(require)) {
|
||||
return require.map(req => this.resolveRequire(directiveName, $element, req));
|
||||
} else if (typeof require === 'object') {
|
||||
const value: {[key: string]: IControllerInstance} = {};
|
||||
|
||||
Object.keys(require).forEach(
|
||||
key => value[key] = this.resolveRequire(directiveName, $element, require[key]));
|
||||
|
||||
return value;
|
||||
} else if (typeof require === 'string') {
|
||||
const match = require.match(REQUIRE_PREFIX_RE);
|
||||
const inheritType = match[1] || match[3];
|
||||
|
||||
const name = require.substring(match[0].length);
|
||||
const isOptional = !!match[2];
|
||||
const searchParents = !!inheritType;
|
||||
const startOnParent = inheritType === '^^';
|
||||
|
||||
const ctrlKey = controllerKey(name);
|
||||
|
||||
if (startOnParent) {
|
||||
$element = $element.parent();
|
||||
}
|
||||
|
||||
const value = searchParents ? $element.inheritedData(ctrlKey) : $element.data(ctrlKey);
|
||||
|
||||
if (!value && !isOptional) {
|
||||
throw new Error(
|
||||
`Unable to find required '${require}' in upgraded directive '${directiveName}'.`);
|
||||
}
|
||||
|
||||
return value;
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unrecognized require syntax on upgraded directive '${directiveName}': ${require}`);
|
||||
}
|
||||
}
|
||||
|
||||
private setupOutputs() {
|
||||
|
@ -305,3 +371,8 @@ export class UpgradeComponent implements OnInit, OnChanges, DoCheck, OnDestroy {
|
|||
function getOrCall<T>(property: Function | T): T {
|
||||
return typeof(property) === 'function' ? property() : property;
|
||||
}
|
||||
|
||||
// NOTE: Only works for `typeof T !== 'object'`.
|
||||
function isMap<T>(value: angular.SingleOrListOrMap<T>): value is {[key: string]: T} {
|
||||
return value && !Array.isArray(value) && typeof value === 'object';
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
import {PlatformRef, Type} from '@angular/core';
|
||||
import * as angular from '@angular/upgrade/src/angular_js';
|
||||
import {$ROOT_SCOPE} from '@angular/upgrade/src/aot/constants';
|
||||
import {UpgradeModule} from '@angular/upgrade/static';
|
||||
|
||||
export function bootstrap(
|
||||
|
@ -20,6 +21,11 @@ export function bootstrap(
|
|||
});
|
||||
}
|
||||
|
||||
export function digest(adapter: UpgradeModule) {
|
||||
const $rootScope = adapter.$injector.get($ROOT_SCOPE) as angular.IRootScopeService;
|
||||
$rootScope.$digest();
|
||||
}
|
||||
|
||||
export function html(html: string): Element {
|
||||
// Don't return `body` itself, because using it as a `$rootElement` for ng1
|
||||
// will attach `$injector` to it and that will affect subsequent tests.
|
||||
|
|
Loading…
Reference in New Issue