refactor(compiler): remove view.rootNodes and view.projectableNodes

They are replaced by generated visitor functions `view.visitRootNodes` / `view.visitProjectableNodes`.
This commit is contained in:
Tobias Bosch
2016-10-31 14:41:16 -07:00
committed by vsavkin
parent b3e3cd3add
commit bda1909ede
13 changed files with 193 additions and 171 deletions

View File

@ -10,8 +10,10 @@ import {ChangeDetectorRef} from '../change_detection/change_detection';
import {Injector} from '../di/injector';
import {unimplemented} from '../facade/errors';
import {Type} from '../type';
import {AppElement} from './element';
import {ElementRef} from './element_ref';
import {AppView} from './view';
import {ViewRef} from './view_ref';
import {ViewUtils} from './view_utils';
@ -104,8 +106,15 @@ export class ComponentFactory<C> {
projectableNodes = [];
}
// Note: Host views don't need a declarationAppElement!
var hostView = this._viewFactory(vu, injector, null);
var hostElement = hostView.create(EMPTY_CONTEXT, projectableNodes, rootSelectorOrNode);
var hostView: AppView<any> = this._viewFactory(vu, injector, null);
hostView.visitProjectableNodesInternal =
(nodeIndex: number, ngContentIndex: number, cb: any, ctx: any) => {
const nodes = projectableNodes[ngContentIndex] || [];
for (var i = 0; i < nodes.length; i++) {
cb(nodes[i], ctx);
}
};
var hostElement = hostView.create(EMPTY_CONTEXT, rootSelectorOrNode);
return new ComponentRef_<C>(hostElement, this._componentType);
}
}

View File

@ -62,6 +62,14 @@ export class AppElement {
}
}
visitNestedViewRootNodes<C>(cb: (node: any, ctx: C) => void, c: C): void {
if (this.nestedViews) {
for (var i = 0; i < this.nestedViews.length; i++) {
this.nestedViews[i].visitRootNodesInternal(cb, c);
}
}
}
mapNestedViews(nestedViewClass: any, callback: Function): any[] {
var result: any[] /** TODO #9100 */ = [];
if (isPresent(this.nestedViews)) {

View File

@ -48,7 +48,7 @@ export class TemplateRef_<C> extends TemplateRef<C> {
createEmbeddedView(context: C): EmbeddedViewRef<C> {
var view: AppView<C> = this._viewFactory(
this._appElement.parentView.viewUtils, this._appElement.parentInjector, this._appElement);
view.create(context || <any>{}, null, null);
view.create(context || <any>{}, null);
return view.ref;
}

View File

@ -20,7 +20,7 @@ import {ElementInjector} from './element_injector';
import {ExpressionChangedAfterItHasBeenCheckedError, ViewDestroyedError, ViewWrappedError} from './errors';
import {ViewRef_} from './view_ref';
import {ViewType} from './view_type';
import {ViewUtils, ensureSlotCount, flattenNestedViewRenderNodes} from './view_utils';
import {ViewUtils, addToArray} from './view_utils';
var _scope_check: WtfScopeFn = wtfCreateScope(`AppView#check(ascii id)`);
@ -30,15 +30,13 @@ var _scope_check: WtfScopeFn = wtfCreateScope(`AppView#check(ascii id)`);
*/
export abstract class AppView<T> {
ref: ViewRef_<T>;
rootNodesOrAppElements: any[];
lastRootNode: any;
allNodes: any[];
disposables: Function[];
viewContainerElement: AppElement = null;
numberOfChecks: number = 0;
projectableNodes: Array<any|any[]>;
renderer: Renderer;
private _hasExternalHostElement: boolean;
@ -67,25 +65,9 @@ export abstract class AppView<T> {
get destroyed(): boolean { return this.cdMode === ChangeDetectorStatus.Destroyed; }
create(context: T, givenProjectableNodes: Array<any|any[]>, rootSelectorOrNode: string|any):
AppElement {
create(context: T, rootSelectorOrNode: string|any): AppElement {
this.context = context;
var projectableNodes: any[];
switch (this.type) {
case ViewType.COMPONENT:
projectableNodes = ensureSlotCount(givenProjectableNodes, this.componentType.slotCount);
break;
case ViewType.EMBEDDED:
projectableNodes = this.declarationAppElement.parentView.projectableNodes;
break;
case ViewType.HOST:
// Note: Don't ensure the slot count for the projectableNodes as we store
// them only for the contained component view (which will later check the slot count...)
projectableNodes = givenProjectableNodes;
break;
}
this._hasExternalHostElement = isPresent(rootSelectorOrNode);
this.projectableNodes = projectableNodes;
return this.createInternal(rootSelectorOrNode);
}
@ -95,8 +77,8 @@ export abstract class AppView<T> {
*/
createInternal(rootSelectorOrNode: string|any): AppElement { return null; }
init(rootNodesOrAppElements: any[], allNodes: any[], disposables: Function[]) {
this.rootNodesOrAppElements = rootNodesOrAppElements;
init(lastRootNode: any, allNodes: any[], disposables: Function[]) {
this.lastRootNode = lastRootNode;
this.allNodes = allNodes;
this.disposables = disposables;
if (this.type === ViewType.COMPONENT) {
@ -180,15 +162,41 @@ export abstract class AppView<T> {
return isPresent(this.declarationAppElement) ? this.declarationAppElement.parentView : null;
}
get flatRootNodes(): any[] { return flattenNestedViewRenderNodes(this.rootNodesOrAppElements); }
get lastRootNode(): any {
var lastNode = this.rootNodesOrAppElements.length > 0 ?
this.rootNodesOrAppElements[this.rootNodesOrAppElements.length - 1] :
null;
return _findLastRenderNode(lastNode);
get flatRootNodes(): any[] {
const nodes: any[] = [];
this.visitRootNodesInternal(addToArray, nodes);
return nodes;
}
projectedNodes(ngContentIndex: number): any[] {
const nodes: any[] = [];
this.visitProjectedNodes(ngContentIndex, addToArray, nodes);
return nodes;
}
visitProjectedNodes<C>(ngContentIndex: number, cb: (node: any, ctx: C) => void, c: C): void {
const appEl = this.declarationAppElement;
switch (this.type) {
case ViewType.EMBEDDED:
appEl.parentView.visitProjectedNodes(ngContentIndex, cb, c);
break;
case ViewType.COMPONENT:
appEl.parentView.visitProjectableNodesInternal(appEl.index, ngContentIndex, cb, c);
break;
}
}
/**
* Overwritten by implementations
*/
visitRootNodesInternal<C>(cb: (node: any, ctx: C) => void, c: C): void {}
/**
* Overwritten by implementations
*/
visitProjectableNodesInternal<C>(
nodeIndex: number, ngContentIndex: number, cb: (node: any, ctx: C) => void, c: C): void {}
/**
* Overwritten by implementations
*/
@ -258,11 +266,10 @@ export class DebugAppView<T> extends AppView<T> {
super(clazz, componentType, type, viewUtils, parentInjector, declarationAppElement, cdMode);
}
create(context: T, givenProjectableNodes: Array<any|any[]>, rootSelectorOrNode: string|any):
AppElement {
create(context: T, rootSelectorOrNode: string|any): AppElement {
this._resetDebug();
try {
return super.create(context, givenProjectableNodes, rootSelectorOrNode);
return super.create(context, rootSelectorOrNode);
} catch (e) {
this._rethrowWithContext(e);
throw e;
@ -339,24 +346,3 @@ export class DebugAppView<T> extends AppView<T> {
};
}
}
function _findLastRenderNode(node: any): any {
var lastNode: any;
if (node instanceof AppElement) {
var appEl = <AppElement>node;
lastNode = appEl.nativeElement;
if (isPresent(appEl.nestedViews)) {
// Note: Views might have no root nodes at all!
for (var i = appEl.nestedViews.length - 1; i >= 0; i--) {
var nestedView = appEl.nestedViews[i];
if (nestedView.rootNodesOrAppElements.length > 0) {
lastNode = _findLastRenderNode(
nestedView.rootNodesOrAppElements[nestedView.rootNodesOrAppElements.length - 1]);
}
}
}
} else {
lastNode = node;
}
return lastNode;
}

View File

@ -48,44 +48,8 @@ export class ViewUtils {
}
}
export function flattenNestedViewRenderNodes(nodes: any[]): any[] {
return _flattenNestedViewRenderNodes(nodes, []);
}
function _flattenNestedViewRenderNodes(nodes: any[], renderNodes: any[]): any[] {
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
if (node instanceof AppElement) {
var appEl = <AppElement>node;
renderNodes.push(appEl.nativeElement);
if (isPresent(appEl.nestedViews)) {
for (var k = 0; k < appEl.nestedViews.length; k++) {
_flattenNestedViewRenderNodes(appEl.nestedViews[k].rootNodesOrAppElements, renderNodes);
}
}
} else {
renderNodes.push(node);
}
}
return renderNodes;
}
const EMPTY_ARR: any[] = [];
export function ensureSlotCount(projectableNodes: any[][], expectedSlotCount: number): any[][] {
var res: any[][];
if (!projectableNodes) {
res = EMPTY_ARR;
} else if (projectableNodes.length < expectedSlotCount) {
var givenSlotCount = projectableNodes.length;
res = new Array(expectedSlotCount);
for (var i = 0; i < expectedSlotCount; i++) {
res[i] = (i < givenSlotCount) ? projectableNodes[i] : EMPTY_ARR;
}
} else {
res = projectableNodes;
}
return res;
export function addToArray(e: any, array: any[]) {
array.push(e);
}
export const MAX_INTERPOLATION_VALUES = 9;

View File

@ -7,9 +7,8 @@
*/
import {Component, Directive, ElementRef, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {getAllDebugNodes} from '@angular/core/src/debug/debug_node';
import {TestBed} from '@angular/core/testing';
import {beforeEach, describe, it} from '@angular/core/testing/testing_internal';
import {beforeEach, ddescribe, describe, iit, it} from '@angular/core/testing/testing_internal';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {expect} from '@angular/platform-browser/testing/matchers';
@ -236,6 +235,13 @@ export function main() {
});
it('should support moving non projected light dom around', () => {
let sourceDirective: ManualViewportDirective;
@Directive({selector: '[manual]'})
class ManualViewportDirective {
constructor(public templateRef: TemplateRef<Object>) { sourceDirective = this; }
}
TestBed.configureTestingModule(
{declarations: [Empty, ProjectDirective, ManualViewportDirective]});
TestBed.overrideComponent(MainComp, {
@ -248,17 +254,6 @@ export function main() {
});
const main = TestBed.createComponent(MainComp);
var sourceDirective: any;
// We can't use the child nodes to get a hold of this because it's not in the dom
// at
// all.
getAllDebugNodes().forEach((debug) => {
if (debug.providerTokens.indexOf(ManualViewportDirective) !== -1) {
sourceDirective = debug.injector.get(ManualViewportDirective);
}
});
var projectDirective: ProjectDirective =
main.debugElement.queryAllNodes(By.directive(ProjectDirective))[0].injector.get(
ProjectDirective);