refactor(ivy): move instructions (#29646)

- moves all publicly exported instructions to their own files
- refactors namespace instructions to set state in `state.ts`
- no longer exports * from `instructions.ts`.
- `instructions.ts` renamed to `shared.ts` (old `shared.ts` contents folded in to `instructions.ts`)
- updates `all.ts` to re-export from public instruction files.

PR Close #29646
This commit is contained in:
Ben Lesh 2019-04-01 15:36:43 -07:00 committed by Jason Aden
parent 03d914a6c2
commit 5a724b34bd
40 changed files with 3500 additions and 3236 deletions

View File

@ -11,24 +11,23 @@
import {Type} from '../core';
import {Injector} from '../di/injector';
import {Sanitizer} from '../sanitization/security';
import {assertDefined} from '../util/assert';
import {assertComponentType} from './assert';
import {getComponentDef} from './definition';
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
import {registerPostOrderHooks, registerPreOrderHooks} from './hooks';
import {CLEAN_PROMISE, addToViewTree, createLView, createNodeAtIndex, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews} from './instructions/all';
import {CLEAN_PROMISE, addToViewTree, createLView, createNodeAtIndex, createTView, getOrCreateTView, initNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews} from './instructions/shared';
import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition';
import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
import {PlayerHandler} from './interfaces/player';
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
import {CONTEXT, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, RENDERER, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
import {applyOnCreateInstructions} from './node_util';
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setCurrentDirectiveDef} from './state';
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState} from './state';
import {renderInitialClasses, renderInitialStyles} from './styling/class_and_style_bindings';
import {publishDefaultGlobalUtils} from './util/global_utils';
import {defaultScheduler, renderStringify} from './util/misc_utils';
import {getRootContext, getRootView} from './util/view_traversal_utils';
import {getRootContext} from './util/view_traversal_utils';
import {readPatchedLView, resetPreOrderHookFlags} from './util/view_utils';

View File

@ -26,11 +26,11 @@ import {assertComponentType} from './assert';
import {LifecycleHooksFeature, createRootComponent, createRootComponentView, createRootContext} from './component';
import {getComponentDef} from './definition';
import {NodeInjector} from './di';
import {addToViewTree, assignTViewNodeToLView, createLView, createTView, elementCreate, locateHostElement, refreshDescendantViews} from './instructions/all';
import {addToViewTree, assignTViewNodeToLView, createLView, createTView, elementCreate, locateHostElement, refreshDescendantViews} from './instructions/shared';
import {ComponentDef} from './interfaces/definition';
import {TContainerNode, TElementContainerNode, TElementNode} from './interfaces/node';
import {RNode, RendererFactory3, domRendererFactory3, isProceduralRenderer} from './interfaces/renderer';
import {HEADER_OFFSET, LView, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {LView, LViewFlags, RootContext, TVIEW} from './interfaces/view';
import {enterView, leaveView} from './state';
import {defaultScheduler} from './util/misc_utils';
import {getTNode} from './util/view_utils';

View File

@ -11,11 +11,11 @@ import {resolveForwardRef} from '../di/forward_ref';
import {ClassProvider, Provider} from '../di/interface/provider';
import {isClassProvider, isTypeProvider, providerToFactory} from '../di/r3_injector';
import {DirectiveDef} from '.';
import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from './di';
import {directiveInject} from './instructions/all';
import {DirectiveDef} from './interfaces/definition';
import {NodeInjectorFactory} from './interfaces/injector';
import {TContainerNode, TElementContainerNode, TElementNode, TNodeFlags, TNodeProviderIndexes} from './interfaces/node';
import {TContainerNode, TElementContainerNode, TElementNode, TNodeProviderIndexes} from './interfaces/node';
import {LView, TData, TVIEW, TView} from './interfaces/view';
import {getLView, getPreviousOrParentTNode} from './state';
import {isComponentDef} from './util/view_utils';

View File

@ -13,7 +13,8 @@ import {addAllToArray} from '../util/array_utils';
import {assertDefined, assertEqual, assertGreaterThan} from '../util/assert';
import {attachPatchData} from './context_discovery';
import {allocExpando, createNodeAtIndex, elementAttribute, load, textBinding} from './instructions/all';
import {elementAttribute, load, textBinding} from './instructions/all';
import {allocExpando, createNodeAtIndex} from './instructions/shared';
import {LContainer, NATIVE} from './interfaces/container';
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18nUpdateOpCode, I18nUpdateOpCodes, IcuType, TI18n, TIcu} from './interfaces/i18n';
import {TElementNode, TIcuContainerNode, TNode, TNodeType} from './interfaces/node';

View File

@ -19,7 +19,6 @@ export {RenderFlags} from './interfaces/definition';
export {CssSelectorList} from './interfaces/projection';
// clang-format off
export {
allocHostVars,
@ -64,6 +63,7 @@ export {
elementHostStylingApply,
select,
property,
listener,
store,

View File

@ -25,5 +25,21 @@
*
* Jira Issue = FW-1184
*/
export * from './instructions';
export * from './alloc_host_vars';
export * from './change_detection';
export * from './container';
export * from './storage';
export * from './di';
export * from './element';
export * from './element_container';
export * from './embedded_view';
export * from './get_current_view';
export * from './listener';
export * from './namespace';
export * from './next_context';
export * from './projection';
export * from './property';
export * from './property_interpolation';
export * from './select';
export * from './styling_instructions';
export * from './text';

View File

@ -0,0 +1,63 @@
/**
* @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
*/
import {assertEqual} from '../../util/assert';
import {ComponentDef, DirectiveDef} from '../interfaces/definition';
import {LView, TVIEW, TView} from '../interfaces/view';
import {getCurrentDirectiveDef, getLView} from '../state';
import {NO_CHANGE} from '../tokens';
/**
* Allocates the necessary amount of slots for host vars.
*
* @param count Amount of vars to be allocated
*/
export function allocHostVars(count: number): void {
const lView = getLView();
const tView = lView[TVIEW];
if (!tView.firstTemplatePass) return;
queueHostBindingForCheck(tView, getCurrentDirectiveDef() !, count);
prefillHostVars(tView, lView, count);
}
/**
* Stores host binding fn and number of host vars so it will be queued for binding refresh during
* CD.
*/
function queueHostBindingForCheck(
tView: TView, def: DirectiveDef<any>| ComponentDef<any>, hostVars: number): void {
ngDevMode &&
assertEqual(tView.firstTemplatePass, true, 'Should only be called in first template pass.');
const expando = tView.expandoInstructions !;
const length = expando.length;
// Check whether a given `hostBindings` function already exists in expandoInstructions,
// which can happen in case directive definition was extended from base definition (as a part of
// the `InheritDefinitionFeature` logic). If we found the same `hostBindings` function in the
// list, we just increase the number of host vars associated with that function, but do not add it
// into the list again.
if (length >= 2 && expando[length - 2] === def.hostBindings) {
expando[length - 1] = (expando[length - 1] as number) + hostVars;
} else {
expando.push(def.hostBindings !, hostVars);
}
}
/**
* On the first template pass, we need to reserve space for host binding values
* after directives are matched (so all directives are saved, then bindings).
* Because we are updating the blueprint, we only need to do this once.
*/
function prefillHostVars(tView: TView, lView: LView, totalHostVars: number): void {
ngDevMode &&
assertEqual(tView.firstTemplatePass, true, 'Should only be called in first template pass.');
for (let i = 0; i < totalHostVars; i++) {
lView.push(NO_CHANGE);
tView.blueprint.push(NO_CHANGE);
tView.data.push(null);
}
}

View File

@ -0,0 +1,72 @@
/**
* @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
*/
import {assertDefined} from '../../util/assert';
import {getComponentViewByInstance} from '../context_discovery';
import {CONTEXT, RootContext, RootContextFlags} from '../interfaces/view';
import {getRootView} from '../util/view_traversal_utils';
import {detectChangesInternal, markViewDirty, scheduleTick, tickRootContext} from './shared';
/**
* Synchronously perform change detection on a component (and possibly its sub-components).
*
* This function triggers change detection in a synchronous way on a component. There should
* be very little reason to call this function directly since a preferred way to do change
* detection is to {@link markDirty} the component and wait for the scheduler to call this method
* at some future point in time. This is because a single user action often results in many
* components being invalidated and calling change detection on each component synchronously
* would be inefficient. It is better to wait until all components are marked as dirty and
* then perform single change detection across all of the components
*
* @param component The component which the change detection should be performed on.
*/
export function detectChanges<T>(component: T): void {
const view = getComponentViewByInstance(component);
detectChangesInternal<T>(view, component);
}
/**
* Mark the component as dirty (needing change detection).
*
* Marking a component dirty will schedule a change detection on this
* component at some point in the future. Marking an already dirty
* component as dirty is a noop. Only one outstanding change detection
* can be scheduled per component tree. (Two components bootstrapped with
* separate `renderComponent` will have separate schedulers)
*
* When the root component is bootstrapped with `renderComponent`, a scheduler
* can be provided.
*
* @param component Component to mark as dirty.
*
* @publicApi
*/
export function markDirty<T>(component: T) {
ngDevMode && assertDefined(component, 'component');
const rootView = markViewDirty(getComponentViewByInstance(component)) !;
ngDevMode && assertDefined(rootView[CONTEXT], 'rootContext should be defined');
scheduleTick(rootView[CONTEXT] as RootContext, RootContextFlags.DetectChanges);
}
/**
* Used to perform change detection on the whole application.
*
* This is equivalent to `detectChanges`, but invoked on root component. Additionally, `tick`
* executes lifecycle hooks and conditionally checks components based on their
* `ChangeDetectionStrategy` and dirtiness.
*
* The preferred way to trigger change detection is to call `markDirty`. `markDirty` internally
* schedules `tick` using a scheduler in order to coalesce multiple `markDirty` calls into a
* single change detection run. By default, the scheduler is `requestAnimationFrame`, but can
* be changed when calling `renderComponent` and providing the `scheduler` option.
*/
export function tick<T>(component: T): void {
const rootView = getRootView(component);
const rootContext = rootView[CONTEXT] as RootContext;
tickRootContext(rootContext);
}

View File

@ -0,0 +1,167 @@
/**
* @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
*/
import {assertEqual} from '../../util/assert';
import {assertHasParent} from '../assert';
import {attachPatchData} from '../context_discovery';
import {executePreOrderHooks, registerPostOrderHooks} from '../hooks';
import {ACTIVE_INDEX, VIEWS} from '../interfaces/container';
import {ComponentTemplate} from '../interfaces/definition';
import {LocalRefExtractor, TAttributes, TContainerNode, TNode, TNodeType} from '../interfaces/node';
import {BINDING_INDEX, HEADER_OFFSET, LView, QUERIES, RENDERER, TVIEW} from '../interfaces/view';
import {assertNodeType} from '../node_assert';
import {appendChild, removeView} from '../node_manipulation';
import {getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, setIsParent, setPreviousOrParentTNode} from '../state';
import {getNativeByTNode, loadInternal} from '../util/view_utils';
import {addToViewTree, createDirectivesAndLocals, createLContainer, createNodeAtIndex, createTView} from './shared';
/**
* Creates an LContainer for inline views, e.g.
*
* % if (showing) {
* <div></div>
* % }
*
* @param index The index of the container in the data array
*/
export function container(index: number): void {
const tNode = containerInternal(index, null, null);
const lView = getLView();
if (lView[TVIEW].firstTemplatePass) {
tNode.tViews = [];
}
addTContainerToQueries(lView, tNode);
setIsParent(false);
}
/**
* Creates an LContainer for an ng-template (dynamically-inserted view), e.g.
*
* <ng-template #foo>
* <div></div>
* </ng-template>
*
* @param index The index of the container in the data array
* @param templateFn Inline template
* @param consts The number of nodes, local refs, and pipes for this template
* @param vars The number of bindings for this template
* @param tagName The name of the container element, if applicable
* @param attrs The attrs attached to the container, if applicable
* @param localRefs A set of local reference bindings on the element.
* @param localRefExtractor A function which extracts local-refs values from the template.
* Defaults to the current element associated with the local-ref.
*/
export function template(
index: number, templateFn: ComponentTemplate<any>| null, consts: number, vars: number,
tagName?: string | null, attrs?: TAttributes | null, localRefs?: string[] | null,
localRefExtractor?: LocalRefExtractor) {
const lView = getLView();
const tView = lView[TVIEW];
// TODO: consider a separate node type for templates
const tContainerNode = containerInternal(index, tagName || null, attrs || null);
if (tView.firstTemplatePass) {
tContainerNode.tViews = createTView(
-1, templateFn, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null, null);
}
createDirectivesAndLocals(tView, lView, localRefs, localRefExtractor);
addTContainerToQueries(lView, tContainerNode);
attachPatchData(getNativeByTNode(tContainerNode, lView), lView);
registerPostOrderHooks(tView, tContainerNode);
setIsParent(false);
}
/**
* Sets a container up to receive views.
*
* @param index The index of the container in the data array
*/
export function containerRefreshStart(index: number): void {
const lView = getLView();
const tView = lView[TVIEW];
let previousOrParentTNode = loadInternal(tView.data, index) as TNode;
setPreviousOrParentTNode(previousOrParentTNode);
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
setIsParent(true);
lView[index + HEADER_OFFSET][ACTIVE_INDEX] = 0;
// We need to execute init hooks here so ngOnInit hooks are called in top level views
// before they are called in embedded views (for backwards compatibility).
executePreOrderHooks(lView, tView, getCheckNoChangesMode(), undefined);
}
/**
* Marks the end of the LContainer.
*
* Marking the end of LContainer is the time when to child views get inserted or removed.
*/
export function containerRefreshEnd(): void {
let previousOrParentTNode = getPreviousOrParentTNode();
if (getIsParent()) {
setIsParent(false);
} else {
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.View);
ngDevMode && assertHasParent(previousOrParentTNode);
previousOrParentTNode = previousOrParentTNode.parent !;
setPreviousOrParentTNode(previousOrParentTNode);
}
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Container);
const lContainer = getLView()[previousOrParentTNode.index];
const nextIndex = lContainer[ACTIVE_INDEX];
// remove extra views at the end of the container
while (nextIndex < lContainer[VIEWS].length) {
removeView(lContainer, nextIndex);
}
}
/**
* Reporting a TContainer node queries is a 2-step process as we need to:
* - check if the container node itself is matching (query might match a <ng-template> node);
* - prepare room for nodes from views that might be created based on the TemplateRef linked to this
* container.
*
* Those 2 operations need to happen in the specific order (match the container node itself, then
* prepare space for nodes from views).
*/
function addTContainerToQueries(lView: LView, tContainerNode: TContainerNode): void {
const queries = lView[QUERIES];
if (queries) {
queries.addNode(tContainerNode);
const lContainer = lView[tContainerNode.index];
lContainer[QUERIES] = queries.container();
}
}
function containerInternal(
index: number, tagName: string | null, attrs: TAttributes | null): TContainerNode {
const lView = getLView();
ngDevMode && assertEqual(
lView[BINDING_INDEX], lView[TVIEW].bindingStartIndex,
'container nodes should be created before any bindings');
const adjustedIndex = index + HEADER_OFFSET;
const comment = lView[RENDERER].createComment(ngDevMode ? 'container' : '');
ngDevMode && ngDevMode.rendererCreateComment++;
const tNode = createNodeAtIndex(index, TNodeType.Container, comment, tagName, attrs);
const lContainer = lView[adjustedIndex] =
createLContainer(lView[adjustedIndex], lView, comment, tNode);
appendChild(comment, tNode, lView);
// Containers are added to the current view tree instead of their embedded views
// because views can be removed and re-inserted.
addToViewTree(lView, lContainer);
ngDevMode && assertNodeType(getPreviousOrParentTNode(), TNodeType.Container);
return tNode;
}

View File

@ -0,0 +1,50 @@
/**
* @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
*/
import {InjectFlags, InjectionToken, resolveForwardRef} from '../../di';
import {Type} from '../../interface/type';
import {getOrCreateInjectable, injectAttributeImpl} from '../di';
import {TContainerNode, TElementContainerNode, TElementNode} from '../interfaces/node';
import {getLView, getPreviousOrParentTNode} from '../state';
/**
* Returns the value associated to the given token from the injectors.
*
* `directiveInject` is intended to be used for directive, component and pipe factories.
* All other injection use `inject` which does not walk the node injector tree.
*
* Usage example (in factory function):
*
* class SomeDirective {
* constructor(directive: DirectiveA) {}
*
* static ngDirectiveDef = defineDirective({
* type: SomeDirective,
* factory: () => new SomeDirective(directiveInject(DirectiveA))
* });
* }
*
* @param token the type or token to inject
* @param flags Injection flags
* @returns the value from the injector or `null` when not found
*/
export function directiveInject<T>(token: Type<T>| InjectionToken<T>): T;
export function directiveInject<T>(token: Type<T>| InjectionToken<T>, flags: InjectFlags): T;
export function directiveInject<T>(
token: Type<T>| InjectionToken<T>, flags = InjectFlags.Default): T|null {
token = resolveForwardRef(token);
return getOrCreateInjectable<T>(
getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode,
getLView(), token, flags);
}
/**
* Facade for the attribute injection from DI.
*/
export function injectAttribute(attrNameToInject: string): string|null {
return injectAttributeImpl(getPreviousOrParentTNode(), attrNameToInject);
}

View File

@ -0,0 +1,272 @@
/**
* @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
*/
import {validateAgainstEventAttributes} from '../../sanitization/sanitization';
import {assertDataInRange, assertEqual} from '../../util/assert';
import {assertHasParent} from '../assert';
import {attachPatchData} from '../context_discovery';
import {registerPostOrderHooks} from '../hooks';
import {TAttributes, TNodeFlags, TNodeType} from '../interfaces/node';
import {RElement, Renderer3, isProceduralRenderer} from '../interfaces/renderer';
import {SanitizerFn} from '../interfaces/sanitization';
import {BINDING_INDEX, QUERIES, RENDERER, TVIEW} from '../interfaces/view';
import {assertNodeType} from '../node_assert';
import {appendChild} from '../node_manipulation';
import {applyOnCreateInstructions} from '../node_util';
import {decreaseElementDepthCount, getActiveHostContext, getElementDepthCount, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, increaseElementDepthCount, setIsParent, setPreviousOrParentTNode} from '../state';
import {getInitialClassNameValue, getInitialStyleStringValue, initializeStaticContext, patchContextWithStaticAttrs, renderInitialClasses, renderInitialStyles} from '../styling/class_and_style_bindings';
import {getStylingContext, hasClassInput, hasStyleInput} from '../styling/util';
import {NO_CHANGE} from '../tokens';
import {attrsStylingIndexOf, setUpAttributes} from '../util/attrs_utils';
import {renderStringify} from '../util/misc_utils';
import {getNativeByIndex, getNativeByTNode, getTNode} from '../util/view_utils';
import {createDirectivesAndLocals, createNodeAtIndex, elementCreate, executeContentQueries, initializeTNodeInputs, setInputsForProperty, setNodeStylingTemplate} from './shared';
/**
* Create DOM element. The instruction must later be followed by `elementEnd()` call.
*
* @param index Index of the element in the LView array
* @param name Name of the DOM Node
* @param attrs Statically bound set of attributes, classes, and styles to be written into the DOM
* element on creation. Use [AttributeMarker] to denote the meaning of this array.
* @param localRefs A set of local reference bindings on the element.
*
* Attributes and localRefs are passed as an array of strings where elements with an even index
* hold an attribute name and elements with an odd index hold an attribute value, ex.:
* ['id', 'warning5', 'class', 'alert']
*/
export function elementStart(
index: number, name: string, attrs?: TAttributes | null, localRefs?: string[] | null): void {
const lView = getLView();
const tView = lView[TVIEW];
ngDevMode && assertEqual(
lView[BINDING_INDEX], tView.bindingStartIndex,
'elements should be created before any bindings ');
ngDevMode && ngDevMode.rendererCreateElement++;
const native = elementCreate(name);
const renderer = lView[RENDERER];
ngDevMode && assertDataInRange(lView, index - 1);
const tNode = createNodeAtIndex(index, TNodeType.Element, native !, name, attrs || null);
let initialStylesIndex = 0;
let initialClassesIndex = 0;
if (attrs) {
const lastAttrIndex = setUpAttributes(native, attrs);
// it's important to only prepare styling-related datastructures once for a given
// tNode and not each time an element is created. Also, the styling code is designed
// to be patched and constructed at various points, but only up until the styling
// template is first allocated (which happens when the very first style/class binding
// value is evaluated). When the template is allocated (when it turns into a context)
// then the styling template is locked and cannot be further extended (it can only be
// instantiated into a context per element)
setNodeStylingTemplate(tView, tNode, attrs, lastAttrIndex);
if (tNode.stylingTemplate) {
// the initial style/class values are rendered immediately after having been
// initialized into the context so the element styling is ready when directives
// are initialized (since they may read style/class values in their constructor)
initialStylesIndex = renderInitialStyles(native, tNode.stylingTemplate, renderer);
initialClassesIndex = renderInitialClasses(native, tNode.stylingTemplate, renderer);
}
}
appendChild(native, tNode, lView);
createDirectivesAndLocals(tView, lView, localRefs);
// any immediate children of a component or template container must be pre-emptively
// monkey-patched with the component view data so that the element can be inspected
// later on using any element discovery utility methods (see `element_discovery.ts`)
if (getElementDepthCount() === 0) {
attachPatchData(native, lView);
}
increaseElementDepthCount();
// if a directive contains a host binding for "class" then all class-based data will
// flow through that (except for `[class.prop]` bindings). This also includes initial
// static class values as well. (Note that this will be fixed once map-based `[style]`
// and `[class]` bindings work for multiple directives.)
if (tView.firstTemplatePass) {
const inputData = initializeTNodeInputs(tNode);
if (inputData && inputData.hasOwnProperty('class')) {
tNode.flags |= TNodeFlags.hasClassInput;
}
if (inputData && inputData.hasOwnProperty('style')) {
tNode.flags |= TNodeFlags.hasStyleInput;
}
}
// we render the styling again below in case any directives have set any `style` and/or
// `class` host attribute values...
if (tNode.stylingTemplate) {
renderInitialClasses(native, tNode.stylingTemplate, renderer, initialClassesIndex);
renderInitialStyles(native, tNode.stylingTemplate, renderer, initialStylesIndex);
}
const currentQueries = lView[QUERIES];
if (currentQueries) {
currentQueries.addNode(tNode);
lView[QUERIES] = currentQueries.clone();
}
executeContentQueries(tView, tNode, lView);
}
/** Mark the end of the element. */
export function elementEnd(): void {
let previousOrParentTNode = getPreviousOrParentTNode();
if (getIsParent()) {
setIsParent(false);
} else {
ngDevMode && assertHasParent(getPreviousOrParentTNode());
previousOrParentTNode = previousOrParentTNode.parent !;
setPreviousOrParentTNode(previousOrParentTNode);
}
// this is required for all host-level styling-related instructions to run
// in the correct order
previousOrParentTNode.onElementCreationFns && applyOnCreateInstructions(previousOrParentTNode);
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.Element);
const lView = getLView();
const currentQueries = lView[QUERIES];
if (currentQueries) {
lView[QUERIES] = currentQueries.parent;
}
registerPostOrderHooks(getLView()[TVIEW], previousOrParentTNode);
decreaseElementDepthCount();
// this is fired at the end of elementEnd because ALL of the stylingBindings code
// (for directives and the template) have now executed which means the styling
// context can be instantiated properly.
if (hasClassInput(previousOrParentTNode)) {
const stylingContext = getStylingContext(previousOrParentTNode.index, lView);
setInputsForProperty(
lView, previousOrParentTNode.inputs !['class'] !, getInitialClassNameValue(stylingContext));
}
if (hasStyleInput(previousOrParentTNode)) {
const stylingContext = getStylingContext(previousOrParentTNode.index, lView);
setInputsForProperty(
lView, previousOrParentTNode.inputs !['style'] !,
getInitialStyleStringValue(stylingContext));
}
}
/**
* Creates an empty element using {@link elementStart} and {@link elementEnd}
*
* @param index Index of the element in the data array
* @param name Name of the DOM Node
* @param attrs Statically bound set of attributes, classes, and styles to be written into the DOM
* element on creation. Use [AttributeMarker] to denote the meaning of this array.
* @param localRefs A set of local reference bindings on the element.
*/
export function element(
index: number, name: string, attrs?: TAttributes | null, localRefs?: string[] | null): void {
elementStart(index, name, attrs, localRefs);
elementEnd();
}
/**
* Updates the value of removes an attribute on an Element.
*
* @param number index The index of the element in the data array
* @param name name The name of the attribute.
* @param value value The attribute is removed when value is `null` or `undefined`.
* Otherwise the attribute value is set to the stringified value.
* @param sanitizer An optional function used to sanitize the value.
* @param namespace Optional namespace to use when setting the attribute.
*/
export function elementAttribute(
index: number, name: string, value: any, sanitizer?: SanitizerFn | null,
namespace?: string): void {
if (value !== NO_CHANGE) {
ngDevMode && validateAgainstEventAttributes(name);
const lView = getLView();
const renderer = lView[RENDERER];
const element = getNativeByIndex(index, lView) as RElement;
if (value == null) {
ngDevMode && ngDevMode.rendererRemoveAttribute++;
isProceduralRenderer(renderer) ? renderer.removeAttribute(element, name, namespace) :
element.removeAttribute(name);
} else {
ngDevMode && ngDevMode.rendererSetAttribute++;
const tNode = getTNode(index, lView);
const strValue =
sanitizer == null ? renderStringify(value) : sanitizer(value, tNode.tagName || '', name);
if (isProceduralRenderer(renderer)) {
renderer.setAttribute(element, name, strValue, namespace);
} else {
namespace ? element.setAttributeNS(namespace, name, strValue) :
element.setAttribute(name, strValue);
}
}
}
}
/**
* Assign static attribute values to a host element.
*
* This instruction will assign static attribute values as well as class and style
* values to an element within the host bindings function. Since attribute values
* can consist of different types of values, the `attrs` array must include the values in
* the following format:
*
* attrs = [
* // static attributes (like `title`, `name`, `id`...)
* attr1, value1, attr2, value,
*
* // a single namespace value (like `x:id`)
* NAMESPACE_MARKER, namespaceUri1, name1, value1,
*
* // another single namespace value (like `x:name`)
* NAMESPACE_MARKER, namespaceUri2, name2, value2,
*
* // a series of CSS classes that will be applied to the element (no spaces)
* CLASSES_MARKER, class1, class2, class3,
*
* // a series of CSS styles (property + value) that will be applied to the element
* STYLES_MARKER, prop1, value1, prop2, value2
* ]
*
* All non-class and non-style attributes must be defined at the start of the list
* first before all class and style values are set. When there is a change in value
* type (like when classes and styles are introduced) a marker must be used to separate
* the entries. The marker values themselves are set via entries found in the
* [AttributeMarker] enum.
*
* NOTE: This instruction is meant to used from `hostBindings` function only.
*
* @param directive A directive instance the styling is associated with.
* @param attrs An array of static values (attributes, classes and styles) with the correct marker
* values.
*
* @publicApi
*/
export function elementHostAttrs(attrs: TAttributes) {
const tNode = getPreviousOrParentTNode();
const lView = getLView();
const native = getNativeByTNode(tNode, lView) as RElement;
const lastAttrIndex = setUpAttributes(native, attrs);
const stylingAttrsStartIndex = attrsStylingIndexOf(attrs, lastAttrIndex);
if (stylingAttrsStartIndex >= 0) {
const directive = getActiveHostContext();
if (tNode.stylingTemplate) {
patchContextWithStaticAttrs(tNode.stylingTemplate, attrs, stylingAttrsStartIndex, directive);
} else {
tNode.stylingTemplate = initializeStaticContext(attrs, stylingAttrsStartIndex, directive);
}
}
}

View File

@ -0,0 +1,87 @@
/**
* @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
*/
import {assertDataInRange, assertEqual} from '../../util/assert';
import {assertHasParent} from '../assert';
import {attachPatchData} from '../context_discovery';
import {registerPostOrderHooks} from '../hooks';
import {TAttributes, TNodeType} from '../interfaces/node';
import {BINDING_INDEX, QUERIES, RENDERER, TVIEW} from '../interfaces/view';
import {assertNodeType} from '../node_assert';
import {appendChild} from '../node_manipulation';
import {getIsParent, getLView, getPreviousOrParentTNode, setIsParent, setPreviousOrParentTNode} from '../state';
import {createDirectivesAndLocals, createNodeAtIndex, executeContentQueries, setNodeStylingTemplate} from './shared';
/**
* Creates a logical container for other nodes (<ng-container>) backed by a comment node in the DOM.
* The instruction must later be followed by `elementContainerEnd()` call.
*
* @param index Index of the element in the LView array
* @param attrs Set of attributes to be used when matching directives.
* @param localRefs A set of local reference bindings on the element.
*
* Even if this instruction accepts a set of attributes no actual attribute values are propagated to
* the DOM (as a comment node can't have attributes). Attributes are here only for directive
* matching purposes and setting initial inputs of directives.
*/
export function elementContainerStart(
index: number, attrs?: TAttributes | null, localRefs?: string[] | null): void {
const lView = getLView();
const tView = lView[TVIEW];
const renderer = lView[RENDERER];
const tagName = 'ng-container';
ngDevMode && assertEqual(
lView[BINDING_INDEX], tView.bindingStartIndex,
'element containers should be created before any bindings');
ngDevMode && ngDevMode.rendererCreateComment++;
const native = renderer.createComment(ngDevMode ? tagName : '');
ngDevMode && assertDataInRange(lView, index - 1);
const tNode =
createNodeAtIndex(index, TNodeType.ElementContainer, native, tagName, attrs || null);
if (attrs) {
// While ng-container doesn't necessarily support styling, we use the style context to identify
// and execute directives on the ng-container.
setNodeStylingTemplate(tView, tNode, attrs, 0);
}
appendChild(native, tNode, lView);
createDirectivesAndLocals(tView, lView, localRefs);
attachPatchData(native, lView);
const currentQueries = lView[QUERIES];
if (currentQueries) {
currentQueries.addNode(tNode);
lView[QUERIES] = currentQueries.clone();
}
executeContentQueries(tView, tNode, lView);
}
/** Mark the end of the <ng-container>. */
export function elementContainerEnd(): void {
let previousOrParentTNode = getPreviousOrParentTNode();
const lView = getLView();
const tView = lView[TVIEW];
if (getIsParent()) {
setIsParent(false);
} else {
ngDevMode && assertHasParent(previousOrParentTNode);
previousOrParentTNode = previousOrParentTNode.parent !;
setPreviousOrParentTNode(previousOrParentTNode);
}
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.ElementContainer);
const currentQueries = lView[QUERIES];
if (currentQueries) {
lView[QUERIES] = currentQueries.parent;
}
registerPostOrderHooks(tView, previousOrParentTNode);
}

View File

@ -0,0 +1,141 @@
/**
* @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
*/
import {assertDefined, assertEqual} from '../../util/assert';
import {assertLContainerOrUndefined} from '../assert';
import {ACTIVE_INDEX, LContainer, VIEWS} from '../interfaces/container';
import {RenderFlags} from '../interfaces/definition';
import {TContainerNode, TNodeType} from '../interfaces/node';
import {FLAGS, LView, LViewFlags, PARENT, QUERIES, TVIEW, TView, T_HOST} from '../interfaces/view';
import {assertNodeType} from '../node_assert';
import {insertView, removeView} from '../node_manipulation';
import {enterView, getIsParent, getLView, getPreviousOrParentTNode, isCreationMode, leaveView, setIsParent, setPreviousOrParentTNode} from '../state';
import {resetPreOrderHookFlags} from '../util/view_utils';
import {assignTViewNodeToLView, createLView, createTView, refreshDescendantViews} from './shared';
/**
* Marks the start of an embedded view.
*
* @param viewBlockId The ID of this view
* @return boolean Whether or not this view is in creation mode
*/
export function embeddedViewStart(viewBlockId: number, consts: number, vars: number): RenderFlags {
const lView = getLView();
const previousOrParentTNode = getPreviousOrParentTNode();
// The previous node can be a view node if we are processing an inline for loop
const containerTNode = previousOrParentTNode.type === TNodeType.View ?
previousOrParentTNode.parent ! :
previousOrParentTNode;
const lContainer = lView[containerTNode.index] as LContainer;
ngDevMode && assertNodeType(containerTNode, TNodeType.Container);
let viewToRender = scanForView(lContainer, lContainer[ACTIVE_INDEX] !, viewBlockId);
if (viewToRender) {
setIsParent(true);
enterView(viewToRender, viewToRender[TVIEW].node);
} else {
// When we create a new LView, we always reset the state of the instructions.
viewToRender = createLView(
lView,
getOrCreateEmbeddedTView(viewBlockId, consts, vars, containerTNode as TContainerNode), null,
LViewFlags.CheckAlways, null, null);
if (lContainer[QUERIES]) {
viewToRender[QUERIES] = lContainer[QUERIES] !.createView();
}
const tParentNode = getIsParent() ? previousOrParentTNode :
previousOrParentTNode && previousOrParentTNode.parent;
assignTViewNodeToLView(viewToRender[TVIEW], tParentNode, viewBlockId, viewToRender);
enterView(viewToRender, viewToRender[TVIEW].node);
}
if (lContainer) {
if (isCreationMode(viewToRender)) {
// it is a new view, insert it into collection of views for a given container
insertView(viewToRender, lContainer, lContainer[ACTIVE_INDEX] !);
}
lContainer[ACTIVE_INDEX] !++;
}
return isCreationMode(viewToRender) ? RenderFlags.Create | RenderFlags.Update :
RenderFlags.Update;
}
/**
* Initialize the TView (e.g. static data) for the active embedded view.
*
* Each embedded view block must create or retrieve its own TView. Otherwise, the embedded view's
* static data for a particular node would overwrite the static data for a node in the view above
* it with the same index (since it's in the same template).
*
* @param viewIndex The index of the TView in TNode.tViews
* @param consts The number of nodes, local refs, and pipes in this template
* @param vars The number of bindings and pure function bindings in this template
* @param container The parent container in which to look for the view's static data
* @returns TView
*/
function getOrCreateEmbeddedTView(
viewIndex: number, consts: number, vars: number, parent: TContainerNode): TView {
const tView = getLView()[TVIEW];
ngDevMode && assertNodeType(parent, TNodeType.Container);
const containerTViews = parent.tViews as TView[];
ngDevMode && assertDefined(containerTViews, 'TView expected');
ngDevMode && assertEqual(Array.isArray(containerTViews), true, 'TViews should be in an array');
if (viewIndex >= containerTViews.length || containerTViews[viewIndex] == null) {
containerTViews[viewIndex] = createTView(
viewIndex, null, consts, vars, tView.directiveRegistry, tView.pipeRegistry, null, null);
}
return containerTViews[viewIndex];
}
/**
* Looks for a view with a given view block id inside a provided LContainer.
* Removes views that need to be deleted in the process.
*
* @param lContainer to search for views
* @param startIdx starting index in the views array to search from
* @param viewBlockId exact view block id to look for
* @returns index of a found view or -1 if not found
*/
function scanForView(lContainer: LContainer, startIdx: number, viewBlockId: number): LView|null {
const views = lContainer[VIEWS];
for (let i = startIdx; i < views.length; i++) {
const viewAtPositionId = views[i][TVIEW].id;
if (viewAtPositionId === viewBlockId) {
return views[i];
} else if (viewAtPositionId < viewBlockId) {
// found a view that should not be at this position - remove
removeView(lContainer, i);
} else {
// found a view with id greater than the one we are searching for
// which means that required view doesn't exist and can't be found at
// later positions in the views array - stop the searchdef.cont here
break;
}
}
return null;
}
/** Marks the end of an embedded view. */
export function embeddedViewEnd(): void {
const lView = getLView();
const viewHost = lView[T_HOST];
if (isCreationMode(lView)) {
refreshDescendantViews(lView); // creation mode pass
lView[FLAGS] &= ~LViewFlags.CreationMode;
}
resetPreOrderHookFlags(lView);
refreshDescendantViews(lView); // update mode pass
const lContainer = lView[PARENT] as LContainer;
ngDevMode && assertLContainerOrUndefined(lContainer);
leaveView(lContainer[PARENT] !);
setPreviousOrParentTNode(viewHost !);
setIsParent(false);
}

View File

@ -0,0 +1,20 @@
/**
* @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
*/
import {OpaqueViewState} from '../interfaces/view';
import {getLView} from '../state';
/**
* Returns the current OpaqueViewState instance.
*
* Used in conjunction with the restoreView() instruction to save a snapshot
* of the current view and restore it when listeners are invoked. This allows
* walking the declaration view tree in listeners to get vars from parent views.
*/
export function getCurrentView(): OpaqueViewState {
return getLView() as any as OpaqueViewState;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,181 @@
/**
* @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
*/
import {assertDataInRange} from '../../util/assert';
import {isObservable} from '../../util/lang';
import {PropertyAliasValue, TNode, TNodeFlags, TNodeType} from '../interfaces/node';
import {GlobalTargetResolver, RElement, Renderer3, isProceduralRenderer} from '../interfaces/renderer';
import {FLAGS, LView, LViewFlags, RENDERER, TVIEW} from '../interfaces/view';
import {assertNodeOfPossibleTypes} from '../node_assert';
import {getLView, getPreviousOrParentTNode} from '../state';
import {getComponentViewByIndex, getNativeByTNode, unwrapRNode} from '../util/view_utils';
import {BindingDirection, generatePropertyAliases, getCleanup, handleError, loadComponentRenderer, markViewDirty} from './shared';
/**
* Adds an event listener to the current node.
*
* If an output exists on one of the node's directives, it also subscribes to the output
* and saves the subscription for later cleanup.
*
* @param eventName Name of the event
* @param listenerFn The function to be called when event emits
* @param useCapture Whether or not to use capture in event listener
* @param eventTargetResolver Function that returns global target information in case this listener
* should be attached to a global object like window, document or body
*/
export function listener(
eventName: string, listenerFn: (e?: any) => any, useCapture = false,
eventTargetResolver?: GlobalTargetResolver): void {
listenerInternal(eventName, listenerFn, useCapture, eventTargetResolver);
}
/**
* Registers a synthetic host listener (e.g. `(@foo.start)`) on a component.
*
* This instruction is for compatibility purposes and is designed to ensure that a
* synthetic host listener (e.g. `@HostListener('@foo.start')`) properly gets rendered
* in the component's renderer. Normally all host listeners are evaluated with the
* parent component's renderer, but, in the case of animation @triggers, they need
* to be evaluated with the sub component's renderer (because that's where the
* animation triggers are defined).
*
* Do not use this instruction as a replacement for `listener`. This instruction
* only exists to ensure compatibility with the ViewEngine's host binding behavior.
*
* @param eventName Name of the event
* @param listenerFn The function to be called when event emits
* @param useCapture Whether or not to use capture in event listener
* @param eventTargetResolver Function that returns global target information in case this listener
* should be attached to a global object like window, document or body
*/
export function componentHostSyntheticListener<T>(
eventName: string, listenerFn: (e?: any) => any, useCapture = false,
eventTargetResolver?: GlobalTargetResolver): void {
listenerInternal(eventName, listenerFn, useCapture, eventTargetResolver, loadComponentRenderer);
}
function listenerInternal(
eventName: string, listenerFn: (e?: any) => any, useCapture = false,
eventTargetResolver?: GlobalTargetResolver,
loadRendererFn?: ((tNode: TNode, lView: LView) => Renderer3) | null): void {
const lView = getLView();
const tNode = getPreviousOrParentTNode();
const tView = lView[TVIEW];
const firstTemplatePass = tView.firstTemplatePass;
const tCleanup: false|any[] = firstTemplatePass && (tView.cleanup || (tView.cleanup = []));
ngDevMode && assertNodeOfPossibleTypes(
tNode, TNodeType.Element, TNodeType.Container, TNodeType.ElementContainer);
// add native event listener - applicable to elements only
if (tNode.type === TNodeType.Element) {
const native = getNativeByTNode(tNode, lView) as RElement;
const resolved = eventTargetResolver ? eventTargetResolver(native) : {} as any;
const target = resolved.target || native;
ngDevMode && ngDevMode.rendererAddEventListener++;
const renderer = loadRendererFn ? loadRendererFn(tNode, lView) : lView[RENDERER];
const lCleanup = getCleanup(lView);
const lCleanupIndex = lCleanup.length;
let useCaptureOrSubIdx: boolean|number = useCapture;
// In order to match current behavior, native DOM event listeners must be added for all
// events (including outputs).
if (isProceduralRenderer(renderer)) {
// The first argument of `listen` function in Procedural Renderer is:
// - either a target name (as a string) in case of global target (window, document, body)
// - or element reference (in all other cases)
listenerFn = wrapListener(tNode, lView, listenerFn, false /** preventDefault */);
const cleanupFn = renderer.listen(resolved.name || target, eventName, listenerFn);
lCleanup.push(listenerFn, cleanupFn);
useCaptureOrSubIdx = lCleanupIndex + 1;
} else {
listenerFn = wrapListener(tNode, lView, listenerFn, true /** preventDefault */);
target.addEventListener(eventName, listenerFn, useCapture);
lCleanup.push(listenerFn);
}
const idxOrTargetGetter = eventTargetResolver ?
(_lView: LView) => eventTargetResolver(unwrapRNode(_lView[tNode.index])).target :
tNode.index;
tCleanup && tCleanup.push(eventName, idxOrTargetGetter, lCleanupIndex, useCaptureOrSubIdx);
}
// subscribe to directive outputs
if (tNode.outputs === undefined) {
// if we create TNode here, inputs must be undefined so we know they still need to be
// checked
tNode.outputs = generatePropertyAliases(tNode, BindingDirection.Output);
}
const outputs = tNode.outputs;
let props: PropertyAliasValue|undefined;
if (outputs && (props = outputs[eventName])) {
const propsLength = props.length;
if (propsLength) {
const lCleanup = getCleanup(lView);
for (let i = 0; i < propsLength; i += 3) {
const index = props[i] as number;
ngDevMode && assertDataInRange(lView, index);
const minifiedName = props[i + 2];
const directiveInstance = lView[index];
const output = directiveInstance[minifiedName];
if (ngDevMode && !isObservable(output)) {
throw new Error(
`@Output ${minifiedName} not initialized in '${directiveInstance.constructor.name}'.`);
}
const subscription = output.subscribe(listenerFn);
const idx = lCleanup.length;
lCleanup.push(listenerFn, subscription);
tCleanup && tCleanup.push(eventName, tNode.index, idx, -(idx + 1));
}
}
}
}
/**
* Wraps an event listener with a function that marks ancestors dirty and prevents default behavior,
* if applicable.
*
* @param tNode The TNode associated with this listener
* @param lView The LView that contains this listener
* @param listenerFn The listener function to call
* @param wrapWithPreventDefault Whether or not to prevent default behavior
* (the procedural renderer does this already, so in those cases, we should skip)
*/
function wrapListener(
tNode: TNode, lView: LView, listenerFn: (e?: any) => any,
wrapWithPreventDefault: boolean): EventListener {
// Note: we are performing most of the work in the listener function itself
// to optimize listener registration.
return function wrapListenerIn_markDirtyAndPreventDefault(e: Event) {
// In order to be backwards compatible with View Engine, events on component host nodes
// must also mark the component view itself dirty (i.e. the view that it owns).
const startView =
tNode.flags & TNodeFlags.isComponent ? getComponentViewByIndex(tNode.index, lView) : lView;
// See interfaces/view.ts for more on LViewFlags.ManualOnPush
if ((lView[FLAGS] & LViewFlags.ManualOnPush) === 0) {
markViewDirty(startView);
}
try {
const result = listenerFn(e);
if (wrapWithPreventDefault && result === false) {
e.preventDefault();
// Necessary for legacy browsers that don't support preventDefault (e.g. IE)
e.returnValue = false;
}
return result;
} catch (error) {
handleError(lView, error);
}
};
}

View File

@ -0,0 +1,9 @@
/**
* @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
*/
export {namespaceHTML, namespaceMathML, namespaceSVG} from '../state';

View File

@ -0,0 +1,22 @@
/**
* @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
*/
import {nextContextImpl} from '../state';
/**
* Retrieves a context at the level specified and saves it as the global, contextViewData.
* Will get the next level up if level is not specified.
*
* This is used to save contexts of parent views so they can be bound in embedded views, or
* in conjunction with reference() to bind a ref from a parent view.
*
* @param level The relative level of the view from which to grab context compared to contextVewData
* @returns context
*/
export function nextContext<T = any>(level: number = 1): T {
return nextContextImpl(level);
}

View File

@ -0,0 +1,89 @@
/**
* @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
*/
import {TElementNode, TNode, TNodeType} from '../interfaces/node';
import {CssSelectorList} from '../interfaces/projection';
import {T_HOST} from '../interfaces/view';
import {appendProjectedNodes} from '../node_manipulation';
import {matchingProjectionSelectorIndex} from '../node_selector_matcher';
import {getLView, setIsParent} from '../state';
import {findComponentView} from '../util/view_traversal_utils';
import {createNodeAtIndex} from './shared';
/**
* Instruction to distribute projectable nodes among <ng-content> occurrences in a given template.
* It takes all the selectors from the entire component's template and decides where
* each projected node belongs (it re-distributes nodes among "buckets" where each "bucket" is
* backed by a selector).
*
* This function requires CSS selectors to be provided in 2 forms: parsed (by a compiler) and text,
* un-parsed form.
*
* The parsed form is needed for efficient matching of a node against a given CSS selector.
* The un-parsed, textual form is needed for support of the ngProjectAs attribute.
*
* Having a CSS selector in 2 different formats is not ideal, but alternatives have even more
* drawbacks:
* - having only a textual form would require runtime parsing of CSS selectors;
* - we can't have only a parsed as we can't re-construct textual form from it (as entered by a
* template author).
*
* @param selectors A collection of parsed CSS selectors
* @param rawSelectors A collection of CSS selectors in the raw, un-parsed form
*/
export function projectionDef(selectors?: CssSelectorList[], textSelectors?: string[]): void {
const componentNode = findComponentView(getLView())[T_HOST] as TElementNode;
if (!componentNode.projection) {
const noOfNodeBuckets = selectors ? selectors.length + 1 : 1;
const projectionHeads: (TNode | null)[] = componentNode.projection =
new Array(noOfNodeBuckets).fill(null);
const tails: (TNode | null)[] = projectionHeads.slice();
let componentChild: TNode|null = componentNode.child;
while (componentChild !== null) {
const bucketIndex = selectors ?
matchingProjectionSelectorIndex(componentChild, selectors, textSelectors !) :
0;
if (tails[bucketIndex]) {
tails[bucketIndex] !.projectionNext = componentChild;
} else {
projectionHeads[bucketIndex] = componentChild;
}
tails[bucketIndex] = componentChild;
componentChild = componentChild.next;
}
}
}
/**
* Inserts previously re-distributed projected nodes. This instruction must be preceded by a call
* to the projectionDef instruction.
*
* @param nodeIndex
* @param selectorIndex:
* - 0 when the selector is `*` (or unspecified as this is the default value),
* - 1 based index of the selector from the {@link projectionDef}
*/
export function projection(nodeIndex: number, selectorIndex: number = 0, attrs?: string[]): void {
const lView = getLView();
const tProjectionNode =
createNodeAtIndex(nodeIndex, TNodeType.Projection, null, null, attrs || null);
// We can't use viewData[HOST_NODE] because projection nodes can be nested in embedded views.
if (tProjectionNode.projection === null) tProjectionNode.projection = selectorIndex;
// `<ng-content>` has no content
setIsParent(false);
// re-distribution of projectable nodes is stored on a component's view level
appendProjectedNodes(lView, tProjectionNode, selectorIndex, findComponentView(lView));
}

View File

@ -0,0 +1,259 @@
/**
* @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
*/
import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '../../metadata/schema';
import {validateAgainstEventProperties} from '../../sanitization/sanitization';
import {normalizeDebugBindingName, normalizeDebugBindingValue} from '../../util/ng_reflect';
import {assertLView} from '../assert';
import {bindingUpdated} from '../bindings';
import {PropertyAliasValue, PropertyAliases, TNode, TNodeType} from '../interfaces/node';
import {RComment, RElement, Renderer3, isProceduralRenderer} from '../interfaces/renderer';
import {SanitizerFn} from '../interfaces/sanitization';
import {BINDING_INDEX, FLAGS, HEADER_OFFSET, LView, LViewFlags, RENDERER, TData, TVIEW} from '../interfaces/view';
import {getLView, getSelectedIndex} from '../state';
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../styling/util';
import {NO_CHANGE} from '../tokens';
import {INTERPOLATION_DELIMITER} from '../util/misc_utils';
import {getComponentViewByIndex, getNativeByIndex, getTNode, isComponent} from '../util/view_utils';
import {TsickleIssue1009, initializeTNodeInputs, loadComponentRenderer, setInputsForProperty, storeBindingMetadata} from './shared';
/**
* Update a property on a selected element.
*
* Operates on the element selected by index via the {@link select} instruction.
*
* If the property name also exists as an input property on one of the element's directives,
* the component property will be set instead of the element property. This check must
* be conducted at runtime so child components that add new `@Inputs` don't have to be re-compiled
*
* @param propName Name of property. Because it is going to DOM, this is not subject to
* renaming as part of minification.
* @param value New value to write.
* @param sanitizer An optional function used to sanitize the value.
* @param nativeOnly Whether or not we should only set native properties and skip input check
* (this is necessary for host property bindings)
* @returns This function returns itself so that it may be chained
* (e.g. `property('name', ctx.name)('title', ctx.title)`)
*/
export function property<T>(
propName: string, value: T, sanitizer?: SanitizerFn | null,
nativeOnly?: boolean): TsickleIssue1009 {
const index = getSelectedIndex();
const bindReconciledValue = bind(value);
elementPropertyInternal(index, propName, bindReconciledValue, sanitizer, nativeOnly);
return property;
}
/**
* Creates a single value binding.
*
* @param value Value to diff
*/
export function bind<T>(value: T): T|NO_CHANGE {
const lView = getLView();
const bindingIndex = lView[BINDING_INDEX]++;
storeBindingMetadata(lView);
return bindingUpdated(lView, bindingIndex, value) ? value : NO_CHANGE;
}
/**
* **TODO: Remove this function after `property` is in use**
* Update a property on an element.
*
* If the property name also exists as an input property on one of the element's directives,
* the component property will be set instead of the element property. This check must
* be conducted at runtime so child components that add new @Inputs don't have to be re-compiled.
*
* @param index The index of the element to update in the data array
* @param propName Name of property. Because it is going to DOM, this is not subject to
* renaming as part of minification.
* @param value New value to write.
* @param sanitizer An optional function used to sanitize the value.
* @param nativeOnly Whether or not we should only set native properties and skip input check
* (this is necessary for host property bindings)
*/
export function elementProperty<T>(
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
nativeOnly?: boolean): void {
elementPropertyInternal(index, propName, value, sanitizer, nativeOnly);
}
/**
* Updates a synthetic host binding (e.g. `[@foo]`) on a component.
*
* This instruction is for compatibility purposes and is designed to ensure that a
* synthetic host binding (e.g. `@HostBinding('@foo')`) properly gets rendered in
* the component's renderer. Normally all host bindings are evaluated with the parent
* component's renderer, but, in the case of animation @triggers, they need to be
* evaluated with the sub component's renderer (because that's where the animation
* triggers are defined).
*
* Do not use this instruction as a replacement for `elementProperty`. This instruction
* only exists to ensure compatibility with the ViewEngine's host binding behavior.
*
* @param index The index of the element to update in the data array
* @param propName Name of property. Because it is going to DOM, this is not subject to
* renaming as part of minification.
* @param value New value to write.
* @param sanitizer An optional function used to sanitize the value.
* @param nativeOnly Whether or not we should only set native properties and skip input check
* (this is necessary for host property bindings)
*/
export function componentHostSyntheticProperty<T>(
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
nativeOnly?: boolean) {
elementPropertyInternal(index, propName, value, sanitizer, nativeOnly, loadComponentRenderer);
}
/**
* Mapping between attributes names that don't correspond to their element property names.
*/
const ATTR_TO_PROP: {[name: string]: string} = {
'class': 'className',
'for': 'htmlFor',
'formaction': 'formAction',
'innerHtml': 'innerHTML',
'readonly': 'readOnly',
'tabindex': 'tabIndex',
};
function elementPropertyInternal<T>(
index: number, propName: string, value: T | NO_CHANGE, sanitizer?: SanitizerFn | null,
nativeOnly?: boolean,
loadRendererFn?: ((tNode: TNode, lView: LView) => Renderer3) | null): void {
if (value === NO_CHANGE) return;
const lView = getLView();
const element = getNativeByIndex(index, lView) as RElement | RComment;
const tNode = getTNode(index, lView);
let inputData: PropertyAliases|null|undefined;
let dataValue: PropertyAliasValue|undefined;
if (!nativeOnly && (inputData = initializeTNodeInputs(tNode)) &&
(dataValue = inputData[propName])) {
setInputsForProperty(lView, dataValue, value);
if (isComponent(tNode)) markDirtyIfOnPush(lView, index + HEADER_OFFSET);
if (ngDevMode) {
if (tNode.type === TNodeType.Element || tNode.type === TNodeType.Container) {
setNgReflectProperties(lView, element, tNode.type, dataValue, value);
}
}
} else if (tNode.type === TNodeType.Element) {
propName = ATTR_TO_PROP[propName] || propName;
if (ngDevMode) {
validateAgainstEventProperties(propName);
validateAgainstUnknownProperties(lView, element, propName, tNode);
ngDevMode.rendererSetProperty++;
}
savePropertyDebugData(tNode, lView, propName, lView[TVIEW].data, nativeOnly);
const renderer = loadRendererFn ? loadRendererFn(tNode, lView) : lView[RENDERER];
// It is assumed that the sanitizer is only added when the compiler determines that the property
// is risky, so sanitization can be done without further checks.
value = sanitizer != null ? (sanitizer(value, tNode.tagName || '', propName) as any) : value;
if (isProceduralRenderer(renderer)) {
renderer.setProperty(element as RElement, propName, value);
} else if (!isAnimationProp(propName)) {
(element as RElement).setProperty ? (element as any).setProperty(propName, value) :
(element as any)[propName] = value;
}
}
}
/** If node is an OnPush component, marks its LView dirty. */
function markDirtyIfOnPush(lView: LView, viewIndex: number): void {
ngDevMode && assertLView(lView);
const childComponentLView = getComponentViewByIndex(viewIndex, lView);
if (!(childComponentLView[FLAGS] & LViewFlags.CheckAlways)) {
childComponentLView[FLAGS] |= LViewFlags.Dirty;
}
}
function setNgReflectProperties(
lView: LView, element: RElement | RComment, type: TNodeType, inputs: PropertyAliasValue,
value: any) {
for (let i = 0; i < inputs.length; i += 3) {
const renderer = lView[RENDERER];
const attrName = normalizeDebugBindingName(inputs[i + 2] as string);
const debugValue = normalizeDebugBindingValue(value);
if (type === TNodeType.Element) {
isProceduralRenderer(renderer) ?
renderer.setAttribute((element as RElement), attrName, debugValue) :
(element as RElement).setAttribute(attrName, debugValue);
} else if (value !== undefined) {
const value = `bindings=${JSON.stringify({[attrName]: debugValue}, null, 2)}`;
if (isProceduralRenderer(renderer)) {
renderer.setValue((element as RComment), value);
} else {
(element as RComment).textContent = value;
}
}
}
}
function validateAgainstUnknownProperties(
hostView: LView, element: RElement | RComment, propName: string, tNode: TNode) {
// If the tag matches any of the schemas we shouldn't throw.
if (matchingSchemas(hostView, tNode.tagName)) {
return;
}
// If prop is not a known property of the HTML element...
if (!(propName in element) &&
// and we are in a browser context... (web worker nodes should be skipped)
typeof Node === 'function' && element instanceof Node &&
// and isn't a synthetic animation property...
propName[0] !== ANIMATION_PROP_PREFIX) {
// ... it is probably a user error and we should throw.
throw new Error(
`Template error: Can't bind to '${propName}' since it isn't a known property of '${tNode.tagName}'.`);
}
}
function matchingSchemas(hostView: LView, tagName: string | null): boolean {
const schemas = hostView[TVIEW].schemas;
if (schemas !== null) {
for (let i = 0; i < schemas.length; i++) {
const schema = schemas[i];
if (schema === NO_ERRORS_SCHEMA ||
schema === CUSTOM_ELEMENTS_SCHEMA && tagName && tagName.indexOf('-') > -1) {
return true;
}
}
}
return false;
}
/**
* Stores debugging data for this property binding on first template pass.
* This enables features like DebugElement.properties.
*/
function savePropertyDebugData(
tNode: TNode, lView: LView, propName: string, tData: TData,
nativeOnly: boolean | undefined): void {
const lastBindingIndex = lView[BINDING_INDEX] - 1;
// Bind/interpolation functions save binding metadata in the last binding index,
// but leave the property name blank. If the interpolation delimiter is at the 0
// index, we know that this is our first pass and the property name still needs to
// be set.
const bindingMetadata = tData[lastBindingIndex] as string;
if (bindingMetadata[0] == INTERPOLATION_DELIMITER) {
tData[lastBindingIndex] = propName + bindingMetadata;
// We don't want to store indices for host bindings because they are stored in a
// different part of LView (the expando section).
if (!nativeOnly) {
if (tNode.propertyMetadataStartIndex == -1) {
tNode.propertyMetadataStartIndex = lastBindingIndex;
}
tNode.propertyMetadataEndIndex = lastBindingIndex + 1;
}
}
}

View File

@ -0,0 +1,253 @@
/**
* @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
*/
import {assertEqual, assertLessThan} from '../../util/assert';
import {bindingUpdated, bindingUpdated2, bindingUpdated3, bindingUpdated4} from '../bindings';
import {BINDING_INDEX, TVIEW} from '../interfaces/view';
import {getLView} from '../state';
import {NO_CHANGE} from '../tokens';
import {renderStringify} from '../util/misc_utils';
import {storeBindingMetadata} from './shared';
/**
* Create interpolation bindings with a variable number of expressions.
*
* If there are 1 to 8 expressions `interpolation1()` to `interpolation8()` should be used instead.
* Those are faster because there is no need to create an array of expressions and iterate over it.
*
* `values`:
* - has static text at even indexes,
* - has evaluated expressions at odd indexes.
*
* Returns the concatenated string when any of the arguments changes, `NO_CHANGE` otherwise.
*/
export function interpolationV(values: any[]): string|NO_CHANGE {
ngDevMode && assertLessThan(2, values.length, 'should have at least 3 values');
ngDevMode && assertEqual(values.length % 2, 1, 'should have an odd number of values');
let different = false;
const lView = getLView();
const tData = lView[TVIEW].data;
let bindingIndex = lView[BINDING_INDEX];
if (tData[bindingIndex] == null) {
// 2 is the index of the first static interstitial value (ie. not prefix)
for (let i = 2; i < values.length; i += 2) {
tData[bindingIndex++] = values[i];
}
bindingIndex = lView[BINDING_INDEX];
}
for (let i = 1; i < values.length; i += 2) {
// Check if bindings (odd indexes) have changed
bindingUpdated(lView, bindingIndex++, values[i]) && (different = true);
}
lView[BINDING_INDEX] = bindingIndex;
storeBindingMetadata(lView, values[0], values[values.length - 1]);
if (!different) {
return NO_CHANGE;
}
// Build the updated content
let content = values[0];
for (let i = 1; i < values.length; i += 2) {
content += renderStringify(values[i]) + values[i + 1];
}
return content;
}
/**
* Creates an interpolation binding with 1 expression.
*
* @param prefix static value used for concatenation only.
* @param v0 value checked for change.
* @param suffix static value used for concatenation only.
*/
export function interpolation1(prefix: string, v0: any, suffix: string): string|NO_CHANGE {
const lView = getLView();
const different = bindingUpdated(lView, lView[BINDING_INDEX]++, v0);
storeBindingMetadata(lView, prefix, suffix);
return different ? prefix + renderStringify(v0) + suffix : NO_CHANGE;
}
/** Creates an interpolation binding with 2 expressions. */
export function interpolation2(
prefix: string, v0: any, i0: string, v1: any, suffix: string): string|NO_CHANGE {
const lView = getLView();
const bindingIndex = lView[BINDING_INDEX];
const different = bindingUpdated2(lView, bindingIndex, v0, v1);
lView[BINDING_INDEX] += 2;
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
lView[TVIEW].data[bindingIndex] = i0;
}
return different ? prefix + renderStringify(v0) + i0 + renderStringify(v1) + suffix : NO_CHANGE;
}
/** Creates an interpolation binding with 3 expressions. */
export function interpolation3(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, suffix: string): string|
NO_CHANGE {
const lView = getLView();
const bindingIndex = lView[BINDING_INDEX];
const different = bindingUpdated3(lView, bindingIndex, v0, v1, v2);
lView[BINDING_INDEX] += 3;
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + suffix :
NO_CHANGE;
}
/** Create an interpolation binding with 4 expressions. */
export function interpolation4(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any,
suffix: string): string|NO_CHANGE {
const lView = getLView();
const bindingIndex = lView[BINDING_INDEX];
const different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
lView[BINDING_INDEX] += 4;
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
tData[bindingIndex + 2] = i2;
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
renderStringify(v3) + suffix :
NO_CHANGE;
}
/** Creates an interpolation binding with 5 expressions. */
export function interpolation5(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any,
i3: string, v4: any, suffix: string): string|NO_CHANGE {
const lView = getLView();
const bindingIndex = lView[BINDING_INDEX];
let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
different = bindingUpdated(lView, bindingIndex + 4, v4) || different;
lView[BINDING_INDEX] += 5;
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
tData[bindingIndex + 2] = i2;
tData[bindingIndex + 3] = i3;
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
renderStringify(v3) + i3 + renderStringify(v4) + suffix :
NO_CHANGE;
}
/** Creates an interpolation binding with 6 expressions. */
export function interpolation6(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any,
i3: string, v4: any, i4: string, v5: any, suffix: string): string|NO_CHANGE {
const lView = getLView();
const bindingIndex = lView[BINDING_INDEX];
let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
different = bindingUpdated2(lView, bindingIndex + 4, v4, v5) || different;
lView[BINDING_INDEX] += 6;
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
tData[bindingIndex + 2] = i2;
tData[bindingIndex + 3] = i3;
tData[bindingIndex + 4] = i4;
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + suffix :
NO_CHANGE;
}
/** Creates an interpolation binding with 7 expressions. */
export function interpolation7(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any,
i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, suffix: string): string|
NO_CHANGE {
const lView = getLView();
const bindingIndex = lView[BINDING_INDEX];
let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
different = bindingUpdated3(lView, bindingIndex + 4, v4, v5, v6) || different;
lView[BINDING_INDEX] += 7;
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
tData[bindingIndex + 2] = i2;
tData[bindingIndex + 3] = i3;
tData[bindingIndex + 4] = i4;
tData[bindingIndex + 5] = i5;
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + i5 +
renderStringify(v6) + suffix :
NO_CHANGE;
}
/** Creates an interpolation binding with 8 expressions. */
export function interpolation8(
prefix: string, v0: any, i0: string, v1: any, i1: string, v2: any, i2: string, v3: any,
i3: string, v4: any, i4: string, v5: any, i5: string, v6: any, i6: string, v7: any,
suffix: string): string|NO_CHANGE {
const lView = getLView();
const bindingIndex = lView[BINDING_INDEX];
let different = bindingUpdated4(lView, bindingIndex, v0, v1, v2, v3);
different = bindingUpdated4(lView, bindingIndex + 4, v4, v5, v6, v7) || different;
lView[BINDING_INDEX] += 8;
// Only set static strings the first time (data will be null subsequent runs).
const data = storeBindingMetadata(lView, prefix, suffix);
if (data) {
const tData = lView[TVIEW].data;
tData[bindingIndex] = i0;
tData[bindingIndex + 1] = i1;
tData[bindingIndex + 2] = i2;
tData[bindingIndex + 3] = i3;
tData[bindingIndex + 4] = i4;
tData[bindingIndex + 5] = i5;
tData[bindingIndex + 6] = i6;
}
return different ?
prefix + renderStringify(v0) + i0 + renderStringify(v1) + i1 + renderStringify(v2) + i2 +
renderStringify(v3) + i3 + renderStringify(v4) + i4 + renderStringify(v5) + i5 +
renderStringify(v6) + i6 + renderStringify(v7) + suffix :
NO_CHANGE;
}

View File

@ -0,0 +1,40 @@
/**
* @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
*/
import {assertGreaterThan, assertLessThan} from '../../util/assert';
import {executePreOrderHooks} from '../hooks';
import {HEADER_OFFSET, TVIEW} from '../interfaces/view';
import {getCheckNoChangesMode, getLView, setSelectedIndex} from '../state';
/**
* Selects an index of an item to act on and flushes lifecycle hooks up to this point
*
* Used in conjunction with instructions like {@link property} to act on elements with specified
* indices, for example those created with {@link element} or {@link elementStart}.
*
* ```ts
* (rf: RenderFlags, ctx: any) => {
* if (rf & 1) {
* element(0, 'div');
* }
* if (rf & 2) {
* select(0); // Select the <div/> created above.
* property('title', 'test');
* }
* }
* ```
* @param index the index of the item to act on with the following instructions
*/
export function select(index: number): void {
ngDevMode && assertGreaterThan(index, -1, 'Invalid index');
ngDevMode &&
assertLessThan(
index, getLView().length - HEADER_OFFSET, 'Should be within range for the view data');
setSelectedIndex(index);
const lView = getLView();
executePreOrderHooks(lView, lView[TVIEW], getCheckNoChangesMode(), index);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
/**
* @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
*/
import {HEADER_OFFSET, TVIEW} from '../interfaces/view';
import {getContextLView, getLView} from '../state';
import {loadInternal} from '../util/view_utils';
/** Store a value in the `data` at a given `index`. */
export function store<T>(index: number, value: T): void {
const lView = getLView();
const tView = lView[TVIEW];
// We don't store any static data for local variables, so the first time
// we see the template, we should store as null to avoid a sparse array
const adjustedIndex = index + HEADER_OFFSET;
if (adjustedIndex >= tView.data.length) {
tView.data[adjustedIndex] = null;
tView.blueprint[adjustedIndex] = null;
}
lView[adjustedIndex] = value;
}
/**
* Retrieves a local reference from the current contextViewData.
*
* If the reference to retrieve is in a parent view, this instruction is used in conjunction
* with a nextContext() call, which walks up the tree and updates the contextViewData instance.
*
* @param index The index of the local ref in contextViewData.
*/
export function reference<T>(index: number) {
const contextLView = getContextLView();
return loadInternal<T>(contextLView, index);
}
/** Retrieves a value from current `viewData`. */
export function load<T>(index: number): T {
return loadInternal<T>(getLView(), index);
}

View File

@ -17,11 +17,7 @@ import {NO_CHANGE} from '../tokens';
import {renderStringify} from '../util/misc_utils';
import {getRootContext} from '../util/view_traversal_utils';
import {getTNode} from '../util/view_utils';
import {scheduleTick} from './instructions';
import {setInputsForProperty} from './shared';
import {scheduleTick, setInputsForProperty} from './shared';
/*
* The contents of this file include the instructions for all styling-related

View File

@ -0,0 +1,57 @@
/**
* @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
*/
import {assertDataInRange, assertDefined, assertEqual} from '../../util/assert';
import {TNodeType} from '../interfaces/node';
import {RText, isProceduralRenderer} from '../interfaces/renderer';
import {BINDING_INDEX, HEADER_OFFSET, RENDERER, TVIEW} from '../interfaces/view';
import {appendChild, createTextNode} from '../node_manipulation';
import {getLView, setIsParent} from '../state';
import {NO_CHANGE} from '../tokens';
import {renderStringify} from '../util/misc_utils';
import {getNativeByIndex} from '../util/view_utils';
import {createNodeAtIndex} from './shared';
/**
* Create static text node
*
* @param index Index of the node in the data array
* @param value Value to write. This value will be stringified.
*/
export function text(index: number, value?: any): void {
const lView = getLView();
ngDevMode && assertEqual(
lView[BINDING_INDEX], lView[TVIEW].bindingStartIndex,
'text nodes should be created before any bindings');
ngDevMode && ngDevMode.rendererCreateTextNode++;
const textNative = createTextNode(value, lView[RENDERER]);
const tNode = createNodeAtIndex(index, TNodeType.Element, textNative, null, null);
// Text nodes are self closing.
setIsParent(false);
appendChild(textNative, tNode, lView);
}
/**
* Create text node with binding
* Bindings should be handled externally with the proper interpolation(1-8) method
*
* @param index Index of the node in the data array.
* @param value Stringified value to write.
*/
export function textBinding<T>(index: number, value: T | NO_CHANGE): void {
if (value !== NO_CHANGE) {
const lView = getLView();
ngDevMode && assertDataInRange(lView, index + HEADER_OFFSET);
const element = getNativeByIndex(index, lView) as any as RText;
ngDevMode && assertDefined(element, 'native element should exist');
ngDevMode && ngDevMode.rendererSetText++;
const renderer = lView[RENDERER];
isProceduralRenderer(renderer) ? renderer.setValue(element, renderStringify(value)) :
element.textContent = renderStringify(value);
}
}

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ComponentType} from '..';
import {R3DirectiveMetadataFacade, getCompilerFacade} from '../../compiler/compiler_facade';
import {R3ComponentMetadataFacade, R3QueryMetadataFacade} from '../../compiler/compiler_facade_interface';
import {resolveForwardRef} from '../../di/forward_ref';
@ -18,6 +17,7 @@ import {componentNeedsResolution, maybeQueueResolutionOfComponentResources} from
import {ViewEncapsulation} from '../../metadata/view';
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
import {NG_COMPONENT_DEF, NG_DIRECTIVE_DEF} from '../fields';
import {ComponentType} from '../interfaces/definition';
import {renderStringify} from '../util/misc_utils';
import {angularCoreEnv} from './environment';

View File

@ -8,12 +8,13 @@
import '../util/ng_dev_mode';
import {getLContext} from './context_discovery';
import {scheduleTick} from './instructions/all';
import {scheduleTick} from './instructions/shared';
import {ComponentInstance, DirectiveInstance, Player} from './interfaces/player';
import {RootContextFlags} from './interfaces/view';
import {addPlayerInternal, getOrCreatePlayerContext, getPlayerContext, getPlayersInternal, getStylingContext, throwInvalidRefError} from './styling/util';
import {getRootContext} from './util/view_traversal_utils';
/**
* Adds a player to an element, directive or component instance that will later be
* animated once change detection has passed.

View File

@ -18,7 +18,8 @@ import {assertDataInRange, assertDefined, assertEqual} from '../util/assert';
import {assertPreviousIsParent} from './assert';
import {getNodeInjectable, locateDirectiveOrProvider} from './di';
import {NG_ELEMENT_ID} from './fields';
import {load, store, storeCleanupWithContext} from './instructions/all';
import {load, store} from './instructions/all';
import {storeCleanupWithContext} from './instructions/shared';
import {unusedValueExportToPlacateAjd as unused1} from './interfaces/definition';
import {unusedValueExportToPlacateAjd as unused2} from './interfaces/injector';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, unusedValueExportToPlacateAjd as unused3} from './interfaces/node';

View File

@ -365,3 +365,32 @@ export function getSelectedIndex() {
export function setSelectedIndex(index: number) {
_selectedIndex = index;
}
let _currentNamespace: string|null = null;
/**
* Sets the namespace used to create elements to `'http://www.w3.org/2000/svg'` in global state.
*/
export function namespaceSVG() {
_currentNamespace = 'http://www.w3.org/2000/svg';
}
/**
* Sets the namespace used to create elements to `'http://www.w3.org/1998/MathML/'` in global state.
*/
export function namespaceMathML() {
_currentNamespace = 'http://www.w3.org/1998/MathML/';
}
/**
* Sets the namespace used to create elements no `null`, which forces element creation to use
* `createElement` rather than `createElementNS`.
*/
export function namespaceHTML() {
_currentNamespace = null;
}
export function getNamespace(): string|null {
return _currentNamespace;
}

View File

@ -18,18 +18,18 @@ import {Renderer2} from '../render/api';
import {assertDefined, assertGreaterThan, assertLessThan} from '../util/assert';
import {NodeInjector, getParentInjectorLocation} from './di';
import {addToViewTree, createEmbeddedViewAndNode, createLContainer, renderEmbeddedTemplate} from './instructions/all';
import {addToViewTree, createEmbeddedViewAndNode, createLContainer, renderEmbeddedTemplate} from './instructions/shared';
import {ACTIVE_INDEX, LContainer, NATIVE, VIEWS} from './interfaces/container';
import {TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
import {RComment, RElement, isProceduralRenderer} from './interfaces/renderer';
import {CONTEXT, FLAGS, LView, LViewFlags, PARENT, QUERIES, RENDERER, TView, T_HOST} from './interfaces/view';
import {CONTEXT, LView, QUERIES, RENDERER, TView, T_HOST} from './interfaces/view';
import {assertNodeOfPossibleTypes} from './node_assert';
import {addRemoveViewFromContainer, appendChild, detachView, getBeforeNodeForView, insertView, nativeInsertBefore, nativeNextSibling, nativeParentNode, removeView} from './node_manipulation';
import {getParentInjectorTNode} from './node_util';
import {getLView, getPreviousOrParentTNode} from './state';
import {getParentInjectorView, hasParentInjector} from './util/injector_utils';
import {findComponentView} from './util/view_traversal_utils';
import {getComponentViewByIndex, getNativeByTNode, isComponent, isLContainer, isRootView, viewAttachedToChangeDetector, viewAttachedToContainer} from './util/view_utils';
import {getComponentViewByIndex, getNativeByTNode, isComponent, isLContainer, isRootView, viewAttachedToContainer} from './util/view_utils';
import {ViewRef} from './view_ref';

View File

@ -11,7 +11,7 @@ import {ChangeDetectorRef as viewEngine_ChangeDetectorRef} from '../change_detec
import {ViewContainerRef as viewEngine_ViewContainerRef} from '../linker/view_container_ref';
import {EmbeddedViewRef as viewEngine_EmbeddedViewRef, InternalViewRef as viewEngine_InternalViewRef} from '../linker/view_ref';
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn} from './instructions/all';
import {checkNoChangesInRootView, checkNoChangesInternal, detectChangesInRootView, detectChangesInternal, markViewDirty, storeCleanupFn} from './instructions/shared';
import {TElementNode, TNode, TNodeType, TViewNode} from './interfaces/node';
import {FLAGS, HOST, LView, LViewFlags, T_HOST} from './interfaces/view';
import {destroyLView, renderDetachView} from './node_manipulation';

View File

@ -377,6 +377,9 @@
{
"name": "getNameOnlyMarkerIndex"
},
{
"name": "getNamespace"
},
{
"name": "getNativeAnchorNode"
},

View File

@ -644,9 +644,6 @@
{
"name": "findViaComponent"
},
{
"name": "select"
},
{
"name": "forwardRef"
},
@ -770,6 +767,9 @@
{
"name": "getNameOnlyMarkerIndex"
},
{
"name": "getNamespace"
},
{
"name": "getNativeAnchorNode"
},
@ -1220,6 +1220,9 @@
{
"name": "searchTokensOnInjector"
},
{
"name": "select"
},
{
"name": "setActiveHost"
},

View File

@ -9,24 +9,23 @@
import {Attribute, ChangeDetectorRef, ElementRef, Host, INJECTOR, Inject, InjectFlags, Injector, Optional, Renderer2, Self, SkipSelf, TemplateRef, ViewContainerRef, defineInjectable, defineInjector} from '@angular/core';
import {ComponentType, RenderFlags} from '@angular/core/src/render3/interfaces/definition';
import {createInjector} from '../../src/di/r3_injector';
import {defineComponent} from '../../src/render3/definition';
import {bloomAdd, bloomHasToken, bloomHashBitOrFactory as bloomHash, getOrCreateNodeInjectorForNode} from '../../src/render3/di';
import {ProvidersFeature, defineDirective, elementProperty, load, templateRefExtractor} from '../../src/render3/index';
import {allocHostVars, bind, container, containerRefreshEnd, containerRefreshStart, createNodeAtIndex, createLView, createTView, directiveInject, element, elementEnd, elementStart, embeddedViewEnd, embeddedViewStart, injectAttribute, interpolation2, projection, projectionDef, reference, template, text, textBinding, elementContainerStart, elementContainerEnd} from '../../src/render3/instructions/all';
import {isProceduralRenderer, RElement} from '../../src/render3/interfaces/renderer';
import {ProvidersFeature, defineDirective, elementProperty, load, templateRefExtractor, elementStart, text, elementEnd, reference, textBinding, bind, directiveInject, element, container, containerRefreshStart, embeddedViewStart, embeddedViewEnd, containerRefreshEnd, template, allocHostVars, interpolation2, elementContainerStart, elementContainerEnd, projectionDef, projection, injectAttribute} from '../../src/render3/index';
import {LContainer, NATIVE} from '../../src/render3/interfaces/container';
import {TNODE} from '../../src/render3/interfaces/injector';
import {AttributeMarker, TNodeType} from '../../src/render3/interfaces/node';
import {getNativeByIndex} from '../../src/render3/util/view_utils';
import {RElement, isProceduralRenderer} from '../../src/render3/interfaces/renderer';
import {LViewFlags} from '../../src/render3/interfaces/view';
import {enterView, leaveView, getLView} from '../../src/render3/state';
import {enterView, getLView, leaveView} from '../../src/render3/state';
import {getNativeByIndex} from '../../src/render3/util/view_utils';
import {ViewRef} from '../../src/render3/view_ref';
import {NgIf} from './common_with_def';
import {getRendererFactory2} from './imported_renderer2';
import {ComponentFixture, createComponent, createDirective, getDirectiveOnNode, renderComponent, toHtml} from './render_util';
import {NgIf} from './common_with_def';
import {TNODE} from '../../src/render3/interfaces/injector';
import {createInjector} from '../../src/di/r3_injector';
import {LContainer, NATIVE} from '../../src/render3/interfaces/container';
import {createLView, createTView, createNodeAtIndex} from '@angular/core/src/render3/instructions/shared';
describe('di', () => {
describe('no dependencies', () => {

View File

@ -7,19 +7,15 @@
*/
import {NgForOfContext} from '@angular/common';
import {RenderFlags} from '../../src/render3';
import {RenderFlags, elementStart, elementStyling, elementEnd, element, elementProperty, bind, elementAttribute, select, elementStyleProp, elementStylingApply, elementStylingMap, template, text, textBinding, interpolation1, property} from '../../src/render3/index';
import {defineComponent} from '../../src/render3/definition';
import {bind, element, elementAttribute, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, elementStylingMap, interpolation1, renderTemplate, template, text, textBinding, select, property} from '../../src/render3/instructions/all';
import {AttributeMarker} from '../../src/render3/interfaces/node';
import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl} from '../../src/sanitization/bypass';
import {defaultStyleSanitizer, sanitizeHtml, sanitizeResourceUrl, sanitizeScript, sanitizeStyle, sanitizeUrl} from '../../src/sanitization/sanitization';
import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
import {StyleSanitizeFn} from '../../src/sanitization/style_sanitizer';
import {NgForOf} from './common_with_def';
import {ComponentFixture, TemplateFixture} from './render_util';
import {setSelectedIndex, getSelectedIndex} from '@angular/core/src/render3/state';
describe('instructions', () => {
function createAnchor() {

View File

@ -11,7 +11,7 @@ import {AttributeMarker, TAttributes, TNode, TNodeType} from '../../src/render3/
import {CssSelector, CssSelectorList, NG_PROJECT_AS_ATTR_NAME, SelectorFlags,} from '../../src/render3/interfaces/projection';
import {getProjectAsAttrValue, isNodeMatchingSelectorList, isNodeMatchingSelector} from '../../src/render3/node_selector_matcher';
import {initializeStaticContext} from '../../src/render3/styling/class_and_style_bindings';
import {createTNode} from '@angular/core/src/render3/instructions/all';
import {createTNode} from '@angular/core/src/render3/instructions/shared';
function testLStaticData(tagName: string, attrs: TAttributes | null): TNode {
return createTNode(null, TNodeType.Element, 0, tagName, attrs);

View File

@ -12,6 +12,7 @@ import {ElementRef} from '@angular/core/src/linker/element_ref';
import {TemplateRef} from '@angular/core/src/linker/template_ref';
import {ViewContainerRef} from '@angular/core/src/linker/view_container_ref';
import {Renderer2} from '@angular/core/src/render/api';
import {renderTemplate} from '@angular/core/src/render3/instructions/shared';
import {getLView} from '@angular/core/src/render3/state';
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
@ -27,7 +28,6 @@ import {getDirectivesAtNodeIndex, getLContext, isComponentInstance} from '../../
import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition';
import {NG_ELEMENT_ID} from '../../src/render3/fields';
import {ComponentTemplate, ComponentType, DirectiveDef, DirectiveType, ProvidersFeature, RenderFlags, defineComponent, defineDirective, renderComponent as _renderComponent, tick} from '../../src/render3/index';
import {renderTemplate} from '../../src/render3/instructions/all';
import {DirectiveDefList, DirectiveTypesOrFactory, HostBindingsFunction, PipeDef, PipeDefList, PipeTypesOrFactory} from '../../src/render3/interfaces/definition';
import {PlayerHandler} from '../../src/render3/interfaces/player';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, RendererStyleFlags3, domRendererFactory3} from '../../src/render3/interfaces/renderer';

View File

@ -5,10 +5,11 @@
* 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
*/
import {createLView, createTView} from '@angular/core/src/render3/instructions/shared';
import {createRootContext} from '../../../src/render3/component';
import {getLContext} from '../../../src/render3/context_discovery';
import {defineComponent, defineDirective} from '../../../src/render3/index';
import {createLView, createTView, elementClassProp, elementEnd, elementHostAttrs, elementHostClassProp, elementHostStyleProp, elementHostStyling, elementHostStylingApply, elementHostStylingMap, elementStart, elementStyleProp, elementStyling, elementStylingApply, elementStylingMap, namespaceSVG} from '../../../src/render3/instructions/all';
import {defineComponent, defineDirective, elementClassProp, elementEnd, elementHostClassProp, elementHostStyleProp, elementHostStyling, elementHostStylingApply, elementStart, elementStyleProp, elementStyling, elementStylingApply, elementStylingMap, namespaceSVG} from '../../../src/render3/index';
import {RenderFlags} from '../../../src/render3/interfaces/definition';
import {AttributeMarker, TAttributes} from '../../../src/render3/interfaces/node';
import {BindingStore, BindingType, PlayState, Player, PlayerContext, PlayerFactory, PlayerHandler} from '../../../src/render3/interfaces/player';

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {createLContainer, createLView, createTNode, createTView} from '@angular/core/src/render3/instructions/all';
import {createLContainer, createLView, createTNode, createTView} from '@angular/core/src/render3/instructions/shared';
import {createEmptyStylingContext} from '@angular/core/src/render3/styling/util';
import {isLContainer, isLView, isStylingContext, unwrapLContainer, unwrapLView, unwrapRNode, unwrapStylingContext} from '@angular/core/src/render3/util/view_utils';