This patch is a final major refactor in styling Angular. This PR includes three main fixes: All temporary state taht is persisted between template style/class application and style/class application in host bindings is now removed. Removes the styling() and stylingApply() instructions. Introduces a "direct apply" mode that is used apply prop-based style/class in the event that there are no map-based bindings as well as property collisions. PR Close #32259 PR Close #32596
This commit is contained in:
@@ -16,8 +16,8 @@
|
||||
"uncompressed": {
|
||||
"runtime-es5": 3042,
|
||||
"runtime-es2015": 3048,
|
||||
"main-es5": 493330,
|
||||
"main-es2015": 435296,
|
||||
"main-es5": 492593,
|
||||
"main-es2015": 438296,
|
||||
"polyfills-es5": 131024,
|
||||
"polyfills-es2015": 52433
|
||||
}
|
||||
@@ -28,7 +28,7 @@
|
||||
"uncompressed": {
|
||||
"runtime-es5": 2932,
|
||||
"runtime-es2015": 2938,
|
||||
"main-es5": 550854,
|
||||
"main-es5": 552068,
|
||||
"main-es2015": 493320,
|
||||
"polyfills-es5": 131024,
|
||||
"polyfills-es2015": 52433
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 1440,
|
||||
"main": 13264,
|
||||
"main": 13604,
|
||||
"polyfills": 45340
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export class TableComponent {
|
||||
@NgModule({imports: [BrowserModule], bootstrap: [TableComponent], declarations: [TableComponent]})
|
||||
export class AppModule {
|
||||
constructor(sanitizer: DomSanitizer) {
|
||||
trustedEmptyColor = sanitizer.bypassSecurityTrustStyle('');
|
||||
trustedEmptyColor = sanitizer.bypassSecurityTrustStyle('white');
|
||||
trustedGreyColor = sanitizer.bypassSecurityTrustStyle('grey');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ɵRenderFlags, ɵrenderComponent as renderComponent, ɵɵadvance, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdefineComponent, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵstyleProp, ɵɵstyling, ɵɵstylingApply, ɵɵtext, ɵɵtextInterpolate1} from '@angular/core';
|
||||
import {ɵRenderFlags, ɵrenderComponent as renderComponent, ɵɵadvance, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵdefineComponent, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵstyleProp, ɵɵtext, ɵɵtextInterpolate1} from '@angular/core';
|
||||
|
||||
import {bindAction, profile} from '../../util';
|
||||
import {createDom, destroyDom, detectChanges} from '../render3/tree';
|
||||
@@ -39,7 +39,6 @@ export function TreeTpl(rf: ɵRenderFlags, ctx: TreeNode) {
|
||||
ɵɵelementStart(0, 'tree');
|
||||
{
|
||||
ɵɵelementStart(1, 'span');
|
||||
ɵɵstyling();
|
||||
{ ɵɵtext(2); }
|
||||
ɵɵelementEnd();
|
||||
ɵɵcontainer(3);
|
||||
@@ -50,7 +49,6 @@ export function TreeTpl(rf: ɵRenderFlags, ctx: TreeNode) {
|
||||
if (rf & ɵRenderFlags.Update) {
|
||||
ɵɵadvance(1);
|
||||
ɵɵstyleProp('background-color', ctx.depth % 2 ? '' : 'grey');
|
||||
ɵɵstylingApply();
|
||||
ɵɵadvance(1);
|
||||
ɵɵtextInterpolate1(' ', ctx.value, ' ');
|
||||
ɵɵcontainerRefreshStart(3);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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 {Directive, DoCheck, Input, ɵRenderFlags, ɵɵallocHostVars, ɵɵclassMap, ɵɵdefineDirective, ɵɵstyling, ɵɵstylingApply} from '@angular/core';
|
||||
import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵallocHostVars, ɵɵclassMap, ɵɵdefineDirective} from '@angular/core';
|
||||
|
||||
import {NgClassImpl, NgClassImplProvider} from './ng_class_impl';
|
||||
|
||||
@@ -35,11 +35,9 @@ export const ngClassDirectiveDef__POST_R3__ = ɵɵdefineDirective({
|
||||
hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) {
|
||||
if (rf & ɵRenderFlags.Create) {
|
||||
ɵɵallocHostVars(1);
|
||||
ɵɵstyling();
|
||||
}
|
||||
if (rf & ɵRenderFlags.Update) {
|
||||
ɵɵclassMap(ctx.getValue());
|
||||
ɵɵstylingApply();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* 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 {Directive, DoCheck, Input, ɵRenderFlags, ɵɵdefineDirective, ɵɵstyleMap, ɵɵstyling, ɵɵstylingApply} from '@angular/core';
|
||||
import {Directive, DoCheck, Input, ɵRenderFlags, ɵɵdefineDirective, ɵɵstyleMap} from '@angular/core';
|
||||
|
||||
import {NgStyleImpl, NgStyleImplProvider} from './ng_style_impl';
|
||||
|
||||
@@ -34,12 +34,8 @@ export const ngStyleDirectiveDef__POST_R3__ = ɵɵdefineDirective({
|
||||
type: function() {} as any,
|
||||
selectors: null as any,
|
||||
hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) {
|
||||
if (rf & ɵRenderFlags.Create) {
|
||||
ɵɵstyling();
|
||||
}
|
||||
if (rf & ɵRenderFlags.Update) {
|
||||
ɵɵstyleMap(ctx.getValue());
|
||||
ɵɵstylingApply();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -483,14 +483,11 @@ describe('compiler compliance', () => {
|
||||
vars: 2,
|
||||
template: function MyComponent_Template(rf,ctx){
|
||||
if (rf & 1) {
|
||||
$r3$.ɵɵelementStart(0, "div");
|
||||
$r3$.ɵɵstyling();
|
||||
$r3$.ɵɵelementEnd();
|
||||
$r3$.ɵɵelement(0, "div");
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵstyleProp("background-color", ctx.color);
|
||||
$r3$.ɵɵclassProp("error", ctx.error);
|
||||
$r3$.ɵɵstylingApply();
|
||||
}
|
||||
},
|
||||
encapsulation: 2
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1867,13 +1867,11 @@ runInEachFileSystem(os => {
|
||||
i0.ɵɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onClick($event); });
|
||||
i0.ɵɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onBodyClick($event); }, false, i0.ɵɵresolveBody);
|
||||
i0.ɵɵlistener("change", function FooCmp_change_HostBindingHandler($event) { return ctx.onChange(ctx.arg1, ctx.arg2, ctx.arg3); });
|
||||
i0.ɵɵstyling();
|
||||
}
|
||||
if (rf & 2) {
|
||||
i0.ɵɵhostProperty("prop", ctx.bar);
|
||||
i0.ɵɵattribute("hello", ctx.foo);
|
||||
i0.ɵɵclassProp("someclass", ctx.someClass);
|
||||
i0.ɵɵstylingApply();
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -69,8 +69,6 @@ export class Identifiers {
|
||||
|
||||
static elementContainer: o.ExternalReference = {name: 'ɵɵelementContainer', moduleName: CORE};
|
||||
|
||||
static styling: o.ExternalReference = {name: 'ɵɵstyling', moduleName: CORE};
|
||||
|
||||
static styleMap: o.ExternalReference = {name: 'ɵɵstyleMap', moduleName: CORE};
|
||||
|
||||
static classMap: o.ExternalReference = {name: 'ɵɵclassMap', moduleName: CORE};
|
||||
@@ -115,8 +113,6 @@ export class Identifiers {
|
||||
static stylePropInterpolateV:
|
||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolateV', moduleName: CORE};
|
||||
|
||||
static stylingApply: o.ExternalReference = {name: 'ɵɵstylingApply', moduleName: CORE};
|
||||
|
||||
static styleSanitizer: o.ExternalReference = {name: 'ɵɵstyleSanitizer', moduleName: CORE};
|
||||
|
||||
static elementHostAttrs: o.ExternalReference = {name: 'ɵɵelementHostAttrs', moduleName: CORE};
|
||||
|
||||
@@ -713,16 +713,6 @@ function createHostBindingsFunction(
|
||||
}
|
||||
|
||||
if (styleBuilder.hasBindings) {
|
||||
// singular style/class bindings (things like `[style.prop]` and `[class.name]`)
|
||||
// MUST be registered on a given element within the component/directive
|
||||
// templateFn/hostBindingsFn functions. The instruction below will figure out
|
||||
// what all the bindings are and then generate the statements required to register
|
||||
// those bindings to the element via `styling`.
|
||||
const stylingInstruction = styleBuilder.buildStylingInstruction(null, constantPool);
|
||||
if (stylingInstruction) {
|
||||
createStatements.push(createStylingStmt(stylingInstruction, bindingContext, bindingFn));
|
||||
}
|
||||
|
||||
// finally each binding that was registered in the statement above will need to be added to
|
||||
// the update block of a component/directive templateFn/hostBindingsFn so that the bindings
|
||||
// are evaluated and updated for the element.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
import {ConstantPool} from '../../constant_pool';
|
||||
import {AttributeMarker} from '../../core';
|
||||
import {AST, BindingType, Interpolation} from '../../expression_parser/ast';
|
||||
import {AST, ASTWithSource, BindingPipe, BindingType, Interpolation} from '../../expression_parser/ast';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {ParseSourceSpan} from '../../parse_util';
|
||||
import {isEmptyExpression} from '../../template_parser/template_parser';
|
||||
@@ -68,7 +68,6 @@ interface BoundStylingEntry {
|
||||
* classMap(...)
|
||||
* styleProp(...)
|
||||
* classProp(...)
|
||||
* stylingApply(...)
|
||||
* }
|
||||
*
|
||||
* The creation/update methods within the builder class produce these instructions.
|
||||
@@ -81,6 +80,7 @@ export class StylingBuilder {
|
||||
* (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
|
||||
*/
|
||||
public hasBindings = false;
|
||||
public hasBindingsWithPipes = false;
|
||||
|
||||
/** the input for [class] (if it exists) */
|
||||
private _classMapInput: BoundStylingEntry|null = null;
|
||||
@@ -187,6 +187,7 @@ export class StylingBuilder {
|
||||
}
|
||||
this._lastStylingInput = entry;
|
||||
this._firstStylingInput = this._firstStylingInput || entry;
|
||||
this._checkForPipes(value);
|
||||
this.hasBindings = true;
|
||||
return entry;
|
||||
}
|
||||
@@ -207,10 +208,17 @@ export class StylingBuilder {
|
||||
}
|
||||
this._lastStylingInput = entry;
|
||||
this._firstStylingInput = this._firstStylingInput || entry;
|
||||
this._checkForPipes(value);
|
||||
this.hasBindings = true;
|
||||
return entry;
|
||||
}
|
||||
|
||||
private _checkForPipes(value: AST) {
|
||||
if ((value instanceof ASTWithSource) && (value.ast instanceof BindingPipe)) {
|
||||
this.hasBindingsWithPipes = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the element's static style string value to the builder.
|
||||
*
|
||||
@@ -284,25 +292,6 @@ export class StylingBuilder {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an instruction with all the expressions and parameters for `styling`.
|
||||
*
|
||||
* The instruction generation code below is used for producing the AOT statement code which is
|
||||
* responsible for registering style/class bindings to an element.
|
||||
*/
|
||||
buildStylingInstruction(sourceSpan: ParseSourceSpan|null, constantPool: ConstantPool):
|
||||
StylingInstruction|null {
|
||||
if (this.hasBindings) {
|
||||
return {
|
||||
sourceSpan,
|
||||
allocateBindingSlots: 0,
|
||||
reference: R3.styling,
|
||||
params: () => [],
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an instruction with all the expressions and parameters for `classMap`.
|
||||
*
|
||||
@@ -422,15 +411,6 @@ export class StylingBuilder {
|
||||
return [];
|
||||
}
|
||||
|
||||
private _buildApplyFn(): StylingInstruction {
|
||||
return {
|
||||
sourceSpan: this._lastStylingInput ? this._lastStylingInput.sourceSpan : null,
|
||||
reference: R3.stylingApply,
|
||||
allocateBindingSlots: 0,
|
||||
params: () => { return []; }
|
||||
};
|
||||
}
|
||||
|
||||
private _buildSanitizerFn(): StylingInstruction {
|
||||
return {
|
||||
sourceSpan: this._firstStylingInput ? this._firstStylingInput.sourceSpan : null,
|
||||
@@ -460,7 +440,6 @@ export class StylingBuilder {
|
||||
}
|
||||
instructions.push(...this._buildStyleInputs(valueConverter));
|
||||
instructions.push(...this._buildClassInputs(valueConverter));
|
||||
instructions.push(this._buildApplyFn());
|
||||
}
|
||||
return instructions;
|
||||
}
|
||||
|
||||
@@ -623,11 +623,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
const hasChildren = (!isI18nRootElement && this.i18n) ? !hasTextChildrenOnly(element.children) :
|
||||
element.children.length > 0;
|
||||
|
||||
const createSelfClosingInstruction = !stylingBuilder.hasBindings &&
|
||||
const createSelfClosingInstruction = !stylingBuilder.hasBindingsWithPipes &&
|
||||
element.outputs.length === 0 && i18nAttrs.length === 0 && !hasChildren;
|
||||
|
||||
const createSelfClosingI18nInstruction = !createSelfClosingInstruction &&
|
||||
!stylingBuilder.hasBindings && hasTextChildrenOnly(element.children);
|
||||
const createSelfClosingI18nInstruction =
|
||||
!createSelfClosingInstruction && hasTextChildrenOnly(element.children);
|
||||
|
||||
if (createSelfClosingInstruction) {
|
||||
this.creationInstruction(
|
||||
@@ -681,16 +680,6 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||
}
|
||||
}
|
||||
|
||||
// The style bindings code is placed into two distinct blocks within the template function AOT
|
||||
// code: creation and update. The creation code contains the `styling` instructions
|
||||
// which will apply the collected binding values to the element. `styling` is
|
||||
// designed to run inside of `elementStart` and `elementEnd`. The update instructions
|
||||
// (things like `styleProp`, `classProp`, etc..) are applied later on in this
|
||||
// file
|
||||
this.processStylingInstruction(
|
||||
elementIndex,
|
||||
stylingBuilder.buildStylingInstruction(element.sourceSpan, this.constantPool), true);
|
||||
|
||||
// Generate Listeners (outputs)
|
||||
element.outputs.forEach((outputAst: t.BoundEvent) => {
|
||||
this.creationInstruction(
|
||||
|
||||
@@ -120,7 +120,6 @@ export {
|
||||
ɵɵelementContainerStart,
|
||||
ɵɵelementContainerEnd,
|
||||
ɵɵelementContainer,
|
||||
ɵɵstyling,
|
||||
ɵɵstyleMap,
|
||||
ɵɵstyleSanitizer,
|
||||
ɵɵclassMap,
|
||||
@@ -143,7 +142,6 @@ export {
|
||||
ɵɵstylePropInterpolate7,
|
||||
ɵɵstylePropInterpolate8,
|
||||
ɵɵstylePropInterpolateV,
|
||||
ɵɵstylingApply,
|
||||
ɵɵclassProp,
|
||||
ɵɵelementHostAttrs,
|
||||
|
||||
|
||||
@@ -12,10 +12,9 @@ import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../render3/interfaces
|
||||
import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node';
|
||||
import {isComponentHost, isLContainer} from '../render3/interfaces/type_checks';
|
||||
import {LView, PARENT, TData, TVIEW, T_HOST} from '../render3/interfaces/view';
|
||||
import {TStylingContext} from '../render3/styling_next/interfaces';
|
||||
import {stylingMapToStringMap} from '../render3/styling_next/map_based_bindings';
|
||||
import {StylingMapArray, TStylingContext} from '../render3/styling_next/interfaces';
|
||||
import {NodeStylingDebug} from '../render3/styling_next/styling_debug';
|
||||
import {isStylingContext} from '../render3/styling_next/util';
|
||||
import {isStylingContext, stylingMapToStringMap} from '../render3/styling_next/util';
|
||||
import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, isBrowserEvents, loadLContext} from '../render3/util/discovery_utils';
|
||||
import {INTERPOLATION_DELIMITER, renderStringify} from '../render3/util/misc_utils';
|
||||
import {findComponentView} from '../render3/util/view_traversal_utils';
|
||||
@@ -431,11 +430,11 @@ function _getStylingDebugInfo(element: any, isClassBased: boolean) {
|
||||
if (isClassBased) {
|
||||
return isStylingContext(tNode.classes) ?
|
||||
new NodeStylingDebug(tNode.classes as TStylingContext, lView, true).values :
|
||||
stylingMapToStringMap(tNode.classes);
|
||||
stylingMapToStringMap(tNode.classes as StylingMapArray | null);
|
||||
} else {
|
||||
return isStylingContext(tNode.styles) ?
|
||||
new NodeStylingDebug(tNode.styles as TStylingContext, lView, false).values :
|
||||
stylingMapToStringMap(tNode.styles);
|
||||
stylingMapToStringMap(tNode.styles as StylingMapArray | null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ import {TElementNode, TNode, TNodeType} from './interfaces/node';
|
||||
import {PlayerHandler} from './interfaces/player';
|
||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||
import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||
import {getPreviousOrParentTNode, resetComponentState, selectView, setActiveHostElement} from './state';
|
||||
import {getPreviousOrParentTNode, incrementActiveDirectiveId, resetComponentState, selectView, setActiveHostElement} from './state';
|
||||
import {publishDefaultGlobalUtils} from './util/global_utils';
|
||||
import {defaultScheduler, stringifyForError} from './util/misc_utils';
|
||||
import {getRootContext} from './util/view_traversal_utils';
|
||||
@@ -217,6 +217,7 @@ export function createRootComponent<T>(
|
||||
if (tView.firstTemplatePass && componentDef.hostBindings) {
|
||||
const elementIndex = rootTNode.index - HEADER_OFFSET;
|
||||
setActiveHostElement(elementIndex);
|
||||
incrementActiveDirectiveId();
|
||||
|
||||
const expando = tView.expandoInstructions !;
|
||||
invokeHostBindingsInCreationMode(
|
||||
|
||||
@@ -11,7 +11,6 @@ import {fillProperties} from '../../util/property';
|
||||
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
|
||||
import {ComponentDef, ContentQueriesFunction, DirectiveDef, DirectiveDefFeature, HostBindingsFunction, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
|
||||
import {isComponentDef} from '../interfaces/type_checks';
|
||||
import {adjustActiveDirectiveSuperClassDepthPosition} from '../state';
|
||||
|
||||
import {ɵɵNgOnChangesFeature} from './ng_onchanges_feature';
|
||||
|
||||
@@ -177,24 +176,8 @@ function inheritHostBindings(
|
||||
// to ensure we don't inherit it twice.
|
||||
if (superHostBindings !== prevHostBindings) {
|
||||
if (prevHostBindings) {
|
||||
// because inheritance is unknown during compile time, the runtime code
|
||||
// needs to be informed of the super-class depth so that instruction code
|
||||
// can distinguish one host bindings function from another. The reason why
|
||||
// relying on the directive uniqueId exclusively is not enough is because the
|
||||
// uniqueId value and the directive instance stay the same between hostBindings
|
||||
// calls throughout the directive inheritance chain. This means that without
|
||||
// a super-class depth value, there is no way to know whether a parent or
|
||||
// sub-class host bindings function is currently being executed.
|
||||
definition.hostBindings = (rf: RenderFlags, ctx: any, elementIndex: number) => {
|
||||
// The reason why we increment first and then decrement is so that parent
|
||||
// hostBindings calls have a higher id value compared to sub-class hostBindings
|
||||
// calls (this way the leaf directive is always at a super-class depth of 0).
|
||||
adjustActiveDirectiveSuperClassDepthPosition(1);
|
||||
try {
|
||||
superHostBindings(rf, ctx, elementIndex);
|
||||
} finally {
|
||||
adjustActiveDirectiveSuperClassDepthPosition(-1);
|
||||
}
|
||||
superHostBindings(rf, ctx, elementIndex);
|
||||
prevHostBindings(rf, ctx, elementIndex);
|
||||
};
|
||||
} else {
|
||||
|
||||
@@ -114,8 +114,6 @@ export {
|
||||
ɵɵstylePropInterpolateV,
|
||||
|
||||
ɵɵstyleSanitizer,
|
||||
ɵɵstyling,
|
||||
ɵɵstylingApply,
|
||||
ɵɵtemplate,
|
||||
|
||||
ɵɵtext,
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
import {assertDataInRange, assertGreaterThan} from '../../util/assert';
|
||||
import {executeCheckHooks, executeInitAndCheckHooks} from '../hooks';
|
||||
import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, TVIEW} from '../interfaces/view';
|
||||
import {getCheckNoChangesMode, getLView, getSelectedIndex, setSelectedIndex} from '../state';
|
||||
import {ActiveElementFlags, executeElementExitFn, getCheckNoChangesMode, getLView, getSelectedIndex, hasActiveElementFlag, setSelectedIndex} from '../state';
|
||||
import {resetStylingState} from '../styling_next/state';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Advances to an element for later binding instructions.
|
||||
@@ -51,6 +54,10 @@ export function selectIndexInternal(lView: LView, index: number, checkNoChangesM
|
||||
ngDevMode && assertGreaterThan(index, -1, 'Invalid index');
|
||||
ngDevMode && assertDataInRange(lView, index + HEADER_OFFSET);
|
||||
|
||||
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
|
||||
executeElementExitFn();
|
||||
}
|
||||
|
||||
// Flush the initial hooks for elements in the view that have been added up to this point.
|
||||
// PERF WARNING: do NOT extract this to a separate function without running benchmarks
|
||||
if (!checkNoChangesMode) {
|
||||
@@ -69,6 +76,10 @@ export function selectIndexInternal(lView: LView, index: number, checkNoChangesM
|
||||
}
|
||||
}
|
||||
|
||||
if (hasActiveElementFlag(ActiveElementFlags.ResetStylesOnExit)) {
|
||||
resetStylingState();
|
||||
}
|
||||
|
||||
// We must set the selected index *after* running the hooks, because hooks may have side-effects
|
||||
// that cause other template functions to run, thus updating the selected index, which is global
|
||||
// state. If we run `setSelectedIndex` *before* we run the hooks, in some cases the selected index
|
||||
|
||||
@@ -138,11 +138,11 @@ export function ɵɵelementEnd(): void {
|
||||
}
|
||||
}
|
||||
|
||||
if (hasClassInput(tNode) && tNode.classes) {
|
||||
if (hasClassInput(tNode)) {
|
||||
setDirectiveStylingInput(tNode.classes, lView, tNode.inputs !['class']);
|
||||
}
|
||||
|
||||
if (hasStyleInput(tNode) && tNode.styles) {
|
||||
if (hasStyleInput(tNode)) {
|
||||
setDirectiveStylingInput(tNode.styles, lView, tNode.inputs !['style']);
|
||||
}
|
||||
}
|
||||
@@ -236,11 +236,12 @@ export function ɵɵelementHostAttrs(attrs: TAttributes) {
|
||||
}
|
||||
|
||||
function setDirectiveStylingInput(
|
||||
context: TStylingContext | StylingMapArray, lView: LView, stylingInputs: (string | number)[]) {
|
||||
context: TStylingContext | StylingMapArray | null, lView: LView,
|
||||
stylingInputs: (string | number)[]) {
|
||||
// older versions of Angular treat the input as `null` in the
|
||||
// event that the value does not exist at all. For this reason
|
||||
// we can't have a styling value be an empty string.
|
||||
const value = getInitialStylingValue(context) || null;
|
||||
const value = (context && getInitialStylingValue(context)) || null;
|
||||
|
||||
// Ivy does an extra `[class]` write with a falsy value since the value
|
||||
// is applied during creation mode. This is a deviation from VE and should
|
||||
|
||||
@@ -29,8 +29,9 @@ import {isComponentDef, isComponentHost, isContentQueryHost, isLContainer, isRoo
|
||||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from '../interfaces/view';
|
||||
import {assertNodeOfPossibleTypes} from '../node_assert';
|
||||
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
|
||||
import {getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, getSelectedIndex, incrementActiveDirectiveId, namespaceHTMLInternal, selectView, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state';
|
||||
import {ActiveElementFlags, executeElementExitFn, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getPreviousOrParentTNode, getSelectedIndex, hasActiveElementFlag, incrementActiveDirectiveId, namespaceHTMLInternal, selectView, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setPreviousOrParentTNode, setSelectedIndex} from '../state';
|
||||
import {renderStylingMap} from '../styling_next/bindings';
|
||||
import {resetStylingState} from '../styling_next/state';
|
||||
import {NO_CHANGE} from '../tokens';
|
||||
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../util/attrs_utils';
|
||||
import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils';
|
||||
@@ -86,16 +87,18 @@ export function setHostBindings(tView: TView, viewData: LView): void {
|
||||
} else {
|
||||
// If it's not a number, it's a host binding function that needs to be executed.
|
||||
if (instruction !== null) {
|
||||
viewData[BINDING_INDEX] = bindingRootIndex;
|
||||
const hostCtx = unwrapRNode(viewData[currentDirectiveIndex]);
|
||||
instruction(RenderFlags.Update, hostCtx, currentElementIndex);
|
||||
|
||||
// Each directive gets a uniqueId value that is the same for both
|
||||
// create and update calls when the hostBindings function is called. The
|
||||
// directive uniqueId is not set anywhere--it is just incremented between
|
||||
// each hostBindings call and is useful for helping instruction code
|
||||
// uniquely determine which directive is currently active when executed.
|
||||
// It is important that this be called first before the actual instructions
|
||||
// are run because this way the first directive ID value is not zero.
|
||||
incrementActiveDirectiveId();
|
||||
|
||||
viewData[BINDING_INDEX] = bindingRootIndex;
|
||||
const hostCtx = unwrapRNode(viewData[currentDirectiveIndex]);
|
||||
instruction(RenderFlags.Update, hostCtx, currentElementIndex);
|
||||
}
|
||||
currentDirectiveIndex++;
|
||||
}
|
||||
@@ -503,6 +506,12 @@ function executeTemplate<T>(
|
||||
}
|
||||
templateFn(rf, context);
|
||||
} finally {
|
||||
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
|
||||
executeElementExitFn();
|
||||
}
|
||||
if (hasActiveElementFlag(ActiveElementFlags.ResetStylesOnExit)) {
|
||||
resetStylingState();
|
||||
}
|
||||
setSelectedIndex(prevSelectedIndex);
|
||||
}
|
||||
}
|
||||
@@ -1086,14 +1095,10 @@ function invokeDirectivesHostBindings(tView: TView, viewData: LView, tNode: TNod
|
||||
const def = tView.data[i] as DirectiveDef<any>;
|
||||
const directive = viewData[i];
|
||||
if (def.hostBindings) {
|
||||
invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstTemplatePass);
|
||||
|
||||
// Each directive gets a uniqueId value that is the same for both
|
||||
// create and update calls when the hostBindings function is called. The
|
||||
// directive uniqueId is not set anywhere--it is just incremented between
|
||||
// each hostBindings call and is useful for helping instruction code
|
||||
// uniquely determine which directive is currently active when executed.
|
||||
// It is important that this be called first before the actual instructions
|
||||
// are run because this way the first directive ID value is not zero.
|
||||
incrementActiveDirectiveId();
|
||||
invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstTemplatePass);
|
||||
} else if (firstTemplatePass) {
|
||||
expando.push(null);
|
||||
}
|
||||
|
||||
@@ -116,7 +116,6 @@ export const angularCoreEnv: {[name: string]: Function} =
|
||||
'ɵɵclassMapInterpolate7': r3.ɵɵclassMapInterpolate7,
|
||||
'ɵɵclassMapInterpolate8': r3.ɵɵclassMapInterpolate8,
|
||||
'ɵɵclassMapInterpolateV': r3.ɵɵclassMapInterpolateV,
|
||||
'ɵɵstyling': r3.ɵɵstyling,
|
||||
'ɵɵstyleMap': r3.ɵɵstyleMap,
|
||||
'ɵɵstyleProp': r3.ɵɵstyleProp,
|
||||
'ɵɵstylePropInterpolate1': r3.ɵɵstylePropInterpolate1,
|
||||
@@ -129,7 +128,6 @@ export const angularCoreEnv: {[name: string]: Function} =
|
||||
'ɵɵstylePropInterpolate8': r3.ɵɵstylePropInterpolate8,
|
||||
'ɵɵstylePropInterpolateV': r3.ɵɵstylePropInterpolateV,
|
||||
'ɵɵstyleSanitizer': r3.ɵɵstyleSanitizer,
|
||||
'ɵɵstylingApply': r3.ɵɵstylingApply,
|
||||
'ɵɵclassProp': r3.ɵɵclassProp,
|
||||
'ɵɵselect': r3.ɵɵselect,
|
||||
'ɵɵadvance': r3.ɵɵadvance,
|
||||
|
||||
@@ -13,7 +13,7 @@ import {assertLViewOrUndefined} from './assert';
|
||||
import {ComponentDef, DirectiveDef} from './interfaces/definition';
|
||||
import {TElementNode, TNode, TViewNode} from './interfaces/node';
|
||||
import {CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState, TVIEW} from './interfaces/view';
|
||||
import {resetAllStylingState, resetStylingState} from './styling_next/state';
|
||||
import {resetStylingState} from './styling_next/state';
|
||||
|
||||
|
||||
/**
|
||||
@@ -129,31 +129,38 @@ export function getLView(): LView {
|
||||
* The reason why this value is `1` instead of `0` is because the `0`
|
||||
* value is reserved for the template.
|
||||
*/
|
||||
const MIN_DIRECTIVE_ID = 1;
|
||||
|
||||
let activeDirectiveId = MIN_DIRECTIVE_ID;
|
||||
let activeDirectiveId = 0;
|
||||
|
||||
/**
|
||||
* Position depth (with respect from leaf to root) in a directive sub-class inheritance chain.
|
||||
* Flags used for an active element during change detection.
|
||||
*
|
||||
* These flags are used within other instructions to inform cleanup or
|
||||
* exit operations to run when an element is being processed.
|
||||
*
|
||||
* Note that these flags are reset each time an element changes (whether it
|
||||
* happens when `advance()` is run or when change detection exits out of a template
|
||||
* function or when all host bindings are processed for an element).
|
||||
*/
|
||||
let activeDirectiveSuperClassDepthPosition = 0;
|
||||
export const enum ActiveElementFlags {
|
||||
Initial = 0b00,
|
||||
RunExitFn = 0b01,
|
||||
ResetStylesOnExit = 0b10,
|
||||
Size = 2,
|
||||
}
|
||||
|
||||
/**
|
||||
* Total count of how many directives are a part of an inheritance chain.
|
||||
*
|
||||
* When directives are sub-classed (extended) from one to another, Angular
|
||||
* needs to keep track of exactly how many were encountered so it can accurately
|
||||
* generate the next directive id (once the next directive id is visited).
|
||||
* Normally the next directive id just a single incremented value from the
|
||||
* previous one, however, if the previous directive is a part of an inheritance
|
||||
* chain (a series of sub-classed directives) then the incremented value must
|
||||
* also take into account the total amount of sub-classed values.
|
||||
*
|
||||
* Note that this value resets back to zero once the next directive is
|
||||
* visited (when `incrementActiveDirectiveId` or `setActiveHostElement`
|
||||
* is called).
|
||||
* Determines whether or not a flag is currently set for the active element.
|
||||
*/
|
||||
let activeDirectiveSuperClassHeight = 0;
|
||||
export function hasActiveElementFlag(flag: ActiveElementFlags) {
|
||||
return (_selectedIndex & flag) === flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a flag is for the active element.
|
||||
*/
|
||||
export function setActiveElementFlag(flag: ActiveElementFlags) {
|
||||
_selectedIndex |= flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the active directive host element and resets the directive id value
|
||||
@@ -163,14 +170,44 @@ let activeDirectiveSuperClassHeight = 0;
|
||||
* the directive/component instance lives
|
||||
*/
|
||||
export function setActiveHostElement(elementIndex: number | null = null) {
|
||||
if (_selectedIndex !== elementIndex) {
|
||||
if (getSelectedIndex() !== elementIndex) {
|
||||
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
|
||||
executeElementExitFn();
|
||||
}
|
||||
if (hasActiveElementFlag(ActiveElementFlags.ResetStylesOnExit)) {
|
||||
resetStylingState();
|
||||
}
|
||||
setSelectedIndex(elementIndex === null ? -1 : elementIndex);
|
||||
activeDirectiveId = elementIndex === null ? 0 : MIN_DIRECTIVE_ID;
|
||||
activeDirectiveSuperClassDepthPosition = 0;
|
||||
activeDirectiveSuperClassHeight = 0;
|
||||
activeDirectiveId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let _elementExitFn: Function|null = null;
|
||||
export function executeElementExitFn() {
|
||||
_elementExitFn !();
|
||||
// TODO (matsko|misko): remove this unassignment once the state management of
|
||||
// global variables are better managed.
|
||||
_selectedIndex &= ~ActiveElementFlags.RunExitFn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a function to be run once the element is "exited" in CD.
|
||||
*
|
||||
* Change detection will focus on an element either when the `advance()`
|
||||
* instruction is called or when the template or host bindings instruction
|
||||
* code is invoked. The element is then "exited" when the next element is
|
||||
* selected or when change detection for the template or host bindings is
|
||||
* complete. When this occurs (the element change operation) then an exit
|
||||
* function will be invoked if it has been set. This function can be used
|
||||
* to assign that exit function.
|
||||
*
|
||||
* @param fn
|
||||
*/
|
||||
export function setElementExitFn(fn: Function): void {
|
||||
setActiveElementFlag(ActiveElementFlags.RunExitFn);
|
||||
_elementExitFn = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current id value of the current directive.
|
||||
*
|
||||
@@ -211,71 +248,12 @@ export function getActiveDirectiveId() {
|
||||
* different set of directives).
|
||||
*/
|
||||
export function incrementActiveDirectiveId() {
|
||||
activeDirectiveId += 1 + activeDirectiveSuperClassHeight;
|
||||
|
||||
// because we are dealing with a new directive this
|
||||
// means we have exited out of the inheritance chain
|
||||
activeDirectiveSuperClassDepthPosition = 0;
|
||||
activeDirectiveSuperClassHeight = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current super class (reverse inheritance) position depth for a directive.
|
||||
*
|
||||
* For example we have two directives: Child and Other (but Child is a sub-class of Parent)
|
||||
* <div child-dir other-dir></div>
|
||||
*
|
||||
* // increment
|
||||
* parentInstance->hostBindings() (depth = 1)
|
||||
* // decrement
|
||||
* childInstance->hostBindings() (depth = 0)
|
||||
* otherInstance->hostBindings() (depth = 0 b/c it's a different directive)
|
||||
*
|
||||
* Note that this is only active when `hostBinding` functions are being processed.
|
||||
*/
|
||||
export function adjustActiveDirectiveSuperClassDepthPosition(delta: number) {
|
||||
activeDirectiveSuperClassDepthPosition += delta;
|
||||
|
||||
// we keep track of the height value so that when the next directive is visited
|
||||
// then Angular knows to generate a new directive id value which has taken into
|
||||
// account how many sub-class directives were a part of the previous directive.
|
||||
activeDirectiveSuperClassHeight =
|
||||
Math.max(activeDirectiveSuperClassHeight, activeDirectiveSuperClassDepthPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns he current depth of the super/sub class inheritance chain.
|
||||
*
|
||||
* This will return how many inherited directive/component classes
|
||||
* exist in the current chain.
|
||||
*
|
||||
* ```typescript
|
||||
* @Directive({ selector: '[super-dir]' })
|
||||
* class SuperDir {}
|
||||
*
|
||||
* @Directive({ selector: '[sub-dir]' })
|
||||
* class SubDir extends SuperDir {}
|
||||
*
|
||||
* // if `<div sub-dir>` is used then the super class height is `1`
|
||||
* // if `<div super-dir>` is used then the super class height is `0`
|
||||
* ```
|
||||
*/
|
||||
export function getActiveDirectiveSuperClassHeight() {
|
||||
return activeDirectiveSuperClassHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current super class (reverse inheritance) depth for a directive.
|
||||
*
|
||||
* This is designed to help instruction code distinguish different hostBindings
|
||||
* calls from each other when a directive has extended from another directive.
|
||||
* Normally using the directive id value is enough, but with the case
|
||||
* of parent/sub-class directive inheritance more information is required.
|
||||
*
|
||||
* Note that this is only active when `hostBinding` functions are being processed.
|
||||
*/
|
||||
export function getActiveDirectiveSuperClassDepth() {
|
||||
return activeDirectiveSuperClassDepthPosition;
|
||||
// Each directive gets a uniqueId value that is the same for both
|
||||
// create and update calls when the hostBindings function is called. The
|
||||
// directive uniqueId is not set anywhere--it is just incremented between
|
||||
// each hostBindings call and is useful for helping instruction code
|
||||
// uniquely determine which directive is currently active when executed.
|
||||
activeDirectiveId += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,10 +425,10 @@ export function resetComponentState() {
|
||||
elementDepthCount = 0;
|
||||
bindingsEnabled = true;
|
||||
setCurrentStyleSanitizer(null);
|
||||
resetAllStylingState();
|
||||
}
|
||||
|
||||
let _selectedIndex = -1;
|
||||
/* tslint:disable */
|
||||
let _selectedIndex = -1 << ActiveElementFlags.Size;
|
||||
|
||||
/**
|
||||
* Gets the most recent index passed to {@link select}
|
||||
@@ -459,7 +437,7 @@ let _selectedIndex = -1;
|
||||
* current `LView` to act on.
|
||||
*/
|
||||
export function getSelectedIndex() {
|
||||
return _selectedIndex;
|
||||
return _selectedIndex >> ActiveElementFlags.Size;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -467,13 +445,12 @@ export function getSelectedIndex() {
|
||||
*
|
||||
* Used with {@link property} instruction (and more in the future) to identify the index in the
|
||||
* current `LView` to act on.
|
||||
*
|
||||
* (Note that if an "exit function" was set earlier (via `setElementExitFn()`) then that will be
|
||||
* run if and when the provided `index` value is different from the current selected index value.)
|
||||
*/
|
||||
export function setSelectedIndex(index: number) {
|
||||
_selectedIndex = index;
|
||||
|
||||
// we have now jumped to another element
|
||||
// therefore the state is stale
|
||||
resetStylingState();
|
||||
_selectedIndex = index << ActiveElementFlags.Size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -5,12 +5,12 @@
|
||||
* 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 {RElement} from '../interfaces/renderer';
|
||||
import {TEMPLATE_DIRECTIVE_INDEX} from './util';
|
||||
|
||||
/**
|
||||
* --------
|
||||
*
|
||||
* // TODO(matsko): add updateMask info
|
||||
*
|
||||
* This file contains all state-based logic for styling in Angular.
|
||||
*
|
||||
* Styling in Angular is evaluated with a series of styling-specific
|
||||
@@ -23,79 +23,92 @@
|
||||
* exited in change detection (once all the instructions are run for
|
||||
* that element).
|
||||
*
|
||||
* There are, however, situations where the state-based values
|
||||
* need to be stored and used at a later point. This ONLY occurs when
|
||||
* there are template-level as well as host-binding-level styling
|
||||
* instructions on the same element. The example below shows exactly
|
||||
* what could be:
|
||||
*
|
||||
* ```html
|
||||
* <!-- two sources of styling: the template and the directive -->
|
||||
* <div [style.width]="width" dir-that-sets-height></div>
|
||||
* ```
|
||||
*
|
||||
* If and when this situation occurs, the current styling state is
|
||||
* stored in a storage map value and then later accessed once the
|
||||
* host bindings are evaluated. Once styling for the current element
|
||||
* is over then the map entry will be cleared.
|
||||
*
|
||||
* To learn more about the algorithm see `TStylingContext`.
|
||||
*
|
||||
* --------
|
||||
*/
|
||||
|
||||
let _stylingState: StylingState|null = null;
|
||||
const _stateStorage = new Map<any, StylingState>();
|
||||
|
||||
// this value is not used outside this file and is only here
|
||||
// as a caching check for when the element changes.
|
||||
let _stylingElement: any = null;
|
||||
|
||||
/**
|
||||
* Used as a state reference for update values between style/class binding instructions.
|
||||
*
|
||||
* In addition to storing the element and bit-mask related values, the state also
|
||||
* stores the `sourceIndex` value. The `sourceIndex` value is an incremented value
|
||||
* that identifies what "source" (i.e. the template, a specific directive by index or
|
||||
* component) is currently applying its styling bindings to the element.
|
||||
*/
|
||||
export interface StylingState {
|
||||
/** The element that is currently being processed */
|
||||
element: RElement|null;
|
||||
|
||||
/** The directive index that is currently active (`0` === template) */
|
||||
directiveIndex: number;
|
||||
|
||||
/** The source (column) index that is currently active (`0` === template) */
|
||||
sourceIndex: number;
|
||||
|
||||
/** The classes update bit mask value that is processed during each class binding */
|
||||
classesBitMask: number;
|
||||
|
||||
/** The classes update bit index value that is processed during each class binding */
|
||||
classesIndex: number;
|
||||
|
||||
/** The styles update bit mask value that is processed during each style binding */
|
||||
stylesBitMask: number;
|
||||
|
||||
/** The styles update bit index value that is processed during each style binding */
|
||||
stylesIndex: number;
|
||||
}
|
||||
|
||||
export const STYLING_INDEX_START_VALUE = 1;
|
||||
export const BIT_MASK_START_VALUE = 0;
|
||||
// these values will get filled in the very first time this is accessed...
|
||||
const _state: StylingState = {
|
||||
element: null,
|
||||
directiveIndex: -1,
|
||||
sourceIndex: -1,
|
||||
classesBitMask: -1,
|
||||
classesIndex: -1,
|
||||
stylesBitMask: -1,
|
||||
stylesIndex: -1,
|
||||
};
|
||||
|
||||
export function getStylingState(element: any, readFromMap?: boolean): StylingState {
|
||||
if (!_stylingElement || element !== _stylingElement) {
|
||||
_stylingElement = element;
|
||||
if (readFromMap) {
|
||||
_stylingState = _stateStorage.get(element) || null;
|
||||
ngDevMode && ngDevMode.stylingReadPersistedState++;
|
||||
}
|
||||
_stylingState = _stylingState || {
|
||||
classesBitMask: BIT_MASK_START_VALUE,
|
||||
classesIndex: STYLING_INDEX_START_VALUE,
|
||||
stylesBitMask: BIT_MASK_START_VALUE,
|
||||
stylesIndex: STYLING_INDEX_START_VALUE,
|
||||
};
|
||||
const BIT_MASK_START_VALUE = 0;
|
||||
|
||||
// the `0` start value is reserved for [map]-based entries
|
||||
const INDEX_START_VALUE = 1;
|
||||
|
||||
/**
|
||||
* Returns (or instantiates) the styling state for the given element.
|
||||
*
|
||||
* Styling state is accessed and processed each time a style or class binding
|
||||
* is evaluated.
|
||||
*
|
||||
* If and when the provided `element` doesn't match the current element in the
|
||||
* state then this means that styling was recently cleared or the element has
|
||||
* changed in change detection. In both cases the styling state is fully reset.
|
||||
*
|
||||
* If and when the provided `directiveIndex` doesn't match the current directive
|
||||
* index in the state then this means that a new source has introduced itself into
|
||||
* the styling code (or, in other words, another directive or component has started
|
||||
* to apply its styling host bindings to the element).
|
||||
*/
|
||||
export function getStylingState(element: RElement, directiveIndex: number): StylingState {
|
||||
if (_state.element !== element) {
|
||||
_state.element = element;
|
||||
_state.directiveIndex = directiveIndex;
|
||||
_state.sourceIndex = directiveIndex === TEMPLATE_DIRECTIVE_INDEX ? 0 : 1;
|
||||
_state.classesBitMask = BIT_MASK_START_VALUE;
|
||||
_state.classesIndex = INDEX_START_VALUE;
|
||||
_state.stylesBitMask = BIT_MASK_START_VALUE;
|
||||
_state.stylesIndex = INDEX_START_VALUE;
|
||||
} else if (_state.directiveIndex !== directiveIndex) {
|
||||
_state.directiveIndex = directiveIndex;
|
||||
_state.sourceIndex++;
|
||||
}
|
||||
return _stylingState !;
|
||||
return _state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the styling state so that it can be used by another element's styling code.
|
||||
*/
|
||||
export function resetStylingState() {
|
||||
_stylingState = null;
|
||||
_stylingElement = null;
|
||||
}
|
||||
|
||||
export function storeStylingState(element: any, state: StylingState) {
|
||||
ngDevMode && ngDevMode.stylingWritePersistedState++;
|
||||
_stateStorage.set(element, state);
|
||||
}
|
||||
|
||||
export function deleteStylingStateFromStorage(element: any) {
|
||||
_stateStorage.delete(element);
|
||||
}
|
||||
|
||||
export function resetAllStylingState() {
|
||||
resetStylingState();
|
||||
_stateStorage.clear();
|
||||
_state.element = null;
|
||||
}
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
*/
|
||||
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||
import {RElement} from '../interfaces/renderer';
|
||||
import {LView} from '../interfaces/view';
|
||||
import {getCurrentStyleSanitizer} from '../state';
|
||||
import {attachDebugObject} from '../util/debug_utils';
|
||||
|
||||
import {applyStyling} from './bindings';
|
||||
import {ApplyStylingFn, LStylingData, TStylingContext, TStylingContextIndex} from './interfaces';
|
||||
import {applyStylingViaContext} from './bindings';
|
||||
import {ApplyStylingFn, LStylingData, TStylingConfig, TStylingContext, TStylingContextIndex} from './interfaces';
|
||||
import {activateStylingMapFeature} from './map_based_bindings';
|
||||
import {getDefaultValue, getGuardMask, getProp, getValuesCount, isContextLocked, isMapBased, isSanitizationRequired} from './util';
|
||||
import {allowDirectStyling as _allowDirectStyling, getDefaultValue, getGuardMask, getProp, getPropValuesStartPosition, getValuesCount, hasConfig, isContextLocked, isMapBased, isSanitizationRequired} from './util';
|
||||
|
||||
|
||||
|
||||
@@ -53,20 +52,32 @@ export interface DebugStyling {
|
||||
/** The associated TStylingContext instance */
|
||||
context: TStylingContext;
|
||||
|
||||
/** Which configuration flags are active (see `TStylingContextConfig`) */
|
||||
config: {
|
||||
hasMapBindings: boolean; //
|
||||
hasPropBindings: boolean; //
|
||||
hasCollisions: boolean; //
|
||||
hasTemplateBindings: boolean; //
|
||||
hasHostBindings: boolean; //
|
||||
templateBindingsLocked: boolean; //
|
||||
hostBindingsLocked: boolean; //
|
||||
allowDirectStyling: boolean; //
|
||||
};
|
||||
|
||||
/**
|
||||
* A summarization of each style/class property
|
||||
* present in the context.
|
||||
* present in the context
|
||||
*/
|
||||
summary: {[key: string]: LStylingSummary};
|
||||
summary: {[propertyName: string]: LStylingSummary};
|
||||
|
||||
/**
|
||||
* A key/value map of all styling properties and their
|
||||
* runtime values.
|
||||
* runtime values
|
||||
*/
|
||||
values: {[key: string]: string | number | null | boolean};
|
||||
values: {[propertyName: string]: string | number | null | boolean};
|
||||
|
||||
/**
|
||||
* Overrides the sanitizer used to process styles.
|
||||
* Overrides the sanitizer used to process styles
|
||||
*/
|
||||
overrideSanitizer(sanitizer: StyleSanitizeFn|null): void;
|
||||
}
|
||||
@@ -83,9 +94,15 @@ export interface TStylingTupleSummary {
|
||||
|
||||
/**
|
||||
* The bit guard mask that is used to compare and protect against
|
||||
* styling changes when and styling bindings update
|
||||
* styling changes when any template style/class bindings update
|
||||
*/
|
||||
guardMask: number;
|
||||
templateBitMask: number;
|
||||
|
||||
/**
|
||||
* The bit guard mask that is used to compare and protect against
|
||||
* styling changes when any host style/class bindings update
|
||||
*/
|
||||
hostBindingsBitMask: number;
|
||||
|
||||
/**
|
||||
* Whether or not the entry requires sanitization
|
||||
@@ -93,18 +110,18 @@ export interface TStylingTupleSummary {
|
||||
sanitizationRequired: boolean;
|
||||
|
||||
/**
|
||||
* The default value that will be applied if any bindings are falsy.
|
||||
* The default value that will be applied if any bindings are falsy
|
||||
*/
|
||||
defaultValue: string|boolean|null;
|
||||
|
||||
/**
|
||||
* All bindingIndex sources that have been registered for this style.
|
||||
* All bindingIndex sources that have been registered for this style
|
||||
*/
|
||||
sources: (number|null|string)[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates and attaches an instance of `TStylingContextDebug` to the provided context.
|
||||
* Instantiates and attaches an instance of `TStylingContextDebug` to the provided context
|
||||
*/
|
||||
export function attachStylingDebugObject(context: TStylingContext) {
|
||||
const debug = new TStylingContextDebug(context);
|
||||
@@ -121,7 +138,8 @@ export function attachStylingDebugObject(context: TStylingContext) {
|
||||
class TStylingContextDebug {
|
||||
constructor(public readonly context: TStylingContext) {}
|
||||
|
||||
get isLocked() { return isContextLocked(this.context); }
|
||||
get isTemplateLocked() { return isContextLocked(this.context, true); }
|
||||
get isHostBindingsLocked() { return isContextLocked(this.context, false); }
|
||||
|
||||
/**
|
||||
* Returns a detailed summary of each styling entry in the context.
|
||||
@@ -130,30 +148,36 @@ class TStylingContextDebug {
|
||||
*/
|
||||
get entries(): {[prop: string]: TStylingTupleSummary} {
|
||||
const context = this.context;
|
||||
const totalColumns = getValuesCount(context);
|
||||
const entries: {[prop: string]: TStylingTupleSummary} = {};
|
||||
const start = TStylingContextIndex.MapBindingsPosition;
|
||||
const start = getPropValuesStartPosition(context);
|
||||
let i = start;
|
||||
while (i < context.length) {
|
||||
const valuesCount = getValuesCount(context, i);
|
||||
// the context may contain placeholder values which are populated ahead of time,
|
||||
// but contain no actual binding values. In this situation there is no point in
|
||||
// classifying this as an "entry" since no real data is stored here yet.
|
||||
if (valuesCount) {
|
||||
const prop = getProp(context, i);
|
||||
const guardMask = getGuardMask(context, i);
|
||||
const defaultValue = getDefaultValue(context, i);
|
||||
const sanitizationRequired = isSanitizationRequired(context, i);
|
||||
const bindingsStartPosition = i + TStylingContextIndex.BindingsStartOffset;
|
||||
const prop = getProp(context, i);
|
||||
const templateBitMask = getGuardMask(context, i, false);
|
||||
const hostBindingsBitMask = getGuardMask(context, i, true);
|
||||
const defaultValue = getDefaultValue(context, i);
|
||||
const sanitizationRequired = isSanitizationRequired(context, i);
|
||||
const bindingsStartPosition = i + TStylingContextIndex.BindingsStartOffset;
|
||||
|
||||
const sources: (number | string | null)[] = [];
|
||||
for (let j = 0; j < valuesCount; j++) {
|
||||
sources.push(context[bindingsStartPosition + j] as number | string | null);
|
||||
const sources: (number | string | null)[] = [];
|
||||
|
||||
for (let j = 0; j < totalColumns; j++) {
|
||||
const bindingIndex = context[bindingsStartPosition + j] as number | string | null;
|
||||
if (bindingIndex !== 0) {
|
||||
sources.push(bindingIndex);
|
||||
}
|
||||
|
||||
entries[prop] = {prop, guardMask, sanitizationRequired, valuesCount, defaultValue, sources};
|
||||
}
|
||||
|
||||
i += TStylingContextIndex.BindingsStartOffset + valuesCount;
|
||||
entries[prop] = {
|
||||
prop,
|
||||
templateBitMask,
|
||||
hostBindingsBitMask,
|
||||
sanitizationRequired,
|
||||
valuesCount: sources.length, defaultValue, sources,
|
||||
};
|
||||
|
||||
i += TStylingContextIndex.BindingsStartOffset + totalColumns;
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
@@ -191,6 +215,29 @@ export class NodeStylingDebug implements DebugStyling {
|
||||
return entries;
|
||||
}
|
||||
|
||||
get config() {
|
||||
const hasMapBindings = hasConfig(this.context, TStylingConfig.HasMapBindings);
|
||||
const hasPropBindings = hasConfig(this.context, TStylingConfig.HasPropBindings);
|
||||
const hasCollisions = hasConfig(this.context, TStylingConfig.HasCollisions);
|
||||
const hasTemplateBindings = hasConfig(this.context, TStylingConfig.HasTemplateBindings);
|
||||
const hasHostBindings = hasConfig(this.context, TStylingConfig.HasHostBindings);
|
||||
const templateBindingsLocked = hasConfig(this.context, TStylingConfig.TemplateBindingsLocked);
|
||||
const hostBindingsLocked = hasConfig(this.context, TStylingConfig.HostBindingsLocked);
|
||||
const allowDirectStyling =
|
||||
_allowDirectStyling(this.context, false) || _allowDirectStyling(this.context, true);
|
||||
|
||||
return {
|
||||
hasMapBindings, //
|
||||
hasPropBindings, //
|
||||
hasCollisions, //
|
||||
hasTemplateBindings, //
|
||||
hasHostBindings, //
|
||||
templateBindingsLocked, //
|
||||
hostBindingsLocked, //
|
||||
allowDirectStyling, //
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a key/value map of all the styles/classes that were last applied to the element.
|
||||
*/
|
||||
@@ -205,16 +252,23 @@ export class NodeStylingDebug implements DebugStyling {
|
||||
// element is only used when the styling algorithm attempts to
|
||||
// style the value (and we mock out the stylingApplyFn anyway).
|
||||
const mockElement = {} as any;
|
||||
const hasMaps = getValuesCount(this.context, TStylingContextIndex.MapBindingsPosition) > 0;
|
||||
const hasMaps = hasConfig(this.context, TStylingConfig.HasMapBindings);
|
||||
if (hasMaps) {
|
||||
activateStylingMapFeature();
|
||||
}
|
||||
|
||||
const mapFn: ApplyStylingFn =
|
||||
(renderer: any, element: RElement, prop: string, value: string | null,
|
||||
bindingIndex?: number | null) => { fn(prop, value, bindingIndex || null); };
|
||||
bindingIndex?: number | null) => fn(prop, value, bindingIndex || null);
|
||||
|
||||
const sanitizer = this._isClassBased ? null : (this._sanitizer || getCurrentStyleSanitizer());
|
||||
applyStyling(this.context, null, mockElement, this._data, true, mapFn, sanitizer);
|
||||
|
||||
// run the template bindings
|
||||
applyStylingViaContext(
|
||||
this.context, null, mockElement, this._data, true, mapFn, sanitizer, false);
|
||||
|
||||
// and also the host bindings
|
||||
applyStylingViaContext(
|
||||
this.context, null, mockElement, this._data, true, mapFn, sanitizer, true);
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user