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:
parent
03d914a6c2
commit
5a724b34bd
|
@ -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';
|
||||
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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';
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -377,6 +377,9 @@
|
|||
{
|
||||
"name": "getNameOnlyMarkerIndex"
|
||||
},
|
||||
{
|
||||
"name": "getNamespace"
|
||||
},
|
||||
{
|
||||
"name": "getNativeAnchorNode"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
|
Loading…
Reference in New Issue