2017-01-20 13:10:57 -08: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
|
|
|
|
*/
|
|
|
|
|
2017-05-05 13:49:59 -07:00
|
|
|
import {ElementData, NodeDef, NodeFlags, Services, ViewData, ViewDefinition, ViewState} from './types';
|
|
|
|
import {RenderNodeAction, declaredViewContainer, isComponentView, renderNode, visitRootRenderNodes} from './util';
|
2017-01-20 13:10:57 -08:00
|
|
|
|
2017-02-22 10:05:56 -08:00
|
|
|
export function attachEmbeddedView(
|
2017-03-29 09:34:45 -07:00
|
|
|
parentView: ViewData, elementData: ElementData, viewIndex: number | undefined | null,
|
|
|
|
view: ViewData) {
|
|
|
|
let embeddedViews = elementData.viewContainer !._embeddedViews;
|
|
|
|
if (viewIndex === null || viewIndex === undefined) {
|
2017-01-20 13:10:57 -08:00
|
|
|
viewIndex = embeddedViews.length;
|
|
|
|
}
|
2017-02-22 10:05:56 -08:00
|
|
|
view.viewContainerParent = parentView;
|
2017-03-29 09:34:45 -07:00
|
|
|
addToArray(embeddedViews, viewIndex !, view);
|
2017-01-25 13:45:07 -08:00
|
|
|
const dvcElementData = declaredViewContainer(view);
|
|
|
|
if (dvcElementData && dvcElementData !== elementData) {
|
2017-03-13 10:44:12 -07:00
|
|
|
let projectedViews = dvcElementData.template._projectedViews;
|
2017-01-23 16:59:20 -08:00
|
|
|
if (!projectedViews) {
|
2017-03-13 10:44:12 -07:00
|
|
|
projectedViews = dvcElementData.template._projectedViews = [];
|
2017-01-23 16:59:20 -08:00
|
|
|
}
|
|
|
|
projectedViews.push(view);
|
2017-05-05 13:49:59 -07:00
|
|
|
if (view.parent) {
|
|
|
|
// Note: we are changing the NodeDef here as we cannot calculate
|
|
|
|
// the fact whether a template is used for projection during compilation.
|
|
|
|
markNodeAsProjectedTemplate(view.parent.def, view.parentNodeDef !);
|
|
|
|
}
|
2017-01-20 13:10:57 -08:00
|
|
|
}
|
2017-01-23 16:59:20 -08:00
|
|
|
|
2017-02-22 10:05:56 -08:00
|
|
|
Services.dirtyParentQueries(view);
|
2017-01-23 16:59:20 -08:00
|
|
|
|
2017-03-29 09:34:45 -07:00
|
|
|
const prevView = viewIndex ! > 0 ? embeddedViews[viewIndex ! - 1] : null;
|
2017-02-01 11:32:27 -08:00
|
|
|
renderAttachEmbeddedView(elementData, prevView, view);
|
2017-01-20 13:10:57 -08:00
|
|
|
}
|
|
|
|
|
2017-05-05 13:49:59 -07:00
|
|
|
function markNodeAsProjectedTemplate(viewDef: ViewDefinition, nodeDef: NodeDef) {
|
|
|
|
if (nodeDef.flags & NodeFlags.ProjectedTemplate) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
viewDef.nodeFlags |= NodeFlags.ProjectedTemplate;
|
|
|
|
nodeDef.flags |= NodeFlags.ProjectedTemplate;
|
|
|
|
let parentNodeDef = nodeDef.parent;
|
|
|
|
while (parentNodeDef) {
|
|
|
|
parentNodeDef.childFlags |= NodeFlags.ProjectedTemplate;
|
|
|
|
parentNodeDef = parentNodeDef.parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-29 09:34:45 -07:00
|
|
|
export function detachEmbeddedView(elementData: ElementData, viewIndex?: number): ViewData|null {
|
|
|
|
const embeddedViews = elementData.viewContainer !._embeddedViews;
|
2017-02-20 14:34:15 -08:00
|
|
|
if (viewIndex == null || viewIndex >= embeddedViews.length) {
|
|
|
|
viewIndex = embeddedViews.length - 1;
|
|
|
|
}
|
|
|
|
if (viewIndex < 0) {
|
|
|
|
return null;
|
2017-01-20 13:10:57 -08:00
|
|
|
}
|
|
|
|
const view = embeddedViews[viewIndex];
|
2017-03-29 09:34:45 -07:00
|
|
|
view.viewContainerParent = null;
|
2017-01-23 16:59:20 -08:00
|
|
|
removeFromArray(embeddedViews, viewIndex);
|
|
|
|
|
2017-01-25 13:45:07 -08:00
|
|
|
const dvcElementData = declaredViewContainer(view);
|
|
|
|
if (dvcElementData && dvcElementData !== elementData) {
|
2017-03-13 10:44:12 -07:00
|
|
|
const projectedViews = dvcElementData.template._projectedViews;
|
2017-01-23 16:59:20 -08:00
|
|
|
removeFromArray(projectedViews, projectedViews.indexOf(view));
|
|
|
|
}
|
|
|
|
|
2017-02-22 10:05:56 -08:00
|
|
|
Services.dirtyParentQueries(view);
|
2017-01-23 16:59:20 -08:00
|
|
|
|
2017-02-22 10:05:56 -08:00
|
|
|
renderDetachView(view);
|
2017-02-01 11:32:27 -08:00
|
|
|
|
|
|
|
return view;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function moveEmbeddedView(
|
|
|
|
elementData: ElementData, oldViewIndex: number, newViewIndex: number): ViewData {
|
2017-03-29 09:34:45 -07:00
|
|
|
const embeddedViews = elementData.viewContainer !._embeddedViews;
|
2017-02-01 11:32:27 -08:00
|
|
|
const view = embeddedViews[oldViewIndex];
|
|
|
|
removeFromArray(embeddedViews, oldViewIndex);
|
|
|
|
if (newViewIndex == null) {
|
|
|
|
newViewIndex = embeddedViews.length;
|
|
|
|
}
|
|
|
|
addToArray(embeddedViews, newViewIndex, view);
|
|
|
|
|
|
|
|
// Note: Don't need to change projectedViews as the order in there
|
|
|
|
// as always invalid...
|
|
|
|
|
2017-02-22 10:05:56 -08:00
|
|
|
Services.dirtyParentQueries(view);
|
2017-02-01 11:32:27 -08:00
|
|
|
|
2017-02-22 10:05:56 -08:00
|
|
|
renderDetachView(view);
|
2017-02-01 11:32:27 -08:00
|
|
|
const prevView = newViewIndex > 0 ? embeddedViews[newViewIndex - 1] : null;
|
|
|
|
renderAttachEmbeddedView(elementData, prevView, view);
|
|
|
|
|
|
|
|
return view;
|
|
|
|
}
|
|
|
|
|
2017-03-29 09:34:45 -07:00
|
|
|
function renderAttachEmbeddedView(
|
|
|
|
elementData: ElementData, prevView: ViewData | null, view: ViewData) {
|
|
|
|
const prevRenderNode = prevView ? renderNode(prevView, prevView.def.lastRenderRootNode !) :
|
|
|
|
elementData.renderElement;
|
2017-02-16 13:55:55 -08:00
|
|
|
const parentNode = view.renderer.parentNode(prevRenderNode);
|
|
|
|
const nextSibling = view.renderer.nextSibling(prevRenderNode);
|
2017-02-03 15:20:50 -08:00
|
|
|
// Note: We can't check if `nextSibling` is present, as on WebWorkers it will always be!
|
|
|
|
// However, browsers automatically do `appendChild` when there is no `nextSibling`.
|
|
|
|
visitRootRenderNodes(view, RenderNodeAction.InsertBefore, parentNode, nextSibling, undefined);
|
2017-02-01 11:32:27 -08:00
|
|
|
}
|
|
|
|
|
2017-02-22 10:05:56 -08:00
|
|
|
export function renderDetachView(view: ViewData) {
|
|
|
|
visitRootRenderNodes(view, RenderNodeAction.RemoveChild, null, null, undefined);
|
2017-01-20 13:10:57 -08:00
|
|
|
}
|
|
|
|
|
2017-01-23 16:59:20 -08:00
|
|
|
function addToArray(arr: any[], index: number, value: any) {
|
|
|
|
// perf: array.push is faster than array.splice!
|
|
|
|
if (index >= arr.length) {
|
|
|
|
arr.push(value);
|
|
|
|
} else {
|
|
|
|
arr.splice(index, 0, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function removeFromArray(arr: any[], index: number) {
|
|
|
|
// perf: array.pop is faster than array.splice!
|
|
|
|
if (index >= arr.length - 1) {
|
|
|
|
arr.pop();
|
|
|
|
} else {
|
|
|
|
arr.splice(index, 1);
|
|
|
|
}
|
|
|
|
}
|