feat(upgrade): use ComponentFactory.inputs/outputs/ngContentSelectors (#15214)

DEPRECATION:
- the arguments `inputs` / `outputs` / `ngContentSelectors` of `downgradeComponent`
  are no longer used as Angular calculates these automatically now.
- Compiler.getNgContentSelectors is deprecated. Use
  ComponentFactory.ngContentSelectors instead.
This commit is contained in:
Tobias Bosch
2017-03-14 14:55:37 -07:00
committed by Miško Hevery
parent 791534f2f4
commit 9429032da1
22 changed files with 94 additions and 418 deletions

View File

@ -9,9 +9,8 @@
import {ChangeDetectorRef, ComponentFactory, ComponentRef, EventEmitter, Injector, OnChanges, ReflectiveInjector, SimpleChange, SimpleChanges, Type} from '@angular/core';
import * as angular from './angular1';
import {ComponentInfo, PropertyBinding} from './component_info';
import {PropertyBinding} from './component_info';
import {$SCOPE} from './constants';
import {NgContentSelectorHelper} from './ng_content_selector_helper';
import {getAttributesAsArray, getComponentName, hookupNgModel} from './util';
const INITIAL_VALUE = {
@ -27,7 +26,7 @@ export class DowngradeComponentAdapter {
private changeDetector: ChangeDetectorRef = null;
constructor(
private id: string, private info: ComponentInfo, private element: angular.IAugmentedJQuery,
private id: string, private element: angular.IAugmentedJQuery,
private attrs: angular.IAttributes, private scope: angular.IScope,
private ngModel: angular.INgModelController, private parentInjector: Injector,
private $injector: angular.IInjectorService, private $compile: angular.ICompileService,
@ -67,9 +66,9 @@ export class DowngradeComponentAdapter {
setupInputs(): void {
const attrs = this.attrs;
const inputs = this.info.inputs || [];
const inputs = this.componentFactory.inputs || [];
for (let i = 0; i < inputs.length; i++) {
const input = new PropertyBinding(inputs[i]);
const input = new PropertyBinding(inputs[i].propName, inputs[i].templateName);
let expr: any /** TODO #9100 */ = null;
if (attrs.hasOwnProperty(input.attr)) {
@ -103,7 +102,7 @@ export class DowngradeComponentAdapter {
}
}
const prototype = this.info.component.prototype;
const prototype = this.componentFactory.componentType.prototype;
if (prototype && (<OnChanges>prototype).ngOnChanges) {
// Detect: OnChanges interface
this.inputChanges = {};
@ -118,9 +117,9 @@ export class DowngradeComponentAdapter {
setupOutputs() {
const attrs = this.attrs;
const outputs = this.info.outputs || [];
const outputs = this.componentFactory.outputs || [];
for (let j = 0; j < outputs.length; j++) {
const output = new PropertyBinding(outputs[j]);
const output = new PropertyBinding(outputs[j].propName, outputs[j].templateName);
let expr: any /** TODO #9100 */ = null;
let assignExpr = false;
@ -158,7 +157,7 @@ export class DowngradeComponentAdapter {
});
} else {
throw new Error(
`Missing emitter '${output.prop}' on component '${getComponentName(this.info.component)}'!`);
`Missing emitter '${output.prop}' on component '${getComponentName(this.componentFactory.componentType)}'!`);
}
}
}
@ -183,49 +182,31 @@ export class DowngradeComponentAdapter {
}
groupProjectableNodes() {
const ngContentSelectorHelper =
this.parentInjector.get(NgContentSelectorHelper) as NgContentSelectorHelper;
const ngContentSelectors = ngContentSelectorHelper.getNgContentSelectors(this.info);
if (!ngContentSelectors) {
throw new Error('Expecting ngContentSelectors for: ' + getComponentName(this.info.component));
}
return this._groupNodesBySelector(ngContentSelectors, this.element.contents());
}
/**
* Group a set of DOM nodes into `ngContent` groups, based on the given content selectors.
*/
private _groupNodesBySelector(ngContentSelectors: string[], nodes: Node[]): Node[][] {
const projectableNodes: Node[][] = [];
let wildcardNgContentIndex: number;
for (let i = 0, ii = ngContentSelectors.length; i < ii; ++i) {
projectableNodes[i] = [];
}
for (let j = 0, jj = nodes.length; j < jj; ++j) {
const node = nodes[j];
const ngContentIndex = findMatchingNgContentIndex(node, ngContentSelectors);
if (ngContentIndex != null) {
projectableNodes[ngContentIndex].push(node);
}
}
return projectableNodes;
let ngContentSelectors = this.componentFactory.ngContentSelectors;
return groupNodesBySelector(ngContentSelectors, this.element.contents());
}
}
let _matches: (this: any, selector: string) => boolean;
/**
* Group a set of DOM nodes into `ngContent` groups, based on the given content selectors.
*/
export function groupNodesBySelector(ngContentSelectors: string[], nodes: Node[]): Node[][] {
const projectableNodes: Node[][] = [];
let wildcardNgContentIndex: number;
function matchesSelector(el: any, selector: string): boolean {
if (!_matches) {
const elProto = <any>Element.prototype;
_matches = elProto.matchesSelector || elProto.mozMatchesSelector || elProto.msMatchesSelector ||
elProto.oMatchesSelector || elProto.webkitMatchesSelector;
for (let i = 0, ii = ngContentSelectors.length; i < ii; ++i) {
projectableNodes[i] = [];
}
return _matches.call(el, selector);
for (let j = 0, jj = nodes.length; j < jj; ++j) {
const node = nodes[j];
const ngContentIndex = findMatchingNgContentIndex(node, ngContentSelectors);
if (ngContentIndex != null) {
projectableNodes[ngContentIndex].push(node);
}
}
return projectableNodes;
}
function findMatchingNgContentIndex(element: any, ngContentSelectors: string[]): number {
@ -247,4 +228,15 @@ function findMatchingNgContentIndex(element: any, ngContentSelectors: string[]):
ngContentIndices.push(wildcardNgContentIndex);
}
return ngContentIndices.length ? ngContentIndices[0] : null;
}
}
let _matches: (this: any, selector: string) => boolean;
function matchesSelector(el: any, selector: string): boolean {
if (!_matches) {
const elProto = <any>Element.prototype;
_matches = elProto.matches || elProto.matchesSelector || elProto.mozMatchesSelector ||
elProto.msMatchesSelector || elProto.oMatchesSelector || elProto.webkitMatchesSelector;
}
return el.nodeType === Node.ELEMENT_NODE ? _matches.call(el, selector) : false;
}