refactor(ivy): remove styling state storage and introduce direct style writing (#32259)
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
This commit is contained in:
parent
8a7129e21a
commit
3b37469735
|
@ -16,8 +16,8 @@
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es5": 3042,
|
"runtime-es5": 3042,
|
||||||
"runtime-es2015": 3048,
|
"runtime-es2015": 3048,
|
||||||
"main-es5": 493330,
|
"main-es5": 492593,
|
||||||
"main-es2015": 435296,
|
"main-es2015": 438296,
|
||||||
"polyfills-es5": 131024,
|
"polyfills-es5": 131024,
|
||||||
"polyfills-es2015": 52433
|
"polyfills-es2015": 52433
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es5": 2932,
|
"runtime-es5": 2932,
|
||||||
"runtime-es2015": 2938,
|
"runtime-es2015": 2938,
|
||||||
"main-es5": 550854,
|
"main-es5": 552068,
|
||||||
"main-es2015": 493320,
|
"main-es2015": 493320,
|
||||||
"polyfills-es5": 131024,
|
"polyfills-es5": 131024,
|
||||||
"polyfills-es2015": 52433
|
"polyfills-es2015": 52433
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime": 1440,
|
"runtime": 1440,
|
||||||
"main": 13264,
|
"main": 13604,
|
||||||
"polyfills": 45340
|
"polyfills": 45340
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ export class TableComponent {
|
||||||
@NgModule({imports: [BrowserModule], bootstrap: [TableComponent], declarations: [TableComponent]})
|
@NgModule({imports: [BrowserModule], bootstrap: [TableComponent], declarations: [TableComponent]})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
constructor(sanitizer: DomSanitizer) {
|
constructor(sanitizer: DomSanitizer) {
|
||||||
trustedEmptyColor = sanitizer.bypassSecurityTrustStyle('');
|
trustedEmptyColor = sanitizer.bypassSecurityTrustStyle('white');
|
||||||
trustedGreyColor = sanitizer.bypassSecurityTrustStyle('grey');
|
trustedGreyColor = sanitizer.bypassSecurityTrustStyle('grey');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* 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 {bindAction, profile} from '../../util';
|
||||||
import {createDom, destroyDom, detectChanges} from '../render3/tree';
|
import {createDom, destroyDom, detectChanges} from '../render3/tree';
|
||||||
|
@ -39,7 +39,6 @@ export function TreeTpl(rf: ɵRenderFlags, ctx: TreeNode) {
|
||||||
ɵɵelementStart(0, 'tree');
|
ɵɵelementStart(0, 'tree');
|
||||||
{
|
{
|
||||||
ɵɵelementStart(1, 'span');
|
ɵɵelementStart(1, 'span');
|
||||||
ɵɵstyling();
|
|
||||||
{ ɵɵtext(2); }
|
{ ɵɵtext(2); }
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
ɵɵcontainer(3);
|
ɵɵcontainer(3);
|
||||||
|
@ -50,7 +49,6 @@ export function TreeTpl(rf: ɵRenderFlags, ctx: TreeNode) {
|
||||||
if (rf & ɵRenderFlags.Update) {
|
if (rf & ɵRenderFlags.Update) {
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('background-color', ctx.depth % 2 ? '' : 'grey');
|
ɵɵstyleProp('background-color', ctx.depth % 2 ? '' : 'grey');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵtextInterpolate1(' ', ctx.value, ' ');
|
ɵɵtextInterpolate1(' ', ctx.value, ' ');
|
||||||
ɵɵcontainerRefreshStart(3);
|
ɵɵcontainerRefreshStart(3);
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* 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';
|
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) {
|
hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) {
|
||||||
if (rf & ɵRenderFlags.Create) {
|
if (rf & ɵRenderFlags.Create) {
|
||||||
ɵɵallocHostVars(1);
|
ɵɵallocHostVars(1);
|
||||||
ɵɵstyling();
|
|
||||||
}
|
}
|
||||||
if (rf & ɵRenderFlags.Update) {
|
if (rf & ɵRenderFlags.Update) {
|
||||||
ɵɵclassMap(ctx.getValue());
|
ɵɵclassMap(ctx.getValue());
|
||||||
ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* 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';
|
import {NgStyleImpl, NgStyleImplProvider} from './ng_style_impl';
|
||||||
|
|
||||||
|
@ -34,12 +34,8 @@ export const ngStyleDirectiveDef__POST_R3__ = ɵɵdefineDirective({
|
||||||
type: function() {} as any,
|
type: function() {} as any,
|
||||||
selectors: null as any,
|
selectors: null as any,
|
||||||
hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) {
|
hostBindings: function(rf: ɵRenderFlags, ctx: any, elIndex: number) {
|
||||||
if (rf & ɵRenderFlags.Create) {
|
|
||||||
ɵɵstyling();
|
|
||||||
}
|
|
||||||
if (rf & ɵRenderFlags.Update) {
|
if (rf & ɵRenderFlags.Update) {
|
||||||
ɵɵstyleMap(ctx.getValue());
|
ɵɵstyleMap(ctx.getValue());
|
||||||
ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -483,14 +483,11 @@ describe('compiler compliance', () => {
|
||||||
vars: 2,
|
vars: 2,
|
||||||
template: function MyComponent_Template(rf,ctx){
|
template: function MyComponent_Template(rf,ctx){
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleProp("background-color", ctx.color);
|
$r3$.ɵɵstyleProp("background-color", ctx.color);
|
||||||
$r3$.ɵɵclassProp("error", ctx.error);
|
$r3$.ɵɵclassProp("error", ctx.error);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
encapsulation: 2
|
encapsulation: 2
|
||||||
|
|
|
@ -376,14 +376,11 @@ describe('compiler compliance: styling', () => {
|
||||||
const template = `
|
const template = `
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap($ctx$.myStyleExp);
|
$r3$.ɵɵstyleMap($ctx$.myStyleExp);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -442,13 +439,10 @@ describe('compiler compliance: styling', () => {
|
||||||
vars: 2,
|
vars: 2,
|
||||||
template: function MyComponentWithInterpolation_Template(rf, $ctx$) {
|
template: function MyComponentWithInterpolation_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵclassMapInterpolate1("foo foo-", $ctx$.fooId, "");
|
$r3$.ɵɵclassMapInterpolate1("foo foo-", $ctx$.fooId, "");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
|
@ -456,13 +450,10 @@ describe('compiler compliance: styling', () => {
|
||||||
vars: 3,
|
vars: 3,
|
||||||
template: function MyComponentWithMuchosInterpolation_Template(rf, $ctx$) {
|
template: function MyComponentWithMuchosInterpolation_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵclassMapInterpolate2("foo foo-", $ctx$.fooId, "-", $ctx$.fooUsername, "");
|
$r3$.ɵɵclassMapInterpolate2("foo foo-", $ctx$.fooId, "-", $ctx$.fooUsername, "");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
|
@ -470,13 +461,10 @@ describe('compiler compliance: styling', () => {
|
||||||
vars: 1,
|
vars: 1,
|
||||||
template: function MyComponentWithoutInterpolation_Template(rf, $ctx$) {
|
template: function MyComponentWithoutInterpolation_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵclassMap($ctx$.exp);
|
$r3$.ɵɵclassMap($ctx$.exp);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -522,16 +510,13 @@ describe('compiler compliance: styling', () => {
|
||||||
vars: 4,
|
vars: 4,
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div", $_c0$);
|
$r3$.ɵɵelement(0, "div", $_c0$);
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap($ctx$.myStyleExp);
|
$r3$.ɵɵstyleMap($ctx$.myStyleExp);
|
||||||
$r3$.ɵɵstyleProp("width", $ctx$.myWidth);
|
$r3$.ɵɵstyleProp("width", $ctx$.myWidth);
|
||||||
$r3$.ɵɵstyleProp("height", $ctx$.myHeight);
|
$r3$.ɵɵstyleProp("height", $ctx$.myHeight);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵattribute("style", "border-width: 10px", $r3$.ɵɵsanitizeStyle);
|
$r3$.ɵɵattribute("style", "border-width: 10px", $r3$.ɵɵsanitizeStyle);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -572,14 +557,11 @@ describe('compiler compliance: styling', () => {
|
||||||
vars: 1,
|
vars: 1,
|
||||||
template: function MyComponent_Template(rf, ctx) {
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleProp("background-image", ctx.myImage);
|
$r3$.ɵɵstyleProp("background-image", ctx.myImage);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
encapsulation: 2
|
encapsulation: 2
|
||||||
|
@ -612,13 +594,10 @@ describe('compiler compliance: styling', () => {
|
||||||
const template = `
|
const template = `
|
||||||
template: function MyComponent_Template(rf, ctx) {
|
template: function MyComponent_Template(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleProp("font-size", 12, "px");
|
$r3$.ɵɵstyleProp("font-size", 12, "px");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -676,13 +655,10 @@ describe('compiler compliance: styling', () => {
|
||||||
const template = `
|
const template = `
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵclassMap($ctx$.myClassExp);
|
$r3$.ɵɵclassMap($ctx$.myClassExp);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -728,15 +704,12 @@ describe('compiler compliance: styling', () => {
|
||||||
vars: 4,
|
vars: 4,
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div", $e0_attrs$);
|
$r3$.ɵɵelement(0, "div", $e0_attrs$);
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵclassMap($ctx$.myClassExp);
|
$r3$.ɵɵclassMap($ctx$.myClassExp);
|
||||||
$r3$.ɵɵclassProp("apple", $ctx$.yesToApple);
|
$r3$.ɵɵclassProp("apple", $ctx$.yesToApple);
|
||||||
$r3$.ɵɵclassProp("orange", $ctx$.yesToOrange);
|
$r3$.ɵɵclassProp("orange", $ctx$.yesToOrange);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵattribute("class", "banana");
|
$r3$.ɵɵattribute("class", "banana");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -844,15 +817,12 @@ describe('compiler compliance: styling', () => {
|
||||||
const template = `
|
const template = `
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap($ctx$.myStyleExp);
|
$r3$.ɵɵstyleMap($ctx$.myStyleExp);
|
||||||
$r3$.ɵɵclassMap($ctx$.myClassExp);
|
$r3$.ɵɵclassMap($ctx$.myClassExp);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -887,7 +857,6 @@ describe('compiler compliance: styling', () => {
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelementStart(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵpipe(1, "stylePipe");
|
$r3$.ɵɵpipe(1, "stylePipe");
|
||||||
$r3$.ɵɵpipe(2, "classPipe");
|
$r3$.ɵɵpipe(2, "classPipe");
|
||||||
$r3$.ɵɵelementEnd();
|
$r3$.ɵɵelementEnd();
|
||||||
|
@ -896,7 +865,6 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap($r3$.ɵɵpipeBind1(1, 2, $ctx$.myStyleExp));
|
$r3$.ɵɵstyleMap($r3$.ɵɵpipeBind1(1, 2, $ctx$.myStyleExp));
|
||||||
$r3$.ɵɵclassMap($r3$.ɵɵpipeBind1(2, 4, $ctx$.myClassExp));
|
$r3$.ɵɵclassMap($r3$.ɵɵpipeBind1(2, 4, $ctx$.myClassExp));
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -939,7 +907,6 @@ describe('compiler compliance: styling', () => {
|
||||||
template: function MyComponent_Template(rf, $ctx$) {
|
template: function MyComponent_Template(rf, $ctx$) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelementStart(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵpipe(1, "pipe");
|
$r3$.ɵɵpipe(1, "pipe");
|
||||||
$r3$.ɵɵpipe(2, "pipe");
|
$r3$.ɵɵpipe(2, "pipe");
|
||||||
$r3$.ɵɵpipe(3, "pipe");
|
$r3$.ɵɵpipe(3, "pipe");
|
||||||
|
@ -954,7 +921,6 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵstyleProp("bar", $r3$.ɵɵpipeBind2(2, 9, $ctx$.barExp, 3000));
|
$r3$.ɵɵstyleProp("bar", $r3$.ɵɵpipeBind2(2, 9, $ctx$.barExp, 3000));
|
||||||
$r3$.ɵɵstyleProp("baz", $r3$.ɵɵpipeBind2(3, 12, $ctx$.bazExp, 4000));
|
$r3$.ɵɵstyleProp("baz", $r3$.ɵɵpipeBind2(3, 12, $ctx$.bazExp, 4000));
|
||||||
$r3$.ɵɵclassProp("foo", $r3$.ɵɵpipeBind2(4, 15, $ctx$.fooExp, 2000));
|
$r3$.ɵɵclassProp("foo", $r3$.ɵɵpipeBind2(4, 15, $ctx$.fooExp, 2000));
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(5);
|
$r3$.ɵɵadvance(5);
|
||||||
$r3$.ɵɵtextInterpolate1(" ", $ctx$.item, "");
|
$r3$.ɵɵtextInterpolate1(" ", $ctx$.item, "");
|
||||||
}
|
}
|
||||||
|
@ -999,16 +965,12 @@ describe('compiler compliance: styling', () => {
|
||||||
…
|
…
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleProp("width", $ctx$.w1);
|
$r3$.ɵɵstyleProp("width", $ctx$.w1);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵstyleProp("height", $ctx$.h1);
|
$r3$.ɵɵstyleProp("height", $ctx$.h1);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵclassProp("active", $ctx$.a1);
|
$r3$.ɵɵclassProp("active", $ctx$.a1);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵclassProp("removed", $ctx$.r1);
|
$r3$.ɵɵclassProp("removed", $ctx$.r1);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1058,7 +1020,6 @@ describe('compiler compliance: styling', () => {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵallocHostVars(4);
|
$r3$.ɵɵallocHostVars(4);
|
||||||
$r3$.ɵɵelementHostAttrs($e0_attrs$);
|
$r3$.ɵɵelementHostAttrs($e0_attrs$);
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
|
@ -1066,7 +1027,6 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵclassMap(ctx.myClass);
|
$r3$.ɵɵclassMap(ctx.myClass);
|
||||||
$r3$.ɵɵstyleProp("color", ctx.myColorProp);
|
$r3$.ɵɵstyleProp("color", ctx.myColorProp);
|
||||||
$r3$.ɵɵclassProp("foo", ctx.myFooClass);
|
$r3$.ɵɵclassProp("foo", ctx.myFooClass);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
consts: 0,
|
consts: 0,
|
||||||
|
@ -1118,7 +1078,6 @@ describe('compiler compliance: styling', () => {
|
||||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵallocHostVars(6);
|
$r3$.ɵɵallocHostVars(6);
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
|
@ -1128,7 +1087,6 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵstyleProp("width", ctx.myWidthProp);
|
$r3$.ɵɵstyleProp("width", ctx.myWidthProp);
|
||||||
$r3$.ɵɵclassProp("bar", ctx.myBarClass);
|
$r3$.ɵɵclassProp("bar", ctx.myBarClass);
|
||||||
$r3$.ɵɵclassProp("foo", ctx.myFooClass);
|
$r3$.ɵɵclassProp("foo", ctx.myFooClass);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
consts: 0,
|
consts: 0,
|
||||||
|
@ -1179,9 +1137,7 @@ describe('compiler compliance: styling', () => {
|
||||||
const template = `
|
const template = `
|
||||||
function MyComponent_Template(rf, ctx) {
|
function MyComponent_Template(rf, ctx) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵelementStart(0, "div");
|
$r3$.ɵɵelement(0, "div");
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
$r3$.ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
|
@ -1189,7 +1145,6 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵclassMap(ctx.myClassExp);
|
$r3$.ɵɵclassMap(ctx.myClassExp);
|
||||||
$r3$.ɵɵstyleProp("height", ctx.myHeightExp);
|
$r3$.ɵɵstyleProp("height", ctx.myHeightExp);
|
||||||
$r3$.ɵɵclassProp("bar", ctx.myBarClassExp);
|
$r3$.ɵɵclassProp("bar", ctx.myBarClassExp);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
`;
|
`;
|
||||||
|
@ -1198,7 +1153,6 @@ describe('compiler compliance: styling', () => {
|
||||||
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function MyComponent_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵallocHostVars(4);
|
$r3$.ɵɵallocHostVars(4);
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
|
@ -1206,7 +1160,6 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵclassMap(ctx.myClassExp);
|
$r3$.ɵɵclassMap(ctx.myClassExp);
|
||||||
$r3$.ɵɵstyleProp("width", ctx.myWidthExp);
|
$r3$.ɵɵstyleProp("width", ctx.myWidthExp);
|
||||||
$r3$.ɵɵclassProp("foo", ctx.myFooClassExp);
|
$r3$.ɵɵclassProp("foo", ctx.myFooClassExp);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
`;
|
`;
|
||||||
|
@ -1266,35 +1219,29 @@ describe('compiler compliance: styling', () => {
|
||||||
function ClassDirective_HostBindings(rf, ctx, elIndex) {
|
function ClassDirective_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵallocHostVars(1);
|
$r3$.ɵɵallocHostVars(1);
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵclassMap(ctx.myClassMap);
|
$r3$.ɵɵclassMap(ctx.myClassMap);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
function WidthDirective_HostBindings(rf, ctx, elIndex) {
|
function WidthDirective_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵallocHostVars(2);
|
$r3$.ɵɵallocHostVars(2);
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleProp("width", ctx.myWidth);
|
$r3$.ɵɵstyleProp("width", ctx.myWidth);
|
||||||
$r3$.ɵɵclassProp("foo", ctx.myFooClass);
|
$r3$.ɵɵclassProp("foo", ctx.myFooClass);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
function HeightDirective_HostBindings(rf, ctx, elIndex) {
|
function HeightDirective_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵallocHostVars(2);
|
$r3$.ɵɵallocHostVars(2);
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleProp("height", ctx.myHeight);
|
$r3$.ɵɵstyleProp("height", ctx.myHeight);
|
||||||
$r3$.ɵɵclassProp("bar", ctx.myBarClass);
|
$r3$.ɵɵclassProp("bar", ctx.myBarClass);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
|
@ -1336,34 +1283,24 @@ describe('compiler compliance: styling', () => {
|
||||||
…
|
…
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵclassMapInterpolateV(["a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j"]);
|
$r3$.ɵɵclassMapInterpolateV(["a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j"]);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵclassMapInterpolate8("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i");
|
$r3$.ɵɵclassMapInterpolate8("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵclassMapInterpolate7("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h");
|
$r3$.ɵɵclassMapInterpolate7("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵclassMapInterpolate6("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g");
|
$r3$.ɵɵclassMapInterpolate6("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵclassMapInterpolate5("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f");
|
$r3$.ɵɵclassMapInterpolate5("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵclassMapInterpolate4("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e");
|
$r3$.ɵɵclassMapInterpolate4("a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵclassMapInterpolate3("a", ctx.one, "b", ctx.two, "c", ctx.three, "d");
|
$r3$.ɵɵclassMapInterpolate3("a", ctx.one, "b", ctx.two, "c", ctx.three, "d");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵclassMapInterpolate2("a", ctx.one, "b", ctx.two, "c");
|
$r3$.ɵɵclassMapInterpolate2("a", ctx.one, "b", ctx.two, "c");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵclassMapInterpolate1("a", ctx.one, "b");
|
$r3$.ɵɵclassMapInterpolate1("a", ctx.one, "b");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵclassMap(ctx.one);
|
$r3$.ɵɵclassMap(ctx.one);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
`;
|
`;
|
||||||
|
@ -1438,34 +1375,24 @@ describe('compiler compliance: styling', () => {
|
||||||
…
|
…
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstylePropInterpolateV("color", ["a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j"]);
|
$r3$.ɵɵstylePropInterpolateV("color", ["a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i", ctx.nine, "j"]);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵstylePropInterpolate8("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i");
|
$r3$.ɵɵstylePropInterpolate8("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h", ctx.eight, "i");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵstylePropInterpolate7("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h");
|
$r3$.ɵɵstylePropInterpolate7("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g", ctx.seven, "h");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵstylePropInterpolate6("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g");
|
$r3$.ɵɵstylePropInterpolate6("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f", ctx.six, "g");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵstylePropInterpolate5("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f");
|
$r3$.ɵɵstylePropInterpolate5("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e", ctx.five, "f");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵstylePropInterpolate4("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e");
|
$r3$.ɵɵstylePropInterpolate4("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d", ctx.four, "e");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵstylePropInterpolate3("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d");
|
$r3$.ɵɵstylePropInterpolate3("color", "a", ctx.one, "b", ctx.two, "c", ctx.three, "d");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵstylePropInterpolate2("color", "a", ctx.one, "b", ctx.two, "c");
|
$r3$.ɵɵstylePropInterpolate2("color", "a", ctx.one, "b", ctx.two, "c");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵstylePropInterpolate1("color", "a", ctx.one, "b");
|
$r3$.ɵɵstylePropInterpolate1("color", "a", ctx.one, "b");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
$r3$.ɵɵadvance(1);
|
$r3$.ɵɵadvance(1);
|
||||||
$r3$.ɵɵstyleProp("color", ctx.one);
|
$r3$.ɵɵstyleProp("color", ctx.one);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
`;
|
`;
|
||||||
|
@ -1496,7 +1423,6 @@ describe('compiler compliance: styling', () => {
|
||||||
…
|
…
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstylePropInterpolate2("width", "a", ctx.one, "b", ctx.two, "c", "px");
|
$r3$.ɵɵstylePropInterpolate2("width", "a", ctx.one, "b", ctx.two, "c", "px");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
`;
|
`;
|
||||||
|
@ -1527,7 +1453,6 @@ describe('compiler compliance: styling', () => {
|
||||||
…
|
…
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstylePropInterpolate2("width", "a", ctx.one, "b", ctx.two, "c");
|
$r3$.ɵɵstylePropInterpolate2("width", "a", ctx.one, "b", ctx.two, "c");
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
`;
|
`;
|
||||||
|
@ -1583,14 +1508,12 @@ describe('compiler compliance: styling', () => {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵallocHostVars(4);
|
$r3$.ɵɵallocHostVars(4);
|
||||||
$r3$.ɵɵelementHostAttrs($_c0$);
|
$r3$.ɵɵelementHostAttrs($_c0$);
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵhostProperty("id", ctx.id)("title", ctx.title);
|
$r3$.ɵɵhostProperty("id", ctx.id)("title", ctx.title);
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap(ctx.myStyle);
|
$r3$.ɵɵstyleMap(ctx.myStyle);
|
||||||
$r3$.ɵɵclassMap(ctx.myClass);
|
$r3$.ɵɵclassMap(ctx.myClass);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1627,13 +1550,11 @@ describe('compiler compliance: styling', () => {
|
||||||
hostBindings: function WidthDirective_HostBindings(rf, ctx, elIndex) {
|
hostBindings: function WidthDirective_HostBindings(rf, ctx, elIndex) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
$r3$.ɵɵallocHostVars(4);
|
$r3$.ɵɵallocHostVars(4);
|
||||||
$r3$.ɵɵstyling();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵhostProperty("id", ctx.id)("title", ctx.title);
|
$r3$.ɵɵhostProperty("id", ctx.id)("title", ctx.title);
|
||||||
$r3$.ɵɵstyleProp("width", ctx.myWidth);
|
$r3$.ɵɵstyleProp("width", ctx.myWidth);
|
||||||
$r3$.ɵɵclassProp("foo", ctx.myFooClass);
|
$r3$.ɵɵclassProp("foo", ctx.myFooClass);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -1669,7 +1590,6 @@ describe('compiler compliance: styling', () => {
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleProp("background-image", ctx.bgExp);
|
$r3$.ɵɵstyleProp("background-image", ctx.bgExp);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
}
|
}
|
||||||
|
@ -1705,7 +1625,6 @@ describe('compiler compliance: styling', () => {
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
$r3$.ɵɵstyleSanitizer($r3$.ɵɵdefaultStyleSanitizer);
|
||||||
$r3$.ɵɵstyleMap(ctx.mapExp);
|
$r3$.ɵɵstyleMap(ctx.mapExp);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
}
|
}
|
||||||
|
@ -1742,7 +1661,6 @@ describe('compiler compliance: styling', () => {
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
$r3$.ɵɵclassMap(ctx.mapExp);
|
$r3$.ɵɵclassMap(ctx.mapExp);
|
||||||
$r3$.ɵɵclassProp("name", ctx.nameExp);
|
$r3$.ɵɵclassProp("name", ctx.nameExp);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
…
|
…
|
||||||
}
|
}
|
||||||
|
@ -1805,7 +1723,6 @@ describe('compiler compliance: styling', () => {
|
||||||
$r3$.ɵɵpureFunction2(6, _c1, ctx._animValue,
|
$r3$.ɵɵpureFunction2(6, _c1, ctx._animValue,
|
||||||
$r3$.ɵɵpureFunction2(3, _c0, ctx._animParam1, ctx._animParam2)));
|
$r3$.ɵɵpureFunction2(3, _c0, ctx._animParam1, ctx._animParam2)));
|
||||||
$r3$.ɵɵclassProp("foo", ctx.foo);
|
$r3$.ɵɵclassProp("foo", ctx.foo);
|
||||||
$r3$.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -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.onClick($event); });
|
||||||
i0.ɵɵlistener("click", function FooCmp_click_HostBindingHandler($event) { return ctx.onBodyClick($event); }, false, i0.ɵɵresolveBody);
|
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.ɵɵlistener("change", function FooCmp_change_HostBindingHandler($event) { return ctx.onChange(ctx.arg1, ctx.arg2, ctx.arg3); });
|
||||||
i0.ɵɵstyling();
|
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
i0.ɵɵhostProperty("prop", ctx.bar);
|
i0.ɵɵhostProperty("prop", ctx.bar);
|
||||||
i0.ɵɵattribute("hello", ctx.foo);
|
i0.ɵɵattribute("hello", ctx.foo);
|
||||||
i0.ɵɵclassProp("someclass", ctx.someClass);
|
i0.ɵɵclassProp("someclass", ctx.someClass);
|
||||||
i0.ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -69,8 +69,6 @@ export class Identifiers {
|
||||||
|
|
||||||
static elementContainer: o.ExternalReference = {name: 'ɵɵelementContainer', moduleName: CORE};
|
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 styleMap: o.ExternalReference = {name: 'ɵɵstyleMap', moduleName: CORE};
|
||||||
|
|
||||||
static classMap: o.ExternalReference = {name: 'ɵɵclassMap', moduleName: CORE};
|
static classMap: o.ExternalReference = {name: 'ɵɵclassMap', moduleName: CORE};
|
||||||
|
@ -115,8 +113,6 @@ export class Identifiers {
|
||||||
static stylePropInterpolateV:
|
static stylePropInterpolateV:
|
||||||
o.ExternalReference = {name: 'ɵɵstylePropInterpolateV', moduleName: CORE};
|
o.ExternalReference = {name: 'ɵɵstylePropInterpolateV', moduleName: CORE};
|
||||||
|
|
||||||
static stylingApply: o.ExternalReference = {name: 'ɵɵstylingApply', moduleName: CORE};
|
|
||||||
|
|
||||||
static styleSanitizer: o.ExternalReference = {name: 'ɵɵstyleSanitizer', moduleName: CORE};
|
static styleSanitizer: o.ExternalReference = {name: 'ɵɵstyleSanitizer', moduleName: CORE};
|
||||||
|
|
||||||
static elementHostAttrs: o.ExternalReference = {name: 'ɵɵelementHostAttrs', moduleName: CORE};
|
static elementHostAttrs: o.ExternalReference = {name: 'ɵɵelementHostAttrs', moduleName: CORE};
|
||||||
|
|
|
@ -713,16 +713,6 @@ function createHostBindingsFunction(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (styleBuilder.hasBindings) {
|
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
|
// 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
|
// the update block of a component/directive templateFn/hostBindingsFn so that the bindings
|
||||||
// are evaluated and updated for the element.
|
// are evaluated and updated for the element.
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
import {ConstantPool} from '../../constant_pool';
|
import {ConstantPool} from '../../constant_pool';
|
||||||
import {AttributeMarker} from '../../core';
|
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 * as o from '../../output/output_ast';
|
||||||
import {ParseSourceSpan} from '../../parse_util';
|
import {ParseSourceSpan} from '../../parse_util';
|
||||||
import {isEmptyExpression} from '../../template_parser/template_parser';
|
import {isEmptyExpression} from '../../template_parser/template_parser';
|
||||||
|
@ -68,7 +68,6 @@ interface BoundStylingEntry {
|
||||||
* classMap(...)
|
* classMap(...)
|
||||||
* styleProp(...)
|
* styleProp(...)
|
||||||
* classProp(...)
|
* classProp(...)
|
||||||
* stylingApply(...)
|
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* The creation/update methods within the builder class produce these instructions.
|
* 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]`)
|
* (i.e. `[style]`, `[class]`, `[style.prop]` or `[class.name]`)
|
||||||
*/
|
*/
|
||||||
public hasBindings = false;
|
public hasBindings = false;
|
||||||
|
public hasBindingsWithPipes = false;
|
||||||
|
|
||||||
/** the input for [class] (if it exists) */
|
/** the input for [class] (if it exists) */
|
||||||
private _classMapInput: BoundStylingEntry|null = null;
|
private _classMapInput: BoundStylingEntry|null = null;
|
||||||
|
@ -187,6 +187,7 @@ export class StylingBuilder {
|
||||||
}
|
}
|
||||||
this._lastStylingInput = entry;
|
this._lastStylingInput = entry;
|
||||||
this._firstStylingInput = this._firstStylingInput || entry;
|
this._firstStylingInput = this._firstStylingInput || entry;
|
||||||
|
this._checkForPipes(value);
|
||||||
this.hasBindings = true;
|
this.hasBindings = true;
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
@ -207,10 +208,17 @@ export class StylingBuilder {
|
||||||
}
|
}
|
||||||
this._lastStylingInput = entry;
|
this._lastStylingInput = entry;
|
||||||
this._firstStylingInput = this._firstStylingInput || entry;
|
this._firstStylingInput = this._firstStylingInput || entry;
|
||||||
|
this._checkForPipes(value);
|
||||||
this.hasBindings = true;
|
this.hasBindings = true;
|
||||||
return entry;
|
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.
|
* Registers the element's static style string value to the builder.
|
||||||
*
|
*
|
||||||
|
@ -284,25 +292,6 @@ export class StylingBuilder {
|
||||||
return null;
|
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`.
|
* Builds an instruction with all the expressions and parameters for `classMap`.
|
||||||
*
|
*
|
||||||
|
@ -422,15 +411,6 @@ export class StylingBuilder {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private _buildApplyFn(): StylingInstruction {
|
|
||||||
return {
|
|
||||||
sourceSpan: this._lastStylingInput ? this._lastStylingInput.sourceSpan : null,
|
|
||||||
reference: R3.stylingApply,
|
|
||||||
allocateBindingSlots: 0,
|
|
||||||
params: () => { return []; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private _buildSanitizerFn(): StylingInstruction {
|
private _buildSanitizerFn(): StylingInstruction {
|
||||||
return {
|
return {
|
||||||
sourceSpan: this._firstStylingInput ? this._firstStylingInput.sourceSpan : null,
|
sourceSpan: this._firstStylingInput ? this._firstStylingInput.sourceSpan : null,
|
||||||
|
@ -460,7 +440,6 @@ export class StylingBuilder {
|
||||||
}
|
}
|
||||||
instructions.push(...this._buildStyleInputs(valueConverter));
|
instructions.push(...this._buildStyleInputs(valueConverter));
|
||||||
instructions.push(...this._buildClassInputs(valueConverter));
|
instructions.push(...this._buildClassInputs(valueConverter));
|
||||||
instructions.push(this._buildApplyFn());
|
|
||||||
}
|
}
|
||||||
return instructions;
|
return instructions;
|
||||||
}
|
}
|
||||||
|
|
|
@ -623,11 +623,10 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
|
||||||
const hasChildren = (!isI18nRootElement && this.i18n) ? !hasTextChildrenOnly(element.children) :
|
const hasChildren = (!isI18nRootElement && this.i18n) ? !hasTextChildrenOnly(element.children) :
|
||||||
element.children.length > 0;
|
element.children.length > 0;
|
||||||
|
|
||||||
const createSelfClosingInstruction = !stylingBuilder.hasBindings &&
|
const createSelfClosingInstruction = !stylingBuilder.hasBindingsWithPipes &&
|
||||||
element.outputs.length === 0 && i18nAttrs.length === 0 && !hasChildren;
|
element.outputs.length === 0 && i18nAttrs.length === 0 && !hasChildren;
|
||||||
|
const createSelfClosingI18nInstruction =
|
||||||
const createSelfClosingI18nInstruction = !createSelfClosingInstruction &&
|
!createSelfClosingInstruction && hasTextChildrenOnly(element.children);
|
||||||
!stylingBuilder.hasBindings && hasTextChildrenOnly(element.children);
|
|
||||||
|
|
||||||
if (createSelfClosingInstruction) {
|
if (createSelfClosingInstruction) {
|
||||||
this.creationInstruction(
|
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)
|
// Generate Listeners (outputs)
|
||||||
element.outputs.forEach((outputAst: t.BoundEvent) => {
|
element.outputs.forEach((outputAst: t.BoundEvent) => {
|
||||||
this.creationInstruction(
|
this.creationInstruction(
|
||||||
|
|
|
@ -120,7 +120,6 @@ export {
|
||||||
ɵɵelementContainerStart,
|
ɵɵelementContainerStart,
|
||||||
ɵɵelementContainerEnd,
|
ɵɵelementContainerEnd,
|
||||||
ɵɵelementContainer,
|
ɵɵelementContainer,
|
||||||
ɵɵstyling,
|
|
||||||
ɵɵstyleMap,
|
ɵɵstyleMap,
|
||||||
ɵɵstyleSanitizer,
|
ɵɵstyleSanitizer,
|
||||||
ɵɵclassMap,
|
ɵɵclassMap,
|
||||||
|
@ -143,7 +142,6 @@ export {
|
||||||
ɵɵstylePropInterpolate7,
|
ɵɵstylePropInterpolate7,
|
||||||
ɵɵstylePropInterpolate8,
|
ɵɵstylePropInterpolate8,
|
||||||
ɵɵstylePropInterpolateV,
|
ɵɵstylePropInterpolateV,
|
||||||
ɵɵstylingApply,
|
|
||||||
ɵɵclassProp,
|
ɵɵclassProp,
|
||||||
ɵɵelementHostAttrs,
|
ɵɵelementHostAttrs,
|
||||||
|
|
||||||
|
|
|
@ -12,10 +12,9 @@ import {CONTAINER_HEADER_OFFSET, LContainer, NATIVE} from '../render3/interfaces
|
||||||
import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node';
|
import {TElementNode, TNode, TNodeFlags, TNodeType} from '../render3/interfaces/node';
|
||||||
import {isComponentHost, isLContainer} from '../render3/interfaces/type_checks';
|
import {isComponentHost, isLContainer} from '../render3/interfaces/type_checks';
|
||||||
import {LView, PARENT, TData, TVIEW, T_HOST} from '../render3/interfaces/view';
|
import {LView, PARENT, TData, TVIEW, T_HOST} from '../render3/interfaces/view';
|
||||||
import {TStylingContext} from '../render3/styling_next/interfaces';
|
import {StylingMapArray, TStylingContext} from '../render3/styling_next/interfaces';
|
||||||
import {stylingMapToStringMap} from '../render3/styling_next/map_based_bindings';
|
|
||||||
import {NodeStylingDebug} from '../render3/styling_next/styling_debug';
|
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 {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, isBrowserEvents, loadLContext} from '../render3/util/discovery_utils';
|
||||||
import {INTERPOLATION_DELIMITER, renderStringify} from '../render3/util/misc_utils';
|
import {INTERPOLATION_DELIMITER, renderStringify} from '../render3/util/misc_utils';
|
||||||
import {findComponentView} from '../render3/util/view_traversal_utils';
|
import {findComponentView} from '../render3/util/view_traversal_utils';
|
||||||
|
@ -431,11 +430,11 @@ function _getStylingDebugInfo(element: any, isClassBased: boolean) {
|
||||||
if (isClassBased) {
|
if (isClassBased) {
|
||||||
return isStylingContext(tNode.classes) ?
|
return isStylingContext(tNode.classes) ?
|
||||||
new NodeStylingDebug(tNode.classes as TStylingContext, lView, true).values :
|
new NodeStylingDebug(tNode.classes as TStylingContext, lView, true).values :
|
||||||
stylingMapToStringMap(tNode.classes);
|
stylingMapToStringMap(tNode.classes as StylingMapArray | null);
|
||||||
} else {
|
} else {
|
||||||
return isStylingContext(tNode.styles) ?
|
return isStylingContext(tNode.styles) ?
|
||||||
new NodeStylingDebug(tNode.styles as TStylingContext, lView, false).values :
|
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 {PlayerHandler} from './interfaces/player';
|
||||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||||
import {CONTEXT, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
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 {publishDefaultGlobalUtils} from './util/global_utils';
|
||||||
import {defaultScheduler, stringifyForError} from './util/misc_utils';
|
import {defaultScheduler, stringifyForError} from './util/misc_utils';
|
||||||
import {getRootContext} from './util/view_traversal_utils';
|
import {getRootContext} from './util/view_traversal_utils';
|
||||||
|
@ -217,6 +217,7 @@ export function createRootComponent<T>(
|
||||||
if (tView.firstTemplatePass && componentDef.hostBindings) {
|
if (tView.firstTemplatePass && componentDef.hostBindings) {
|
||||||
const elementIndex = rootTNode.index - HEADER_OFFSET;
|
const elementIndex = rootTNode.index - HEADER_OFFSET;
|
||||||
setActiveHostElement(elementIndex);
|
setActiveHostElement(elementIndex);
|
||||||
|
incrementActiveDirectiveId();
|
||||||
|
|
||||||
const expando = tView.expandoInstructions !;
|
const expando = tView.expandoInstructions !;
|
||||||
invokeHostBindingsInCreationMode(
|
invokeHostBindingsInCreationMode(
|
||||||
|
|
|
@ -11,7 +11,6 @@ import {fillProperties} from '../../util/property';
|
||||||
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
|
import {EMPTY_ARRAY, EMPTY_OBJ} from '../empty';
|
||||||
import {ComponentDef, ContentQueriesFunction, DirectiveDef, DirectiveDefFeature, HostBindingsFunction, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
|
import {ComponentDef, ContentQueriesFunction, DirectiveDef, DirectiveDefFeature, HostBindingsFunction, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
|
||||||
import {isComponentDef} from '../interfaces/type_checks';
|
import {isComponentDef} from '../interfaces/type_checks';
|
||||||
import {adjustActiveDirectiveSuperClassDepthPosition} from '../state';
|
|
||||||
|
|
||||||
import {ɵɵNgOnChangesFeature} from './ng_onchanges_feature';
|
import {ɵɵNgOnChangesFeature} from './ng_onchanges_feature';
|
||||||
|
|
||||||
|
@ -177,24 +176,8 @@ function inheritHostBindings(
|
||||||
// to ensure we don't inherit it twice.
|
// to ensure we don't inherit it twice.
|
||||||
if (superHostBindings !== prevHostBindings) {
|
if (superHostBindings !== prevHostBindings) {
|
||||||
if (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) => {
|
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);
|
superHostBindings(rf, ctx, elementIndex);
|
||||||
} finally {
|
|
||||||
adjustActiveDirectiveSuperClassDepthPosition(-1);
|
|
||||||
}
|
|
||||||
prevHostBindings(rf, ctx, elementIndex);
|
prevHostBindings(rf, ctx, elementIndex);
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -114,8 +114,6 @@ export {
|
||||||
ɵɵstylePropInterpolateV,
|
ɵɵstylePropInterpolateV,
|
||||||
|
|
||||||
ɵɵstyleSanitizer,
|
ɵɵstyleSanitizer,
|
||||||
ɵɵstyling,
|
|
||||||
ɵɵstylingApply,
|
|
||||||
ɵɵtemplate,
|
ɵɵtemplate,
|
||||||
|
|
||||||
ɵɵtext,
|
ɵɵtext,
|
||||||
|
|
|
@ -8,7 +8,10 @@
|
||||||
import {assertDataInRange, assertGreaterThan} from '../../util/assert';
|
import {assertDataInRange, assertGreaterThan} from '../../util/assert';
|
||||||
import {executeCheckHooks, executeInitAndCheckHooks} from '../hooks';
|
import {executeCheckHooks, executeInitAndCheckHooks} from '../hooks';
|
||||||
import {FLAGS, HEADER_OFFSET, InitPhaseState, LView, LViewFlags, TVIEW} from '../interfaces/view';
|
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.
|
* 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 && assertGreaterThan(index, -1, 'Invalid index');
|
||||||
ngDevMode && assertDataInRange(lView, index + HEADER_OFFSET);
|
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.
|
// 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
|
// PERF WARNING: do NOT extract this to a separate function without running benchmarks
|
||||||
if (!checkNoChangesMode) {
|
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
|
// 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
|
// 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
|
// 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']);
|
setDirectiveStylingInput(tNode.classes, lView, tNode.inputs !['class']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasStyleInput(tNode) && tNode.styles) {
|
if (hasStyleInput(tNode)) {
|
||||||
setDirectiveStylingInput(tNode.styles, lView, tNode.inputs !['style']);
|
setDirectiveStylingInput(tNode.styles, lView, tNode.inputs !['style']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,11 +236,12 @@ export function ɵɵelementHostAttrs(attrs: TAttributes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setDirectiveStylingInput(
|
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
|
// older versions of Angular treat the input as `null` in the
|
||||||
// event that the value does not exist at all. For this reason
|
// event that the value does not exist at all. For this reason
|
||||||
// we can't have a styling value be an empty string.
|
// 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
|
// 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
|
// 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 {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 {assertNodeOfPossibleTypes} from '../node_assert';
|
||||||
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
|
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 {renderStylingMap} from '../styling_next/bindings';
|
||||||
|
import {resetStylingState} from '../styling_next/state';
|
||||||
import {NO_CHANGE} from '../tokens';
|
import {NO_CHANGE} from '../tokens';
|
||||||
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../util/attrs_utils';
|
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../util/attrs_utils';
|
||||||
import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils';
|
import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils';
|
||||||
|
@ -86,16 +87,18 @@ export function setHostBindings(tView: TView, viewData: LView): void {
|
||||||
} else {
|
} else {
|
||||||
// If it's not a number, it's a host binding function that needs to be executed.
|
// If it's not a number, it's a host binding function that needs to be executed.
|
||||||
if (instruction !== null) {
|
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
|
// Each directive gets a uniqueId value that is the same for both
|
||||||
// create and update calls when the hostBindings function is called. The
|
// create and update calls when the hostBindings function is called. The
|
||||||
// directive uniqueId is not set anywhere--it is just incremented between
|
// directive uniqueId is not set anywhere--it is just incremented between
|
||||||
// each hostBindings call and is useful for helping instruction code
|
// each hostBindings call and is useful for helping instruction code
|
||||||
// uniquely determine which directive is currently active when executed.
|
// 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();
|
incrementActiveDirectiveId();
|
||||||
|
|
||||||
|
viewData[BINDING_INDEX] = bindingRootIndex;
|
||||||
|
const hostCtx = unwrapRNode(viewData[currentDirectiveIndex]);
|
||||||
|
instruction(RenderFlags.Update, hostCtx, currentElementIndex);
|
||||||
}
|
}
|
||||||
currentDirectiveIndex++;
|
currentDirectiveIndex++;
|
||||||
}
|
}
|
||||||
|
@ -503,6 +506,12 @@ function executeTemplate<T>(
|
||||||
}
|
}
|
||||||
templateFn(rf, context);
|
templateFn(rf, context);
|
||||||
} finally {
|
} finally {
|
||||||
|
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
|
||||||
|
executeElementExitFn();
|
||||||
|
}
|
||||||
|
if (hasActiveElementFlag(ActiveElementFlags.ResetStylesOnExit)) {
|
||||||
|
resetStylingState();
|
||||||
|
}
|
||||||
setSelectedIndex(prevSelectedIndex);
|
setSelectedIndex(prevSelectedIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1086,14 +1095,10 @@ function invokeDirectivesHostBindings(tView: TView, viewData: LView, tNode: TNod
|
||||||
const def = tView.data[i] as DirectiveDef<any>;
|
const def = tView.data[i] as DirectiveDef<any>;
|
||||||
const directive = viewData[i];
|
const directive = viewData[i];
|
||||||
if (def.hostBindings) {
|
if (def.hostBindings) {
|
||||||
invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstTemplatePass);
|
// 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.
|
||||||
// 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.
|
|
||||||
incrementActiveDirectiveId();
|
incrementActiveDirectiveId();
|
||||||
|
invokeHostBindingsInCreationMode(def, expando, directive, tNode, firstTemplatePass);
|
||||||
} else if (firstTemplatePass) {
|
} else if (firstTemplatePass) {
|
||||||
expando.push(null);
|
expando.push(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,6 @@ export const angularCoreEnv: {[name: string]: Function} =
|
||||||
'ɵɵclassMapInterpolate7': r3.ɵɵclassMapInterpolate7,
|
'ɵɵclassMapInterpolate7': r3.ɵɵclassMapInterpolate7,
|
||||||
'ɵɵclassMapInterpolate8': r3.ɵɵclassMapInterpolate8,
|
'ɵɵclassMapInterpolate8': r3.ɵɵclassMapInterpolate8,
|
||||||
'ɵɵclassMapInterpolateV': r3.ɵɵclassMapInterpolateV,
|
'ɵɵclassMapInterpolateV': r3.ɵɵclassMapInterpolateV,
|
||||||
'ɵɵstyling': r3.ɵɵstyling,
|
|
||||||
'ɵɵstyleMap': r3.ɵɵstyleMap,
|
'ɵɵstyleMap': r3.ɵɵstyleMap,
|
||||||
'ɵɵstyleProp': r3.ɵɵstyleProp,
|
'ɵɵstyleProp': r3.ɵɵstyleProp,
|
||||||
'ɵɵstylePropInterpolate1': r3.ɵɵstylePropInterpolate1,
|
'ɵɵstylePropInterpolate1': r3.ɵɵstylePropInterpolate1,
|
||||||
|
@ -129,7 +128,6 @@ export const angularCoreEnv: {[name: string]: Function} =
|
||||||
'ɵɵstylePropInterpolate8': r3.ɵɵstylePropInterpolate8,
|
'ɵɵstylePropInterpolate8': r3.ɵɵstylePropInterpolate8,
|
||||||
'ɵɵstylePropInterpolateV': r3.ɵɵstylePropInterpolateV,
|
'ɵɵstylePropInterpolateV': r3.ɵɵstylePropInterpolateV,
|
||||||
'ɵɵstyleSanitizer': r3.ɵɵstyleSanitizer,
|
'ɵɵstyleSanitizer': r3.ɵɵstyleSanitizer,
|
||||||
'ɵɵstylingApply': r3.ɵɵstylingApply,
|
|
||||||
'ɵɵclassProp': r3.ɵɵclassProp,
|
'ɵɵclassProp': r3.ɵɵclassProp,
|
||||||
'ɵɵselect': r3.ɵɵselect,
|
'ɵɵselect': r3.ɵɵselect,
|
||||||
'ɵɵadvance': r3.ɵɵadvance,
|
'ɵɵadvance': r3.ɵɵadvance,
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {assertLViewOrUndefined} from './assert';
|
||||||
import {ComponentDef, DirectiveDef} from './interfaces/definition';
|
import {ComponentDef, DirectiveDef} from './interfaces/definition';
|
||||||
import {TElementNode, TNode, TViewNode} from './interfaces/node';
|
import {TElementNode, TNode, TViewNode} from './interfaces/node';
|
||||||
import {CONTEXT, DECLARATION_VIEW, LView, OpaqueViewState, TVIEW} from './interfaces/view';
|
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`
|
* The reason why this value is `1` instead of `0` is because the `0`
|
||||||
* value is reserved for the template.
|
* value is reserved for the template.
|
||||||
*/
|
*/
|
||||||
const MIN_DIRECTIVE_ID = 1;
|
let activeDirectiveId = 0;
|
||||||
|
|
||||||
let activeDirectiveId = MIN_DIRECTIVE_ID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
* Determines whether or not a flag is currently set for the active element.
|
||||||
*
|
|
||||||
* 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).
|
|
||||||
*/
|
*/
|
||||||
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
|
* Sets the active directive host element and resets the directive id value
|
||||||
|
@ -163,12 +170,39 @@ let activeDirectiveSuperClassHeight = 0;
|
||||||
* the directive/component instance lives
|
* the directive/component instance lives
|
||||||
*/
|
*/
|
||||||
export function setActiveHostElement(elementIndex: number | null = null) {
|
export function setActiveHostElement(elementIndex: number | null = null) {
|
||||||
if (_selectedIndex !== elementIndex) {
|
if (getSelectedIndex() !== elementIndex) {
|
||||||
setSelectedIndex(elementIndex === null ? -1 : elementIndex);
|
if (hasActiveElementFlag(ActiveElementFlags.RunExitFn)) {
|
||||||
activeDirectiveId = elementIndex === null ? 0 : MIN_DIRECTIVE_ID;
|
executeElementExitFn();
|
||||||
activeDirectiveSuperClassDepthPosition = 0;
|
|
||||||
activeDirectiveSuperClassHeight = 0;
|
|
||||||
}
|
}
|
||||||
|
if (hasActiveElementFlag(ActiveElementFlags.ResetStylesOnExit)) {
|
||||||
|
resetStylingState();
|
||||||
|
}
|
||||||
|
setSelectedIndex(elementIndex === null ? -1 : elementIndex);
|
||||||
|
activeDirectiveId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _elementExitFn: Function|null = null;
|
||||||
|
export function executeElementExitFn() {
|
||||||
|
_elementExitFn !();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -211,71 +245,12 @@ export function getActiveDirectiveId() {
|
||||||
* different set of directives).
|
* different set of directives).
|
||||||
*/
|
*/
|
||||||
export function incrementActiveDirectiveId() {
|
export function incrementActiveDirectiveId() {
|
||||||
activeDirectiveId += 1 + activeDirectiveSuperClassHeight;
|
// Each directive gets a uniqueId value that is the same for both
|
||||||
|
// create and update calls when the hostBindings function is called. The
|
||||||
// because we are dealing with a new directive this
|
// directive uniqueId is not set anywhere--it is just incremented between
|
||||||
// means we have exited out of the inheritance chain
|
// each hostBindings call and is useful for helping instruction code
|
||||||
activeDirectiveSuperClassDepthPosition = 0;
|
// uniquely determine which directive is currently active when executed.
|
||||||
activeDirectiveSuperClassHeight = 0;
|
activeDirectiveId += 1;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -447,10 +422,10 @@ export function resetComponentState() {
|
||||||
elementDepthCount = 0;
|
elementDepthCount = 0;
|
||||||
bindingsEnabled = true;
|
bindingsEnabled = true;
|
||||||
setCurrentStyleSanitizer(null);
|
setCurrentStyleSanitizer(null);
|
||||||
resetAllStylingState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let _selectedIndex = -1;
|
/* tslint:disable */
|
||||||
|
let _selectedIndex = -1 << ActiveElementFlags.Size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the most recent index passed to {@link select}
|
* Gets the most recent index passed to {@link select}
|
||||||
|
@ -459,7 +434,7 @@ let _selectedIndex = -1;
|
||||||
* current `LView` to act on.
|
* current `LView` to act on.
|
||||||
*/
|
*/
|
||||||
export function getSelectedIndex() {
|
export function getSelectedIndex() {
|
||||||
return _selectedIndex;
|
return _selectedIndex >> ActiveElementFlags.Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -467,13 +442,12 @@ export function getSelectedIndex() {
|
||||||
*
|
*
|
||||||
* Used with {@link property} instruction (and more in the future) to identify the index in the
|
* Used with {@link property} instruction (and more in the future) to identify the index in the
|
||||||
* current `LView` to act on.
|
* 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) {
|
export function setSelectedIndex(index: number) {
|
||||||
_selectedIndex = index;
|
_selectedIndex = index << ActiveElementFlags.Size;
|
||||||
|
|
||||||
// we have now jumped to another element
|
|
||||||
// therefore the state is stale
|
|
||||||
resetStylingState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,14 +5,14 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {SafeValue} from '../../sanitization/bypass';
|
import {SafeValue, unwrapSafeValue} from '../../sanitization/bypass';
|
||||||
import {StyleSanitizeFn, StyleSanitizeMode} from '../../sanitization/style_sanitizer';
|
import {StyleSanitizeFn, StyleSanitizeMode} from '../../sanitization/style_sanitizer';
|
||||||
import {ProceduralRenderer3, RElement, Renderer3, RendererStyleFlags3, isProceduralRenderer} from '../interfaces/renderer';
|
import {ProceduralRenderer3, RElement, Renderer3, RendererStyleFlags3, isProceduralRenderer} from '../interfaces/renderer';
|
||||||
import {NO_CHANGE} from '../tokens';
|
import {NO_CHANGE} from '../tokens';
|
||||||
|
|
||||||
import {ApplyStylingFn, LStylingData, StylingMapArray, StylingMapArrayIndex, StylingMapsSyncMode, SyncStylingMapsFn, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags} from './interfaces';
|
import {ApplyStylingFn, LStylingData, StylingMapArray, StylingMapArrayIndex, StylingMapsSyncMode, SyncStylingMapsFn, TStylingConfig, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags} from './interfaces';
|
||||||
import {BIT_MASK_START_VALUE, deleteStylingStateFromStorage, getStylingState, resetStylingState, storeStylingState} from './state';
|
import {getStylingState, resetStylingState} from './state';
|
||||||
import {allowStylingFlush, getBindingValue, getGuardMask, getMapProp, getMapValue, getProp, getPropValuesStartPosition, getStylingMapArray, getValuesCount, hasValueChanged, isContextLocked, isSanitizationRequired, isStylingValueDefined, lockContext, setGuardMask, stateIsPersisted} from './util';
|
import {DEFAULT_BINDING_INDEX, DEFAULT_BINDING_VALUE, DEFAULT_GUARD_MASK_VALUE, MAP_BASED_ENTRY_PROP_NAME, getBindingValue, getConfig, getDefaultValue, getGuardMask, getMapProp, getMapValue, getProp, getPropValuesStartPosition, getStylingMapArray, getTotalSources, getValue, getValuesCount, hasConfig, hasValueChanged, isContextLocked, isHostStylingActive, isSanitizationRequired, isStylingValueDefined, lockContext, patchConfig, setDefaultValue, setGuardMask, setMapAsDirty, setValue} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,13 +35,6 @@ import {allowStylingFlush, getBindingValue, getGuardMask, getMapProp, getMapValu
|
||||||
* --------
|
* --------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// The first bit value reflects a map-based binding value's bit.
|
|
||||||
// The reason why it's always activated for every entry in the map
|
|
||||||
// is so that if any map-binding values update then all other prop
|
|
||||||
// based bindings will pass the guard check automatically without
|
|
||||||
// any extra code or flags.
|
|
||||||
export const DEFAULT_GUARD_MASK_VALUE = 0b1;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The guard/update mask bit index location for map-based bindings.
|
* The guard/update mask bit index location for map-based bindings.
|
||||||
*
|
*
|
||||||
|
@ -49,26 +42,6 @@ export const DEFAULT_GUARD_MASK_VALUE = 0b1;
|
||||||
*/
|
*/
|
||||||
const STYLING_INDEX_FOR_MAP_BINDING = 0;
|
const STYLING_INDEX_FOR_MAP_BINDING = 0;
|
||||||
|
|
||||||
/**
|
|
||||||
* Default fallback value for a styling binding.
|
|
||||||
*
|
|
||||||
* A value of `null` is used here which signals to the styling algorithm that
|
|
||||||
* the styling value is not present. This way if there are no other values
|
|
||||||
* detected then it will be removed once the style/class property is dirty and
|
|
||||||
* diffed within the styling algorithm present in `flushStyling`.
|
|
||||||
*/
|
|
||||||
const DEFAULT_BINDING_VALUE = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default size count value for a new entry in a context.
|
|
||||||
*
|
|
||||||
* A value of `1` is used here because each entry in the context has a default
|
|
||||||
* property.
|
|
||||||
*/
|
|
||||||
const DEFAULT_SIZE_VALUE = 1;
|
|
||||||
|
|
||||||
let deferredBindingQueue: (TStylingContext | number | string | null | boolean)[] = [];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits a class-based binding and updates the new value (if changed).
|
* Visits a class-based binding and updates the new value (if changed).
|
||||||
*
|
*
|
||||||
|
@ -79,23 +52,25 @@ let deferredBindingQueue: (TStylingContext | number | string | null | boolean)[]
|
||||||
* state each time it's called (which then allows the `TStylingContext`
|
* state each time it's called (which then allows the `TStylingContext`
|
||||||
* and the bit mask values to be in sync).
|
* and the bit mask values to be in sync).
|
||||||
*/
|
*/
|
||||||
export function updateClassBinding(
|
export function updateClassViaContext(
|
||||||
context: TStylingContext, data: LStylingData, element: RElement, prop: string | null,
|
context: TStylingContext, data: LStylingData, element: RElement, directiveIndex: number,
|
||||||
bindingIndex: number, value: boolean | string | null | undefined | StylingMapArray | NO_CHANGE,
|
prop: string | null, bindingIndex: number,
|
||||||
deferRegistration: boolean, forceUpdate: boolean): boolean {
|
value: boolean | string | null | undefined | StylingMapArray | NO_CHANGE,
|
||||||
|
forceUpdate?: boolean): boolean {
|
||||||
const isMapBased = !prop;
|
const isMapBased = !prop;
|
||||||
const state = getStylingState(element, stateIsPersisted(context));
|
const state = getStylingState(element, directiveIndex);
|
||||||
const index = isMapBased ? STYLING_INDEX_FOR_MAP_BINDING : state.classesIndex++;
|
const countIndex = isMapBased ? STYLING_INDEX_FOR_MAP_BINDING : state.classesIndex++;
|
||||||
if (value !== NO_CHANGE) {
|
if (value !== NO_CHANGE) {
|
||||||
const updated = updateBindingData(
|
const updated = updateBindingData(
|
||||||
context, data, index, prop, bindingIndex, value, deferRegistration, forceUpdate, false);
|
context, data, countIndex, state.sourceIndex, prop, bindingIndex, value, forceUpdate,
|
||||||
|
false);
|
||||||
if (updated || forceUpdate) {
|
if (updated || forceUpdate) {
|
||||||
// We flip the bit in the bitMask to reflect that the binding
|
// We flip the bit in the bitMask to reflect that the binding
|
||||||
// at the `index` slot has changed. This identifies to the flushing
|
// at the `index` slot has changed. This identifies to the flushing
|
||||||
// phase that the bindings for this particular CSS class need to be
|
// phase that the bindings for this particular CSS class need to be
|
||||||
// applied again because on or more of the bindings for the CSS
|
// applied again because on or more of the bindings for the CSS
|
||||||
// class have changed.
|
// class have changed.
|
||||||
state.classesBitMask |= 1 << index;
|
state.classesBitMask |= 1 << countIndex;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,19 +87,20 @@ export function updateClassBinding(
|
||||||
* state each time it's called (which then allows the `TStylingContext`
|
* state each time it's called (which then allows the `TStylingContext`
|
||||||
* and the bit mask values to be in sync).
|
* and the bit mask values to be in sync).
|
||||||
*/
|
*/
|
||||||
export function updateStyleBinding(
|
export function updateStyleViaContext(
|
||||||
context: TStylingContext, data: LStylingData, element: RElement, prop: string | null,
|
context: TStylingContext, data: LStylingData, element: RElement, directiveIndex: number,
|
||||||
bindingIndex: number,
|
prop: string | null, bindingIndex: number,
|
||||||
value: string | number | SafeValue | null | undefined | StylingMapArray | NO_CHANGE,
|
value: string | number | SafeValue | null | undefined | StylingMapArray | NO_CHANGE,
|
||||||
sanitizer: StyleSanitizeFn | null, deferRegistration: boolean, forceUpdate: boolean): boolean {
|
sanitizer: StyleSanitizeFn | null, forceUpdate?: boolean): boolean {
|
||||||
const isMapBased = !prop;
|
const isMapBased = !prop;
|
||||||
const state = getStylingState(element, stateIsPersisted(context));
|
const state = getStylingState(element, directiveIndex);
|
||||||
const index = isMapBased ? STYLING_INDEX_FOR_MAP_BINDING : state.stylesIndex++;
|
const countIndex = isMapBased ? STYLING_INDEX_FOR_MAP_BINDING : state.stylesIndex++;
|
||||||
if (value !== NO_CHANGE) {
|
if (value !== NO_CHANGE) {
|
||||||
const sanitizationRequired = isMapBased ||
|
const sanitizationRequired = isMapBased ?
|
||||||
|
true :
|
||||||
(sanitizer ? sanitizer(prop !, null, StyleSanitizeMode.ValidateProperty) : false);
|
(sanitizer ? sanitizer(prop !, null, StyleSanitizeMode.ValidateProperty) : false);
|
||||||
const updated = updateBindingData(
|
const updated = updateBindingData(
|
||||||
context, data, index, prop, bindingIndex, value, deferRegistration, forceUpdate,
|
context, data, countIndex, state.sourceIndex, prop, bindingIndex, value, forceUpdate,
|
||||||
sanitizationRequired);
|
sanitizationRequired);
|
||||||
if (updated || forceUpdate) {
|
if (updated || forceUpdate) {
|
||||||
// We flip the bit in the bitMask to reflect that the binding
|
// We flip the bit in the bitMask to reflect that the binding
|
||||||
|
@ -132,7 +108,7 @@ export function updateStyleBinding(
|
||||||
// phase that the bindings for this particular property need to be
|
// phase that the bindings for this particular property need to be
|
||||||
// applied again because on or more of the bindings for the CSS
|
// applied again because on or more of the bindings for the CSS
|
||||||
// property have changed.
|
// property have changed.
|
||||||
state.stylesBitMask |= 1 << index;
|
state.stylesBitMask |= 1 << countIndex;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,8 +120,6 @@ export function updateStyleBinding(
|
||||||
*
|
*
|
||||||
* This function is designed to be called from `updateStyleBinding` and `updateClassBinding`.
|
* This function is designed to be called from `updateStyleBinding` and `updateClassBinding`.
|
||||||
* If called during the first update pass, the binding will be registered in the context.
|
* If called during the first update pass, the binding will be registered in the context.
|
||||||
* If the binding does get registered and the `deferRegistration` flag is true then the
|
|
||||||
* binding data will be queued up until the context is later flushed in `applyStyling`.
|
|
||||||
*
|
*
|
||||||
* This function will also update binding slot in the provided `LStylingData` with the
|
* This function will also update binding slot in the provided `LStylingData` with the
|
||||||
* new binding entry (if it has changed).
|
* new binding entry (if it has changed).
|
||||||
|
@ -153,74 +127,92 @@ export function updateStyleBinding(
|
||||||
* @returns whether or not the binding value was updated in the `LStylingData`.
|
* @returns whether or not the binding value was updated in the `LStylingData`.
|
||||||
*/
|
*/
|
||||||
function updateBindingData(
|
function updateBindingData(
|
||||||
context: TStylingContext, data: LStylingData, counterIndex: number, prop: string | null,
|
context: TStylingContext, data: LStylingData, counterIndex: number, sourceIndex: number,
|
||||||
bindingIndex: number,
|
prop: string | null, bindingIndex: number,
|
||||||
value: string | SafeValue | number | boolean | null | undefined | StylingMapArray | NO_CHANGE,
|
value: string | SafeValue | number | boolean | null | undefined | StylingMapArray,
|
||||||
deferRegistration: boolean, forceUpdate: boolean, sanitizationRequired: boolean): boolean {
|
forceUpdate?: boolean, sanitizationRequired?: boolean): boolean {
|
||||||
if (!isContextLocked(context)) {
|
const hostBindingsMode = isHostStylingActive(sourceIndex);
|
||||||
if (deferRegistration) {
|
if (!isContextLocked(context, hostBindingsMode)) {
|
||||||
deferBindingRegistration(context, counterIndex, prop, bindingIndex, sanitizationRequired);
|
|
||||||
} else {
|
|
||||||
deferredBindingQueue.length && flushDeferredBindings();
|
|
||||||
|
|
||||||
// this will only happen during the first update pass of the
|
// this will only happen during the first update pass of the
|
||||||
// context. The reason why we can't use `tNode.firstTemplatePass`
|
// context. The reason why we can't use `tNode.firstTemplatePass`
|
||||||
// here is because its not guaranteed to be true when the first
|
// here is because its not guaranteed to be true when the first
|
||||||
// update pass is executed (remember that all styling instructions
|
// update pass is executed (remember that all styling instructions
|
||||||
// are run in the update phase, and, as a result, are no more
|
// are run in the update phase, and, as a result, are no more
|
||||||
// styling instructions that are run in the creation phase).
|
// styling instructions that are run in the creation phase).
|
||||||
registerBinding(context, counterIndex, prop, bindingIndex, sanitizationRequired);
|
registerBinding(context, counterIndex, sourceIndex, prop, bindingIndex, sanitizationRequired);
|
||||||
}
|
patchConfig(
|
||||||
|
context,
|
||||||
|
hostBindingsMode ? TStylingConfig.HasHostBindings : TStylingConfig.HasTemplateBindings);
|
||||||
|
patchConfig(context, prop ? TStylingConfig.HasPropBindings : TStylingConfig.HasMapBindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
const changed = forceUpdate || hasValueChanged(data[bindingIndex], value);
|
const changed = forceUpdate || hasValueChanged(data[bindingIndex], value);
|
||||||
if (changed) {
|
if (changed) {
|
||||||
data[bindingIndex] = value;
|
setValue(data, bindingIndex, value);
|
||||||
|
const doSetValuesAsStale = (getConfig(context) & TStylingConfig.HasHostBindings) &&
|
||||||
|
!hostBindingsMode && (prop ? !value : true);
|
||||||
|
if (doSetValuesAsStale) {
|
||||||
|
renderHostBindingsAsStale(context, data, prop, !prop);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedules a binding registration to be run at a later point.
|
* Iterates over all host-binding values for the given `prop` value in the context and sets their
|
||||||
|
* corresponding binding values to `null`.
|
||||||
*
|
*
|
||||||
* The reasoning for this feature is to ensure that styling
|
* Whenever a template binding changes its value to `null`, all host-binding values should be
|
||||||
* bindings are registered in the correct order for when
|
* re-applied
|
||||||
* directives/components have a super/sub class inheritance
|
* to the element when the host bindings are evaluated. This may not always happen in the event
|
||||||
* chains. Each directive's styling bindings must be
|
* that none of the bindings changed within the host bindings code. For this reason this function
|
||||||
* registered into the context in reverse order. Therefore all
|
* is expected to be called each time a template binding becomes falsy or when a map-based template
|
||||||
* bindings will be buffered in reverse order and then applied
|
* binding changes.
|
||||||
* after the inheritance chain exits.
|
|
||||||
*/
|
*/
|
||||||
function deferBindingRegistration(
|
function renderHostBindingsAsStale(
|
||||||
context: TStylingContext, counterIndex: number, prop: string | null, bindingIndex: number,
|
context: TStylingContext, data: LStylingData, prop: string | null, isMapBased: boolean): void {
|
||||||
sanitizationRequired: boolean) {
|
const valuesCount = getValuesCount(context);
|
||||||
deferredBindingQueue.unshift(context, counterIndex, prop, bindingIndex, sanitizationRequired);
|
|
||||||
|
if (hasConfig(context, TStylingConfig.HasPropBindings)) {
|
||||||
|
const itemsPerRow = TStylingContextIndex.BindingsStartOffset + valuesCount;
|
||||||
|
|
||||||
|
let i = TStylingContextIndex.ValuesStartPosition;
|
||||||
|
while (i < context.length) {
|
||||||
|
if (getProp(context, i) === prop) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i += itemsPerRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const bindingsStart = i + TStylingContextIndex.BindingsStartOffset;
|
||||||
* Flushes the collection of deferred bindings and causes each entry
|
const valuesStart = bindingsStart + 1; // the first column is template bindings
|
||||||
* to be registered into the context.
|
const valuesEnd = bindingsStart + valuesCount - 1;
|
||||||
*/
|
|
||||||
function flushDeferredBindings() {
|
for (let i = valuesStart; i < valuesEnd; i++) {
|
||||||
let i = 0;
|
const bindingIndex = context[i] as number;
|
||||||
while (i < deferredBindingQueue.length) {
|
if (bindingIndex !== 0) {
|
||||||
const context = deferredBindingQueue[i++] as TStylingContext;
|
setValue(data, bindingIndex, null);
|
||||||
const count = deferredBindingQueue[i++] as number;
|
}
|
||||||
const prop = deferredBindingQueue[i++] as string;
|
}
|
||||||
const bindingIndex = deferredBindingQueue[i++] as number | null;
|
}
|
||||||
const sanitizationRequired = deferredBindingQueue[i++] as boolean;
|
|
||||||
registerBinding(context, count, prop, bindingIndex, sanitizationRequired);
|
if (hasConfig(context, TStylingConfig.HasMapBindings)) {
|
||||||
|
const bindingsStart =
|
||||||
|
TStylingContextIndex.ValuesStartPosition + TStylingContextIndex.BindingsStartOffset;
|
||||||
|
const valuesStart = bindingsStart + 1; // the first column is template bindings
|
||||||
|
const valuesEnd = bindingsStart + valuesCount - 1;
|
||||||
|
for (let i = valuesStart; i < valuesEnd; i++) {
|
||||||
|
const stylingMap = getValue<StylingMapArray>(data, context[i] as number);
|
||||||
|
if (stylingMap) {
|
||||||
|
setMapAsDirty(stylingMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
deferredBindingQueue.length = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the provided binding (prop + bindingIndex) into the context.
|
* Registers the provided binding (prop + bindingIndex) into the context.
|
||||||
*
|
*
|
||||||
* This function is shared between bindings that are assigned immediately
|
|
||||||
* (via `updateBindingData`) and at a deferred stage. When called, it will
|
|
||||||
* figure out exactly where to place the binding data in the context.
|
|
||||||
*
|
|
||||||
* It is needed because it will either update or insert a styling property
|
* It is needed because it will either update or insert a styling property
|
||||||
* into the context at the correct spot.
|
* into the context at the correct spot.
|
||||||
*
|
*
|
||||||
|
@ -246,61 +238,79 @@ function flushDeferredBindings() {
|
||||||
* value.
|
* value.
|
||||||
*
|
*
|
||||||
* Note that this function is also used for map-based styling bindings. They are treated
|
* Note that this function is also used for map-based styling bindings. They are treated
|
||||||
* much the same as prop-based bindings, but, because they do not have a property value
|
* much the same as prop-based bindings, but, their property name value is set as `[MAP]`.
|
||||||
* (since it's a map), all map-based entries are stored in an already populated area of
|
|
||||||
* the context at the top (which is reserved for map-based entries).
|
|
||||||
*/
|
*/
|
||||||
export function registerBinding(
|
export function registerBinding(
|
||||||
context: TStylingContext, countId: number, prop: string | null,
|
context: TStylingContext, countId: number, sourceIndex: number, prop: string | null,
|
||||||
bindingValue: number | null | string | boolean, sanitizationRequired?: boolean): boolean {
|
bindingValue: number | null | string | boolean, sanitizationRequired?: boolean): void {
|
||||||
let registered = false;
|
|
||||||
if (prop) {
|
|
||||||
// prop-based bindings (e.g `<div [style.width]="w" [class.foo]="f">`)
|
|
||||||
let found = false;
|
let found = false;
|
||||||
let i = getPropValuesStartPosition(context);
|
prop = prop || MAP_BASED_ENTRY_PROP_NAME;
|
||||||
while (i < context.length) {
|
|
||||||
const valuesCount = getValuesCount(context, i);
|
const total = getTotalSources(context);
|
||||||
const p = getProp(context, i);
|
|
||||||
found = prop <= p;
|
// if a new source is detected then a new column needs to be allocated into
|
||||||
if (found) {
|
// the styling context. The column is basically a new allocation of binding
|
||||||
|
// sources that will be available to each property.
|
||||||
|
if (sourceIndex >= total) {
|
||||||
|
addNewSourceColumn(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isBindingIndexValue = typeof bindingValue === 'number';
|
||||||
|
const entriesPerRow = TStylingContextIndex.BindingsStartOffset + getValuesCount(context);
|
||||||
|
let i = TStylingContextIndex.ValuesStartPosition;
|
||||||
|
|
||||||
// all style/class bindings are sorted by property name
|
// all style/class bindings are sorted by property name
|
||||||
|
while (i < context.length) {
|
||||||
|
const p = getProp(context, i);
|
||||||
|
if (prop <= p) {
|
||||||
if (prop < p) {
|
if (prop < p) {
|
||||||
allocateNewContextEntry(context, i, prop, sanitizationRequired);
|
allocateNewContextEntry(context, i, prop, sanitizationRequired);
|
||||||
|
} else if (isBindingIndexValue) {
|
||||||
|
patchConfig(context, TStylingConfig.HasCollisions);
|
||||||
}
|
}
|
||||||
addBindingIntoContext(context, false, i, bindingValue, countId);
|
addBindingIntoContext(context, i, bindingValue, countId, sourceIndex);
|
||||||
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i += TStylingContextIndex.BindingsStartOffset + valuesCount;
|
i += entriesPerRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
allocateNewContextEntry(context, context.length, prop, sanitizationRequired);
|
allocateNewContextEntry(context, context.length, prop, sanitizationRequired);
|
||||||
addBindingIntoContext(context, false, i, bindingValue, countId);
|
addBindingIntoContext(context, i, bindingValue, countId, sourceIndex);
|
||||||
registered = true;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// map-based bindings (e.g `<div [style]="s" [class]="{className:true}">`)
|
|
||||||
// there is no need to allocate the map-based binding region into the context
|
|
||||||
// since it is already there when the context is first created.
|
|
||||||
addBindingIntoContext(
|
|
||||||
context, true, TStylingContextIndex.MapBindingsPosition, bindingValue, countId);
|
|
||||||
registered = true;
|
|
||||||
}
|
|
||||||
return registered;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a new row into the provided `TStylingContext` and assigns the provided `prop` value as
|
||||||
|
* the property entry.
|
||||||
|
*/
|
||||||
function allocateNewContextEntry(
|
function allocateNewContextEntry(
|
||||||
context: TStylingContext, index: number, prop: string, sanitizationRequired?: boolean) {
|
context: TStylingContext, index: number, prop: string, sanitizationRequired?: boolean): void {
|
||||||
// 1,2: splice index locations
|
|
||||||
// 3: each entry gets a config value (guard mask + flags)
|
|
||||||
// 4. each entry gets a size value (which is always one because there is always a default binding
|
|
||||||
// value)
|
|
||||||
// 5. the property that is getting allocated into the context
|
|
||||||
// 6. the default binding value (usually `null`)
|
|
||||||
const config = sanitizationRequired ? TStylingContextPropConfigFlags.SanitizationRequired :
|
const config = sanitizationRequired ? TStylingContextPropConfigFlags.SanitizationRequired :
|
||||||
TStylingContextPropConfigFlags.Default;
|
TStylingContextPropConfigFlags.Default;
|
||||||
context.splice(index, 0, config, DEFAULT_SIZE_VALUE, prop, DEFAULT_BINDING_VALUE);
|
context.splice(
|
||||||
setGuardMask(context, index, DEFAULT_GUARD_MASK_VALUE);
|
index, 0,
|
||||||
|
config, // 1) config value
|
||||||
|
DEFAULT_GUARD_MASK_VALUE, // 2) template bit mask
|
||||||
|
DEFAULT_GUARD_MASK_VALUE, // 3) host bindings bit mask
|
||||||
|
prop, // 4) prop value (e.g. `width`, `myClass`, etc...)
|
||||||
|
);
|
||||||
|
|
||||||
|
index += 4; // the 4 values above
|
||||||
|
|
||||||
|
// 5...) default binding index for the template value
|
||||||
|
// depending on how many sources already exist in the context,
|
||||||
|
// multiple default index entries may need to be inserted for
|
||||||
|
// the new value in the context.
|
||||||
|
const totalBindingsPerEntry = getTotalSources(context);
|
||||||
|
for (let i = 0; i < totalBindingsPerEntry; i++) {
|
||||||
|
context.splice(index, 0, DEFAULT_BINDING_INDEX);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6) default binding value for the new entry
|
||||||
|
context.splice(index, 0, DEFAULT_BINDING_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -316,50 +326,47 @@ function allocateNewContextEntry(
|
||||||
*
|
*
|
||||||
* - Otherwise the binding value will update the default value for the property
|
* - Otherwise the binding value will update the default value for the property
|
||||||
* and this will only happen if the default value is `null`.
|
* and this will only happen if the default value is `null`.
|
||||||
*
|
|
||||||
* Note that this function also handles map-based bindings and will insert them
|
|
||||||
* at the top of the context.
|
|
||||||
*/
|
*/
|
||||||
function addBindingIntoContext(
|
function addBindingIntoContext(
|
||||||
context: TStylingContext, isMapBased: boolean, index: number,
|
context: TStylingContext, index: number, bindingValue: number | string | boolean | null,
|
||||||
bindingValue: number | string | boolean | null, countId: number) {
|
bitIndex: number, sourceIndex: number) {
|
||||||
const valuesCount = getValuesCount(context, index);
|
|
||||||
|
|
||||||
const firstValueIndex = index + TStylingContextIndex.BindingsStartOffset;
|
|
||||||
let lastValueIndex = firstValueIndex + valuesCount;
|
|
||||||
if (!isMapBased) {
|
|
||||||
// prop-based values all have default values, but map-based entries do not.
|
|
||||||
// we want to access the index for the default value in this case and not just
|
|
||||||
// the bindings...
|
|
||||||
lastValueIndex--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof bindingValue === 'number') {
|
if (typeof bindingValue === 'number') {
|
||||||
// the loop here will check to see if the binding already exists
|
const hostBindingsMode = isHostStylingActive(sourceIndex);
|
||||||
// for the property in the context. Why? The reason for this is
|
const cellIndex = index + TStylingContextIndex.BindingsStartOffset + sourceIndex;
|
||||||
// because the styling context is not "locked" until the first
|
context[cellIndex] = bindingValue;
|
||||||
// flush has occurred. This means that if a repeated element
|
const updatedBitMask = getGuardMask(context, index, hostBindingsMode) | (1 << bitIndex);
|
||||||
// registers its styling bindings then it will register each
|
setGuardMask(context, index, updatedBitMask, hostBindingsMode);
|
||||||
// binding more than once (since its duplicated). This check
|
} else if (bindingValue !== null && getDefaultValue(context, index) === null) {
|
||||||
// will prevent that from happening. Note that this only happens
|
setDefaultValue(context, index, bindingValue);
|
||||||
// when a binding is first encountered and not each time it is
|
}
|
||||||
// updated.
|
|
||||||
for (let i = firstValueIndex; i <= lastValueIndex; i++) {
|
|
||||||
const indexAtPosition = context[i];
|
|
||||||
if (indexAtPosition === bindingValue) return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.splice(lastValueIndex, 0, bindingValue);
|
/**
|
||||||
(context[index + TStylingContextIndex.ValuesCountOffset] as number)++;
|
* Registers a new column into the provided `TStylingContext`.
|
||||||
|
*
|
||||||
|
* If and when a new source is detected then a new column needs to
|
||||||
|
* be allocated into the styling context. The column is basically
|
||||||
|
* a new allocation of binding sources that will be available to each
|
||||||
|
* property.
|
||||||
|
*
|
||||||
|
* Each column that exists in the styling context resembles a styling
|
||||||
|
* source. A styling source an either be the template or one or more
|
||||||
|
* components or directives all containing styling host bindings.
|
||||||
|
*/
|
||||||
|
function addNewSourceColumn(context: TStylingContext): void {
|
||||||
|
// we use -1 here because we want to insert right before the last value (the default value)
|
||||||
|
const insertOffset = TStylingContextIndex.BindingsStartOffset + getValuesCount(context) - 1;
|
||||||
|
|
||||||
// now that a new binding index has been added to the property
|
let index = TStylingContextIndex.ValuesStartPosition;
|
||||||
// the guard mask bit value (at the `countId` position) needs
|
while (index < context.length) {
|
||||||
// to be included into the existing mask value.
|
index += insertOffset;
|
||||||
const guardMask = getGuardMask(context, index) | (1 << countId);
|
context.splice(index++, 0, DEFAULT_BINDING_INDEX);
|
||||||
setGuardMask(context, index, guardMask);
|
|
||||||
} else if (bindingValue !== null && context[lastValueIndex] == null) {
|
// the value was inserted just before the default value, but the
|
||||||
context[lastValueIndex] = bindingValue;
|
// next entry in the context starts just after it. Therefore++.
|
||||||
|
index++;
|
||||||
}
|
}
|
||||||
|
context[TStylingContextIndex.TotalSourcesPosition]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -367,24 +374,22 @@ function addBindingIntoContext(
|
||||||
*
|
*
|
||||||
* This function will attempt to flush styling via the provided `classesContext`
|
* This function will attempt to flush styling via the provided `classesContext`
|
||||||
* and `stylesContext` context values. This function is designed to be run from
|
* and `stylesContext` context values. This function is designed to be run from
|
||||||
* the `stylingApply()` instruction (which is run at the very end of styling
|
* the internal `stylingApply` function (which is scheduled to run at the very
|
||||||
* change detection) and will rely on any state values that are set from when
|
* end of change detection for an element if one or more style/class bindings
|
||||||
* any styling bindings update.
|
* were processed) and will rely on any state values that are set from when
|
||||||
|
* any of the styling bindings executed.
|
||||||
*
|
*
|
||||||
* This function may be called multiple times on the same element because it can
|
* This function is designed to be called twice: one when change detection has
|
||||||
* be called from the template code as well as from host bindings. In order for
|
* processed an element within the template bindings (i.e. just as `advance()`
|
||||||
* styling to be successfully flushed to the element (which will only happen once
|
* is called) and when host bindings have been processed. In both cases the
|
||||||
* despite this being called multiple times), the following criteria must be met:
|
* styles and classes in both contexts will be applied to the element, but the
|
||||||
|
* algorithm will selectively decide which bindings to run depending on the
|
||||||
|
* columns in the context. The provided `directiveIndex` value will help the
|
||||||
|
* algorithm determine which bindings to apply: either the template bindings or
|
||||||
|
* the host bindings (see `applyStylingToElement` for more information).
|
||||||
*
|
*
|
||||||
* - `flushStyling` is called from the very last directive that has styling for
|
* Note that once this function is called all temporary styling state data
|
||||||
* the element (see `allowStylingFlush()`).
|
* (i.e. the `bitMask` and `counter` values for styles and classes will be cleared).
|
||||||
* - one or more bindings for classes or styles has updated (this is checked by
|
|
||||||
* examining the classes or styles bit mask).
|
|
||||||
*
|
|
||||||
* If the style and class values are successfully applied to the element then
|
|
||||||
* the temporary state values for the element will be cleared. Otherwise, if
|
|
||||||
* this did not occur then the styling state is persisted (see `state.ts` for
|
|
||||||
* more information on how this works).
|
|
||||||
*/
|
*/
|
||||||
export function flushStyling(
|
export function flushStyling(
|
||||||
renderer: Renderer3 | ProceduralRenderer3 | null, data: LStylingData,
|
renderer: Renderer3 | ProceduralRenderer3 | null, data: LStylingData,
|
||||||
|
@ -392,53 +397,28 @@ export function flushStyling(
|
||||||
element: RElement, directiveIndex: number, styleSanitizer: StyleSanitizeFn | null): void {
|
element: RElement, directiveIndex: number, styleSanitizer: StyleSanitizeFn | null): void {
|
||||||
ngDevMode && ngDevMode.flushStyling++;
|
ngDevMode && ngDevMode.flushStyling++;
|
||||||
|
|
||||||
const persistState = classesContext ? stateIsPersisted(classesContext) :
|
const state = getStylingState(element, directiveIndex);
|
||||||
(stylesContext ? stateIsPersisted(stylesContext) : false);
|
const hostBindingsMode = isHostStylingActive(state.sourceIndex);
|
||||||
const allowFlushClasses = allowStylingFlush(classesContext, directiveIndex);
|
|
||||||
const allowFlushStyles = allowStylingFlush(stylesContext, directiveIndex);
|
|
||||||
|
|
||||||
// deferred bindings are bindings which are scheduled to register with
|
if (stylesContext) {
|
||||||
// the context at a later point. These bindings can only registered when
|
if (!isContextLocked(stylesContext, hostBindingsMode)) {
|
||||||
// the context will be 100% flushed to the element.
|
lockAndFinalizeContext(stylesContext, hostBindingsMode);
|
||||||
if (deferredBindingQueue.length && (allowFlushClasses || allowFlushStyles)) {
|
}
|
||||||
flushDeferredBindings();
|
applyStylingViaContext(
|
||||||
|
stylesContext, renderer, element, data, state.stylesBitMask, setStyle, styleSanitizer,
|
||||||
|
hostBindingsMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = getStylingState(element, persistState);
|
if (classesContext) {
|
||||||
const classesFlushed = maybeApplyStyling(
|
if (!isContextLocked(classesContext, hostBindingsMode)) {
|
||||||
renderer, element, data, classesContext, allowFlushClasses, state.classesBitMask, setClass,
|
lockAndFinalizeContext(classesContext, hostBindingsMode);
|
||||||
null);
|
}
|
||||||
const stylesFlushed = maybeApplyStyling(
|
applyStylingViaContext(
|
||||||
renderer, element, data, stylesContext, allowFlushStyles, state.stylesBitMask, setStyle,
|
classesContext, renderer, element, data, state.classesBitMask, setClass, null,
|
||||||
styleSanitizer);
|
hostBindingsMode);
|
||||||
|
}
|
||||||
|
|
||||||
if (classesFlushed && stylesFlushed) {
|
|
||||||
resetStylingState();
|
resetStylingState();
|
||||||
if (persistState) {
|
|
||||||
deleteStylingStateFromStorage(element);
|
|
||||||
}
|
|
||||||
} else if (persistState) {
|
|
||||||
storeStylingState(element, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybeApplyStyling(
|
|
||||||
renderer: Renderer3 | ProceduralRenderer3 | null, element: RElement, data: LStylingData,
|
|
||||||
context: TStylingContext | null, allowFlush: boolean, bitMask: number,
|
|
||||||
styleSetter: ApplyStylingFn, styleSanitizer: any | null): boolean {
|
|
||||||
if (allowFlush && context) {
|
|
||||||
lockAndFinalizeContext(context);
|
|
||||||
if (contextHasUpdates(context, bitMask)) {
|
|
||||||
ngDevMode && (styleSanitizer ? ngDevMode.stylesApplied++ : ngDevMode.classesApplied++);
|
|
||||||
applyStyling(context !, renderer, element, data, bitMask, styleSetter, styleSanitizer);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return allowFlush;
|
|
||||||
}
|
|
||||||
|
|
||||||
function contextHasUpdates(context: TStylingContext | null, bitMask: number) {
|
|
||||||
return context && bitMask > BIT_MASK_START_VALUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -462,14 +442,50 @@ function contextHasUpdates(context: TStylingContext | null, bitMask: number) {
|
||||||
* be updated each time a host binding applies its static styling values (via `elementHostAttrs`)
|
* be updated each time a host binding applies its static styling values (via `elementHostAttrs`)
|
||||||
* so these values are only read at this point because this is the very last point before the
|
* so these values are only read at this point because this is the very last point before the
|
||||||
* first style/class values are flushed to the element.
|
* first style/class values are flushed to the element.
|
||||||
|
*
|
||||||
|
* Note that the `TStylingContext` styling context contains two locks: one for template bindings
|
||||||
|
* and another for host bindings. Either one of these locks will be set when styling is applied
|
||||||
|
* during the template binding flush and/or during the host bindings flush.
|
||||||
*/
|
*/
|
||||||
function lockAndFinalizeContext(context: TStylingContext): void {
|
function lockAndFinalizeContext(context: TStylingContext, hostBindingsMode: boolean): void {
|
||||||
if (!isContextLocked(context)) {
|
const initialValues = getStylingMapArray(context) !;
|
||||||
const initialValues = getStylingMapArray(context);
|
|
||||||
if (initialValues) {
|
|
||||||
updateInitialStylingOnContext(context, initialValues);
|
updateInitialStylingOnContext(context, initialValues);
|
||||||
|
lockContext(context, hostBindingsMode);
|
||||||
}
|
}
|
||||||
lockContext(context);
|
|
||||||
|
/**
|
||||||
|
* Registers all initial styling entries into the provided context.
|
||||||
|
*
|
||||||
|
* This function will iterate over all entries in the provided `initialStyling` ar}ray and register
|
||||||
|
* them as default (initial) values in the provided context. Initial styling values in a context are
|
||||||
|
* the default values that are to be applied unless overwritten by a binding.
|
||||||
|
*
|
||||||
|
* The reason why this function exists and isn't a part of the context construction is because
|
||||||
|
* host binding is evaluated at a later stage after the element is created. This means that
|
||||||
|
* if a directive or component contains any initial styling code (i.e. `<div class="foo">`)
|
||||||
|
* then that initial styling data can only be applied once the styling for that element
|
||||||
|
* is first applied (at the end of the update phase). Once that happens then the context will
|
||||||
|
* update itself with the complete initial styling for the element.
|
||||||
|
*/
|
||||||
|
function updateInitialStylingOnContext(
|
||||||
|
context: TStylingContext, initialStyling: StylingMapArray): void {
|
||||||
|
// `-1` is used here because all initial styling data is not a apart
|
||||||
|
// of a binding (since it's static)
|
||||||
|
const COUNT_ID_FOR_STYLING = -1;
|
||||||
|
|
||||||
|
let hasInitialStyling = false;
|
||||||
|
for (let i = StylingMapArrayIndex.ValuesStartPosition; i < initialStyling.length;
|
||||||
|
i += StylingMapArrayIndex.TupleSize) {
|
||||||
|
const value = getMapValue(initialStyling, i);
|
||||||
|
if (value) {
|
||||||
|
const prop = getMapProp(initialStyling, i);
|
||||||
|
registerBinding(context, COUNT_ID_FOR_STYLING, 0, prop, value, false);
|
||||||
|
hasInitialStyling = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasInitialStyling) {
|
||||||
|
patchConfig(context, TStylingConfig.HasInitialStyling);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,60 +513,74 @@ function lockAndFinalizeContext(context: TStylingContext): void {
|
||||||
* algorithm works for map-based styling bindings.
|
* algorithm works for map-based styling bindings.
|
||||||
*
|
*
|
||||||
* Note that this function is not designed to be called in isolation (use
|
* Note that this function is not designed to be called in isolation (use
|
||||||
* `applyClasses` and `applyStyles` to actually apply styling values).
|
* the `flushStyling` function so that it can call this function for both
|
||||||
|
* the styles and classes contexts).
|
||||||
*/
|
*/
|
||||||
export function applyStyling(
|
export function applyStylingViaContext(
|
||||||
context: TStylingContext, renderer: Renderer3 | ProceduralRenderer3 | null, element: RElement,
|
context: TStylingContext, renderer: Renderer3 | ProceduralRenderer3 | null, element: RElement,
|
||||||
bindingData: LStylingData, bitMaskValue: number | boolean, applyStylingFn: ApplyStylingFn,
|
bindingData: LStylingData, bitMaskValue: number | boolean, applyStylingFn: ApplyStylingFn,
|
||||||
sanitizer: StyleSanitizeFn | null) {
|
sanitizer: StyleSanitizeFn | null, hostBindingsMode: boolean): void {
|
||||||
const bitMask = normalizeBitMaskValue(bitMaskValue);
|
const bitMask = normalizeBitMaskValue(bitMaskValue);
|
||||||
const stylingMapsSyncFn = getStylingMapsSyncFn();
|
|
||||||
const mapsGuardMask = getGuardMask(context, TStylingContextIndex.MapBindingsPosition);
|
let stylingMapsSyncFn: SyncStylingMapsFn|null = null;
|
||||||
const applyAllValues = (bitMask & mapsGuardMask) > 0;
|
let applyAllValues = false;
|
||||||
const mapsMode =
|
if (hasConfig(context, TStylingConfig.HasMapBindings)) {
|
||||||
|
stylingMapsSyncFn = getStylingMapsSyncFn();
|
||||||
|
const mapsGuardMask =
|
||||||
|
getGuardMask(context, TStylingContextIndex.ValuesStartPosition, hostBindingsMode);
|
||||||
|
applyAllValues = (bitMask & mapsGuardMask) !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const valuesCount = getValuesCount(context);
|
||||||
|
let totalBindingsToVisit = 1;
|
||||||
|
let mapsMode =
|
||||||
applyAllValues ? StylingMapsSyncMode.ApplyAllValues : StylingMapsSyncMode.TraverseValues;
|
applyAllValues ? StylingMapsSyncMode.ApplyAllValues : StylingMapsSyncMode.TraverseValues;
|
||||||
|
if (hostBindingsMode) {
|
||||||
|
mapsMode |= StylingMapsSyncMode.RecurseInnerMaps;
|
||||||
|
totalBindingsToVisit = valuesCount - 1;
|
||||||
|
}
|
||||||
|
|
||||||
let i = getPropValuesStartPosition(context);
|
let i = getPropValuesStartPosition(context);
|
||||||
while (i < context.length) {
|
while (i < context.length) {
|
||||||
const valuesCount = getValuesCount(context, i);
|
const guardMask = getGuardMask(context, i, hostBindingsMode);
|
||||||
const guardMask = getGuardMask(context, i);
|
|
||||||
if (bitMask & guardMask) {
|
if (bitMask & guardMask) {
|
||||||
let valueApplied = false;
|
let valueApplied = false;
|
||||||
const prop = getProp(context, i);
|
const prop = getProp(context, i);
|
||||||
const valuesCountUpToDefault = valuesCount - 1;
|
const defaultValue = getDefaultValue(context, i);
|
||||||
const defaultValue = getBindingValue(context, i, valuesCountUpToDefault) as string | null;
|
|
||||||
|
|
||||||
// case 1: apply prop-based values
|
// Part 1: Visit the `[styling.prop]` value
|
||||||
// try to apply the binding values and see if a non-null
|
for (let j = 0; j < totalBindingsToVisit; j++) {
|
||||||
// value gets set for the styling binding
|
|
||||||
for (let j = 0; j < valuesCountUpToDefault; j++) {
|
|
||||||
const bindingIndex = getBindingValue(context, i, j) as number;
|
const bindingIndex = getBindingValue(context, i, j) as number;
|
||||||
const value = bindingData[bindingIndex];
|
if (!valueApplied && bindingIndex !== 0) {
|
||||||
|
const value = getValue(bindingData, bindingIndex);
|
||||||
if (isStylingValueDefined(value)) {
|
if (isStylingValueDefined(value)) {
|
||||||
|
const checkValueOnly = hostBindingsMode && j === 0;
|
||||||
|
if (!checkValueOnly) {
|
||||||
const finalValue = sanitizer && isSanitizationRequired(context, i) ?
|
const finalValue = sanitizer && isSanitizationRequired(context, i) ?
|
||||||
sanitizer(prop, value, StyleSanitizeMode.SanitizeOnly) :
|
sanitizer(prop, value, StyleSanitizeMode.SanitizeOnly) :
|
||||||
value;
|
unwrapSafeValue(value);
|
||||||
applyStylingFn(renderer, element, prop, finalValue, bindingIndex);
|
applyStylingFn(renderer, element, prop, finalValue, bindingIndex);
|
||||||
|
}
|
||||||
valueApplied = true;
|
valueApplied = true;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// case 2: apply map-based values
|
// Part 2: Visit the `[style]` or `[class]` map-based value
|
||||||
// traverse through each map-based styling binding and update all values up to
|
|
||||||
// the provided `prop` value. If the property was not applied in the loop above
|
|
||||||
// then it will be attempted to be applied in the maps sync code below.
|
|
||||||
if (stylingMapsSyncFn) {
|
if (stylingMapsSyncFn) {
|
||||||
// determine whether or not to apply the target property or to skip it
|
// determine whether or not to apply the target property or to skip it
|
||||||
const mode = mapsMode | (valueApplied ? StylingMapsSyncMode.SkipTargetProp :
|
let mode = mapsMode | (valueApplied ? StylingMapsSyncMode.SkipTargetProp :
|
||||||
StylingMapsSyncMode.ApplyTargetProp);
|
StylingMapsSyncMode.ApplyTargetProp);
|
||||||
|
if (hostBindingsMode && j === 0) {
|
||||||
|
mode |= StylingMapsSyncMode.CheckValuesOnly;
|
||||||
|
}
|
||||||
const valueAppliedWithinMap = stylingMapsSyncFn(
|
const valueAppliedWithinMap = stylingMapsSyncFn(
|
||||||
context, renderer, element, bindingData, applyStylingFn, sanitizer, mode, prop,
|
context, renderer, element, bindingData, j, applyStylingFn, sanitizer, mode, prop,
|
||||||
defaultValue);
|
defaultValue);
|
||||||
valueApplied = valueApplied || valueAppliedWithinMap;
|
valueApplied = valueApplied || valueAppliedWithinMap;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// case 3: apply the default value
|
// Part 3: apply the default value (e.g. `<div style="width:200">` => `200px` gets applied)
|
||||||
// if the value has not yet been applied then a truthy value does not exist in the
|
// if the value has not yet been applied then a truthy value does not exist in the
|
||||||
// prop-based or map-based bindings code. If and when this happens, just apply the
|
// prop-based or map-based bindings code. If and when this happens, just apply the
|
||||||
// default value (even if the default value is `null`).
|
// default value (even if the default value is `null`).
|
||||||
|
@ -566,8 +596,90 @@ export function applyStyling(
|
||||||
// values. For this reason, one more call to the sync function
|
// values. For this reason, one more call to the sync function
|
||||||
// needs to be issued at the end.
|
// needs to be issued at the end.
|
||||||
if (stylingMapsSyncFn) {
|
if (stylingMapsSyncFn) {
|
||||||
stylingMapsSyncFn(context, renderer, element, bindingData, applyStylingFn, sanitizer, mapsMode);
|
if (hostBindingsMode) {
|
||||||
|
mapsMode |= StylingMapsSyncMode.CheckValuesOnly;
|
||||||
}
|
}
|
||||||
|
stylingMapsSyncFn(
|
||||||
|
context, renderer, element, bindingData, 0, applyStylingFn, sanitizer, mapsMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the provided styling map to the element directly (without context resolution).
|
||||||
|
*
|
||||||
|
* This function is designed to be run from the styling instructions and will be called
|
||||||
|
* automatically. This function is intended to be used for performance reasons in the
|
||||||
|
* event that there is no need to apply styling via context resolution.
|
||||||
|
*
|
||||||
|
* See `allowDirectStylingApply`.
|
||||||
|
*
|
||||||
|
* @returns whether or not the styling map was applied to the element.
|
||||||
|
*/
|
||||||
|
export function applyStylingMapDirectly(
|
||||||
|
renderer: any, context: TStylingContext, element: RElement, data: LStylingData,
|
||||||
|
bindingIndex: number, map: StylingMapArray, applyFn: ApplyStylingFn,
|
||||||
|
sanitizer?: StyleSanitizeFn | null, forceUpdate?: boolean): boolean {
|
||||||
|
if (forceUpdate || hasValueChanged(data[bindingIndex], map)) {
|
||||||
|
setValue(data, bindingIndex, map);
|
||||||
|
for (let i = StylingMapArrayIndex.ValuesStartPosition; i < map.length;
|
||||||
|
i += StylingMapArrayIndex.TupleSize) {
|
||||||
|
const prop = getMapProp(map, i);
|
||||||
|
const value = getMapValue(map, i);
|
||||||
|
applyStylingValue(renderer, context, element, prop, value, applyFn, bindingIndex, sanitizer);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the provided styling prop/value to the element directly (without context resolution).
|
||||||
|
*
|
||||||
|
* This function is designed to be run from the styling instructions and will be called
|
||||||
|
* automatically. This function is intended to be used for performance reasons in the
|
||||||
|
* event that there is no need to apply styling via context resolution.
|
||||||
|
*
|
||||||
|
* See `allowDirectStylingApply`.
|
||||||
|
*
|
||||||
|
* @returns whether or not the prop/value styling was applied to the element.
|
||||||
|
*/
|
||||||
|
export function applyStylingValueDirectly(
|
||||||
|
renderer: any, context: TStylingContext, element: RElement, data: LStylingData,
|
||||||
|
bindingIndex: number, prop: string, value: any, applyFn: ApplyStylingFn,
|
||||||
|
sanitizer?: StyleSanitizeFn | null): boolean {
|
||||||
|
if (hasValueChanged(data[bindingIndex], value)) {
|
||||||
|
setValue(data, bindingIndex, value);
|
||||||
|
applyStylingValue(renderer, context, element, prop, value, applyFn, bindingIndex, sanitizer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyStylingValue(
|
||||||
|
renderer: any, context: TStylingContext, element: RElement, prop: string, value: any,
|
||||||
|
applyFn: ApplyStylingFn, bindingIndex: number, sanitizer?: StyleSanitizeFn | null) {
|
||||||
|
let valueToApply: string|null = unwrapSafeValue(value);
|
||||||
|
if (isStylingValueDefined(valueToApply)) {
|
||||||
|
valueToApply =
|
||||||
|
sanitizer ? sanitizer(prop, value, StyleSanitizeMode.SanitizeOnly) : valueToApply;
|
||||||
|
} else if (hasConfig(context, TStylingConfig.HasInitialStyling)) {
|
||||||
|
const initialStyles = getStylingMapArray(context);
|
||||||
|
if (initialStyles) {
|
||||||
|
valueToApply = findInitialStylingValue(initialStyles, prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
applyFn(renderer, element, prop, valueToApply, bindingIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
function findInitialStylingValue(map: StylingMapArray, prop: string): string|null {
|
||||||
|
for (let i = StylingMapArrayIndex.ValuesStartPosition; i < map.length;
|
||||||
|
i += StylingMapArrayIndex.TupleSize) {
|
||||||
|
const p = getMapProp(map, i);
|
||||||
|
if (p >= prop) {
|
||||||
|
return p === prop ? getMapValue(map, i) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeBitMaskValue(value: number | boolean): number {
|
function normalizeBitMaskValue(value: number | boolean): number {
|
||||||
|
@ -593,7 +705,7 @@ export function setStylingMapsSyncFn(fn: SyncStylingMapsFn) {
|
||||||
/**
|
/**
|
||||||
* Assigns a style value to a style property for the given element.
|
* Assigns a style value to a style property for the given element.
|
||||||
*/
|
*/
|
||||||
const setStyle: ApplyStylingFn =
|
export const setStyle: ApplyStylingFn =
|
||||||
(renderer: Renderer3 | null, native: RElement, prop: string, value: string | null) => {
|
(renderer: Renderer3 | null, native: RElement, prop: string, value: string | null) => {
|
||||||
// the reason why this may be `null` is either because
|
// the reason why this may be `null` is either because
|
||||||
// it's a container element or it's a part of a test
|
// it's a container element or it's a part of a test
|
||||||
|
@ -620,7 +732,7 @@ const setStyle: ApplyStylingFn =
|
||||||
/**
|
/**
|
||||||
* Adds/removes the provided className value to the provided element.
|
* Adds/removes the provided className value to the provided element.
|
||||||
*/
|
*/
|
||||||
const setClass: ApplyStylingFn =
|
export const setClass: ApplyStylingFn =
|
||||||
(renderer: Renderer3 | null, native: RElement, className: string, value: any) => {
|
(renderer: Renderer3 | null, native: RElement, className: string, value: any) => {
|
||||||
if (className !== '') {
|
if (className !== '') {
|
||||||
// the reason why this may be `null` is either because
|
// the reason why this may be `null` is either because
|
||||||
|
@ -666,33 +778,3 @@ export function renderStylingMap(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers all initial styling entries into the provided context.
|
|
||||||
*
|
|
||||||
* This function will iterate over all entries in the provided `initialStyling` ar}ray and register
|
|
||||||
* them as default (initial) values in the provided context. Initial styling values in a context are
|
|
||||||
* the default values that are to be applied unless overwritten by a binding.
|
|
||||||
*
|
|
||||||
* The reason why this function exists and isn't a part of the context construction is because
|
|
||||||
* host binding is evaluated at a later stage after the element is created. This means that
|
|
||||||
* if a directive or component contains any initial styling code (i.e. `<div class="foo">`)
|
|
||||||
* then that initial styling data can only be applied once the styling for that element
|
|
||||||
* is first applied (at the end of the update phase). Once that happens then the context will
|
|
||||||
* update itself with the complete initial styling for the element.
|
|
||||||
*/
|
|
||||||
function updateInitialStylingOnContext(
|
|
||||||
context: TStylingContext, initialStyling: StylingMapArray): void {
|
|
||||||
// `-1` is used here because all initial styling data is not a spart
|
|
||||||
// of a binding (since it's static)
|
|
||||||
const INITIAL_STYLING_COUNT_ID = -1;
|
|
||||||
|
|
||||||
for (let i = StylingMapArrayIndex.ValuesStartPosition; i < initialStyling.length;
|
|
||||||
i += StylingMapArrayIndex.TupleSize) {
|
|
||||||
const value = getMapValue(initialStyling, i);
|
|
||||||
if (value) {
|
|
||||||
const prop = getMapProp(initialStyling, i);
|
|
||||||
registerBinding(context, INITIAL_STYLING_COUNT_ID, prop, value, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,17 +10,17 @@ import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||||
import {setInputsForProperty} from '../instructions/shared';
|
import {setInputsForProperty} from '../instructions/shared';
|
||||||
import {AttributeMarker, TAttributes, TNode, TNodeType} from '../interfaces/node';
|
import {AttributeMarker, TAttributes, TNode, TNodeType} from '../interfaces/node';
|
||||||
import {RElement} from '../interfaces/renderer';
|
import {RElement} from '../interfaces/renderer';
|
||||||
import {BINDING_INDEX, LView, RENDERER, TVIEW} from '../interfaces/view';
|
import {BINDING_INDEX, LView, RENDERER} from '../interfaces/view';
|
||||||
import {getActiveDirectiveId, getActiveDirectiveSuperClassDepth, getActiveDirectiveSuperClassHeight, getCurrentStyleSanitizer, getLView, getPreviousOrParentTNode, getSelectedIndex, setCurrentStyleSanitizer} from '../state';
|
import {ActiveElementFlags, getActiveDirectiveId, getCurrentStyleSanitizer, getLView, getSelectedIndex, setActiveElementFlag, setCurrentStyleSanitizer, setElementExitFn} from '../state';
|
||||||
import {NO_CHANGE} from '../tokens';
|
import {NO_CHANGE} from '../tokens';
|
||||||
import {renderStringify} from '../util/misc_utils';
|
import {renderStringify} from '../util/misc_utils';
|
||||||
import {getNativeByTNode, getTNode} from '../util/view_utils';
|
import {getNativeByTNode, getTNode} from '../util/view_utils';
|
||||||
|
|
||||||
import {flushStyling, updateClassBinding, updateStyleBinding} from './bindings';
|
import {applyStylingMapDirectly, applyStylingValueDirectly, flushStyling, setClass, setStyle, updateClassViaContext, updateStyleViaContext} from './bindings';
|
||||||
import {StylingMapArray, StylingMapArrayIndex, TStylingContext} from './interfaces';
|
import {StylingMapArray, StylingMapArrayIndex, TStylingContext} from './interfaces';
|
||||||
import {activateStylingMapFeature, addItemToStylingMap, normalizeIntoStylingMap, stylingMapToString} from './map_based_bindings';
|
import {activateStylingMapFeature} from './map_based_bindings';
|
||||||
import {attachStylingDebugObject} from './styling_debug';
|
import {attachStylingDebugObject} from './styling_debug';
|
||||||
import {allocTStylingContext, concatString, forceClassesAsString, forceStylesAsString, getInitialStylingValue, getStylingMapArray, hasClassInput, hasStyleInput, hasValueChanged, isContextLocked, isStylingContext, updateLastDirectiveIndex as _updateLastDirectiveIndex} from './util';
|
import {addItemToStylingMap, allocStylingMapArray, allocTStylingContext, allowDirectStyling, concatString, forceClassesAsString, forceStylesAsString, getInitialStylingValue, getStylingMapArray, hasClassInput, hasStyleInput, hasValueChanged, isContextLocked, isHostStylingActive, isStylingContext, normalizeIntoStylingMap, setValue, stylingMapToString} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,34 +34,13 @@ import {allocTStylingContext, concatString, forceClassesAsString, forceStylesAsS
|
||||||
* --------
|
* --------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporary function to bridge styling functionality between this new
|
|
||||||
* refactor (which is here inside of `styling_next/`) and the old
|
|
||||||
* implementation (which lives inside of `styling/`).
|
|
||||||
*
|
|
||||||
* This function is executed during the creation block of an element.
|
|
||||||
* Because the existing styling implementation issues a call to the
|
|
||||||
* `styling()` instruction, this instruction will also get run. The
|
|
||||||
* central idea here is that the directive index values are bound
|
|
||||||
* into the context. The directive index is temporary and is only
|
|
||||||
* required until the `select(n)` instruction is fully functional.
|
|
||||||
*
|
|
||||||
* @codeGenApi
|
|
||||||
*/
|
|
||||||
export function ɵɵstyling() {
|
|
||||||
const tView = getLView()[TVIEW];
|
|
||||||
if (tView.firstTemplatePass) {
|
|
||||||
updateLastDirectiveIndex(getPreviousOrParentTNode(), getActiveDirectiveStylingIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the current style sanitizer function which will then be used
|
* Sets the current style sanitizer function which will then be used
|
||||||
* within all follow-up prop and map-based style binding instructions
|
* within all follow-up prop and map-based style binding instructions
|
||||||
* for the given element.
|
* for the given element.
|
||||||
*
|
*
|
||||||
* Note that once styling has been applied to the element (i.e. once
|
* Note that once styling has been applied to the element (i.e. once
|
||||||
* `select(n)` is executed or the hostBindings/template function exits)
|
* `advance(n)` is executed or the hostBindings/template function exits)
|
||||||
* then the active `sanitizerFn` will be set to `null`. This means that
|
* then the active `sanitizerFn` will be set to `null`. This means that
|
||||||
* once styling is applied to another element then a another call to
|
* once styling is applied to another element then a another call to
|
||||||
* `styleSanitizer` will need to be made.
|
* `styleSanitizer` will need to be made.
|
||||||
|
@ -92,7 +71,7 @@ export function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn | null): void {
|
||||||
* be ignored.
|
* be ignored.
|
||||||
*
|
*
|
||||||
* Note that this will apply the provided style value to the host element if this function is called
|
* Note that this will apply the provided style value to the host element if this function is called
|
||||||
* within a host binding.
|
* within a host binding function.
|
||||||
*
|
*
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
|
@ -101,9 +80,15 @@ export function ɵɵstyleProp(
|
||||||
stylePropInternal(getSelectedIndex(), prop, value, suffix);
|
stylePropInternal(getSelectedIndex(), prop, value, suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function for applying a single style to an element.
|
||||||
|
*
|
||||||
|
* The reason why this function has been separated from `ɵɵstyleProp` is because
|
||||||
|
* it is also called from `ɵɵstylePropInterpolate`.
|
||||||
|
*/
|
||||||
export function stylePropInternal(
|
export function stylePropInternal(
|
||||||
elementIndex: number, prop: string, value: string | number | SafeValue | null,
|
elementIndex: number, prop: string, value: string | number | SafeValue | null,
|
||||||
suffix?: string | null | undefined) {
|
suffix?: string | null | undefined): void {
|
||||||
const lView = getLView();
|
const lView = getLView();
|
||||||
|
|
||||||
// if a value is interpolated then it may render a `NO_CHANGE` value.
|
// if a value is interpolated then it may render a `NO_CHANGE` value.
|
||||||
|
@ -112,9 +97,8 @@ export function stylePropInternal(
|
||||||
// are stored inside of the lView.
|
// are stored inside of the lView.
|
||||||
const bindingIndex = lView[BINDING_INDEX]++;
|
const bindingIndex = lView[BINDING_INDEX]++;
|
||||||
|
|
||||||
const updated = _stylingProp(
|
const updated =
|
||||||
elementIndex, bindingIndex, prop, resolveStylePropValue(value, suffix), false,
|
stylingProp(elementIndex, bindingIndex, prop, resolveStylePropValue(value, suffix), false);
|
||||||
deferStylingUpdate());
|
|
||||||
if (ngDevMode) {
|
if (ngDevMode) {
|
||||||
ngDevMode.styleProp++;
|
ngDevMode.styleProp++;
|
||||||
if (updated) {
|
if (updated) {
|
||||||
|
@ -134,7 +118,7 @@ export function stylePropInternal(
|
||||||
* @param value A true/false value which will turn the class on or off.
|
* @param value A true/false value which will turn the class on or off.
|
||||||
*
|
*
|
||||||
* Note that this will apply the provided class value to the host element if this function
|
* Note that this will apply the provided class value to the host element if this function
|
||||||
* is called within a host binding.
|
* is called within a host binding function.
|
||||||
*
|
*
|
||||||
* @codeGenApi
|
* @codeGenApi
|
||||||
*/
|
*/
|
||||||
|
@ -147,8 +131,7 @@ export function ɵɵclassProp(className: string, value: boolean | null): void {
|
||||||
// are stored inside of the lView.
|
// are stored inside of the lView.
|
||||||
const bindingIndex = lView[BINDING_INDEX]++;
|
const bindingIndex = lView[BINDING_INDEX]++;
|
||||||
|
|
||||||
const updated =
|
const updated = stylingProp(getSelectedIndex(), bindingIndex, className, value, true);
|
||||||
_stylingProp(getSelectedIndex(), bindingIndex, className, value, true, deferStylingUpdate());
|
|
||||||
if (ngDevMode) {
|
if (ngDevMode) {
|
||||||
ngDevMode.classProp++;
|
ngDevMode.classProp++;
|
||||||
if (updated) {
|
if (updated) {
|
||||||
|
@ -159,28 +142,57 @@ export function ɵɵclassProp(className: string, value: boolean | null): void {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shared function used to update a prop-based styling binding for an element.
|
* Shared function used to update a prop-based styling binding for an element.
|
||||||
|
*
|
||||||
|
* Depending on the state of the `tNode.styles` styles context, the style/prop
|
||||||
|
* value may be applied directly to the element instead of being processed
|
||||||
|
* through the context. The reason why this occurs is for performance and fully
|
||||||
|
* depends on the state of the context (i.e. whether or not there are duplicate
|
||||||
|
* bindings or whether or not there are map-based bindings and property bindings
|
||||||
|
* present together).
|
||||||
*/
|
*/
|
||||||
function _stylingProp(
|
function stylingProp(
|
||||||
elementIndex: number, bindingIndex: number, prop: string,
|
elementIndex: number, bindingIndex: number, prop: string,
|
||||||
value: boolean | number | SafeValue | string | null | undefined | NO_CHANGE,
|
value: boolean | number | SafeValue | string | null | undefined | NO_CHANGE,
|
||||||
isClassBased: boolean, defer: boolean): boolean {
|
isClassBased: boolean): boolean {
|
||||||
|
let updated = false;
|
||||||
|
|
||||||
const lView = getLView();
|
const lView = getLView();
|
||||||
const tNode = getTNode(elementIndex, lView);
|
const tNode = getTNode(elementIndex, lView);
|
||||||
const native = getNativeByTNode(tNode, lView) as RElement;
|
const native = getNativeByTNode(tNode, lView) as RElement;
|
||||||
|
|
||||||
let valueHasChanged = false;
|
const hostBindingsMode = isHostStyling();
|
||||||
if (isClassBased) {
|
const context = isClassBased ? getClassesContext(tNode) : getStylesContext(tNode);
|
||||||
valueHasChanged = updateClassBinding(
|
const sanitizer = isClassBased ? null : getCurrentStyleSanitizer();
|
||||||
getClassesContext(tNode), lView, native, prop, bindingIndex,
|
|
||||||
value as string | boolean | null, defer, false);
|
// Direct Apply Case: bypass context resolution and apply the
|
||||||
|
// style/class value directly to the element
|
||||||
|
if (allowDirectStyling(context, hostBindingsMode)) {
|
||||||
|
const renderer = getRenderer(tNode, lView);
|
||||||
|
updated = applyStylingValueDirectly(
|
||||||
|
renderer, context, native, lView, bindingIndex, prop, value,
|
||||||
|
isClassBased ? setClass : setStyle, sanitizer);
|
||||||
} else {
|
} else {
|
||||||
const sanitizer = getCurrentStyleSanitizer();
|
// Context Resolution (or first update) Case: save the value
|
||||||
valueHasChanged = updateStyleBinding(
|
// and defer to the context to flush and apply the style/class binding
|
||||||
getStylesContext(tNode), lView, native, prop, bindingIndex,
|
// value to the element.
|
||||||
value as string | SafeValue | null, sanitizer, defer, false);
|
const directiveIndex = getActiveDirectiveId();
|
||||||
|
if (isClassBased) {
|
||||||
|
updated = updateClassViaContext(
|
||||||
|
context, lView, native, directiveIndex, prop, bindingIndex,
|
||||||
|
value as string | boolean | null);
|
||||||
|
} else {
|
||||||
|
updated = updateStyleViaContext(
|
||||||
|
context, lView, native, directiveIndex, prop, bindingIndex,
|
||||||
|
value as string | SafeValue | null, sanitizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
return valueHasChanged;
|
if (updated) {
|
||||||
|
setElementExitFn(applyStyling);
|
||||||
|
}
|
||||||
|
markStylingStateAsDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -207,7 +219,6 @@ export function ɵɵstyleMap(styles: {[styleName: string]: any} | NO_CHANGE | nu
|
||||||
const lView = getLView();
|
const lView = getLView();
|
||||||
const tNode = getTNode(index, lView);
|
const tNode = getTNode(index, lView);
|
||||||
const context = getStylesContext(tNode);
|
const context = getStylesContext(tNode);
|
||||||
const directiveIndex = getActiveDirectiveStylingIndex();
|
|
||||||
|
|
||||||
// if a value is interpolated then it may render a `NO_CHANGE` value.
|
// if a value is interpolated then it may render a `NO_CHANGE` value.
|
||||||
// in this case we do not need to do anything, but the binding index
|
// in this case we do not need to do anything, but the binding index
|
||||||
|
@ -218,12 +229,12 @@ export function ɵɵstyleMap(styles: {[styleName: string]: any} | NO_CHANGE | nu
|
||||||
// inputs are only evaluated from a template binding into a directive, therefore,
|
// inputs are only evaluated from a template binding into a directive, therefore,
|
||||||
// there should not be a situation where a directive host bindings function
|
// there should not be a situation where a directive host bindings function
|
||||||
// evaluates the inputs (this should only happen in the template function)
|
// evaluates the inputs (this should only happen in the template function)
|
||||||
if (!directiveIndex && hasStyleInput(tNode) && styles !== NO_CHANGE) {
|
if (!isHostStyling() && hasStyleInput(tNode) && styles !== NO_CHANGE) {
|
||||||
updateDirectiveInputValue(context, lView, tNode, bindingIndex, styles, false);
|
updateDirectiveInputValue(context, lView, tNode, bindingIndex, styles, false);
|
||||||
styles = NO_CHANGE;
|
styles = NO_CHANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updated = _stylingMap(index, context, bindingIndex, styles, false, deferStylingUpdate());
|
const updated = _stylingMap(index, context, bindingIndex, styles, false);
|
||||||
if (ngDevMode) {
|
if (ngDevMode) {
|
||||||
ngDevMode.styleMap++;
|
ngDevMode.styleMap++;
|
||||||
if (updated) {
|
if (updated) {
|
||||||
|
@ -254,12 +265,17 @@ export function ɵɵclassMap(classes: {[className: string]: any} | NO_CHANGE | s
|
||||||
classMapInternal(getSelectedIndex(), classes);
|
classMapInternal(getSelectedIndex(), classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function for applying a class string or key/value map of classes to an element.
|
||||||
|
*
|
||||||
|
* The reason why this function has been separated from `ɵɵclassMap` is because
|
||||||
|
* it is also called from `ɵɵclassMapInterpolate`.
|
||||||
|
*/
|
||||||
export function classMapInternal(
|
export function classMapInternal(
|
||||||
elementIndex: number, classes: {[className: string]: any} | NO_CHANGE | string | null) {
|
elementIndex: number, classes: {[className: string]: any} | NO_CHANGE | string | null): void {
|
||||||
const lView = getLView();
|
const lView = getLView();
|
||||||
const tNode = getTNode(elementIndex, lView);
|
const tNode = getTNode(elementIndex, lView);
|
||||||
const context = getClassesContext(tNode);
|
const context = getClassesContext(tNode);
|
||||||
const directiveIndex = getActiveDirectiveStylingIndex();
|
|
||||||
|
|
||||||
// if a value is interpolated then it may render a `NO_CHANGE` value.
|
// if a value is interpolated then it may render a `NO_CHANGE` value.
|
||||||
// in this case we do not need to do anything, but the binding index
|
// in this case we do not need to do anything, but the binding index
|
||||||
|
@ -270,13 +286,12 @@ export function classMapInternal(
|
||||||
// inputs are only evaluated from a template binding into a directive, therefore,
|
// inputs are only evaluated from a template binding into a directive, therefore,
|
||||||
// there should not be a situation where a directive host bindings function
|
// there should not be a situation where a directive host bindings function
|
||||||
// evaluates the inputs (this should only happen in the template function)
|
// evaluates the inputs (this should only happen in the template function)
|
||||||
if (!directiveIndex && hasClassInput(tNode) && classes !== NO_CHANGE) {
|
if (!isHostStyling() && hasClassInput(tNode) && classes !== NO_CHANGE) {
|
||||||
updateDirectiveInputValue(context, lView, tNode, bindingIndex, classes, true);
|
updateDirectiveInputValue(context, lView, tNode, bindingIndex, classes, true);
|
||||||
classes = NO_CHANGE;
|
classes = NO_CHANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updated =
|
const updated = _stylingMap(elementIndex, context, bindingIndex, classes, true);
|
||||||
_stylingMap(elementIndex, context, bindingIndex, classes, true, deferStylingUpdate());
|
|
||||||
if (ngDevMode) {
|
if (ngDevMode) {
|
||||||
ngDevMode.classMap++;
|
ngDevMode.classMap++;
|
||||||
if (updated) {
|
if (updated) {
|
||||||
|
@ -293,27 +308,52 @@ export function classMapInternal(
|
||||||
*/
|
*/
|
||||||
function _stylingMap(
|
function _stylingMap(
|
||||||
elementIndex: number, context: TStylingContext, bindingIndex: number,
|
elementIndex: number, context: TStylingContext, bindingIndex: number,
|
||||||
value: {[key: string]: any} | string | null, isClassBased: boolean, defer: boolean) {
|
value: {[key: string]: any} | string | null, isClassBased: boolean): boolean {
|
||||||
activateStylingMapFeature();
|
let updated = false;
|
||||||
const lView = getLView();
|
|
||||||
|
|
||||||
|
const lView = getLView();
|
||||||
|
const directiveIndex = getActiveDirectiveId();
|
||||||
const tNode = getTNode(elementIndex, lView);
|
const tNode = getTNode(elementIndex, lView);
|
||||||
const native = getNativeByTNode(tNode, lView) as RElement;
|
const native = getNativeByTNode(tNode, lView) as RElement;
|
||||||
const oldValue = lView[bindingIndex];
|
const oldValue = lView[bindingIndex] as StylingMapArray | null;
|
||||||
|
const hostBindingsMode = isHostStyling();
|
||||||
|
const sanitizer = getCurrentStyleSanitizer();
|
||||||
|
|
||||||
const valueHasChanged = hasValueChanged(oldValue, value);
|
const valueHasChanged = hasValueChanged(oldValue, value);
|
||||||
const stylingMapArr =
|
const stylingMapArr =
|
||||||
value === NO_CHANGE ? NO_CHANGE : normalizeIntoStylingMap(oldValue, value, !isClassBased);
|
value === NO_CHANGE ? NO_CHANGE : normalizeIntoStylingMap(oldValue, value, !isClassBased);
|
||||||
if (isClassBased) {
|
|
||||||
updateClassBinding(
|
// Direct Apply Case: bypass context resolution and apply the
|
||||||
context, lView, native, null, bindingIndex, stylingMapArr, defer, valueHasChanged);
|
// style/class map values directly to the element
|
||||||
|
if (allowDirectStyling(context, hostBindingsMode)) {
|
||||||
|
const renderer = getRenderer(tNode, lView);
|
||||||
|
updated = applyStylingMapDirectly(
|
||||||
|
renderer, context, native, lView, bindingIndex, stylingMapArr as StylingMapArray,
|
||||||
|
isClassBased ? setClass : setStyle, sanitizer, valueHasChanged);
|
||||||
} else {
|
} else {
|
||||||
const sanitizer = getCurrentStyleSanitizer();
|
// Context Resolution (or first update) Case: save the map value
|
||||||
updateStyleBinding(
|
// and defer to the context to flush and apply the style/class binding
|
||||||
context, lView, native, null, bindingIndex, stylingMapArr, sanitizer, defer,
|
// value to the element.
|
||||||
|
if (isClassBased) {
|
||||||
|
updateClassViaContext(
|
||||||
|
context, lView, native, directiveIndex, null, bindingIndex, stylingMapArr,
|
||||||
|
valueHasChanged);
|
||||||
|
} else {
|
||||||
|
updateStyleViaContext(
|
||||||
|
context, lView, native, directiveIndex, null, bindingIndex, stylingMapArr, sanitizer,
|
||||||
valueHasChanged);
|
valueHasChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
return valueHasChanged;
|
if (valueHasChanged) {
|
||||||
|
updated = true;
|
||||||
|
setElementExitFn(applyStyling);
|
||||||
|
}
|
||||||
|
|
||||||
|
activateStylingMapFeature();
|
||||||
|
}
|
||||||
|
|
||||||
|
markStylingStateAsDirty();
|
||||||
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -338,13 +378,15 @@ function updateDirectiveInputValue(
|
||||||
// even if the value has changed we may not want to emit it to the
|
// even if the value has changed we may not want to emit it to the
|
||||||
// directive input(s) in the event that it is falsy during the
|
// directive input(s) in the event that it is falsy during the
|
||||||
// first update pass.
|
// first update pass.
|
||||||
if (newValue || isContextLocked(context)) {
|
if (newValue || isContextLocked(context, false)) {
|
||||||
const inputs = tNode.inputs ![isClassBased ? 'class' : 'style'] !;
|
const inputName = isClassBased ? 'class' : 'style';
|
||||||
|
const inputs = tNode.inputs ![inputName] !;
|
||||||
const initialValue = getInitialStylingValue(context);
|
const initialValue = getInitialStylingValue(context);
|
||||||
const value = normalizeStylingDirectiveInputValue(initialValue, newValue, isClassBased);
|
const value = normalizeStylingDirectiveInputValue(initialValue, newValue, isClassBased);
|
||||||
setInputsForProperty(lView, inputs, value);
|
setInputsForProperty(lView, inputs, value);
|
||||||
}
|
}
|
||||||
lView[bindingIndex] = newValue;
|
setValue(lView, bindingIndex, newValue);
|
||||||
|
setElementExitFn(applyStyling);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,7 +404,7 @@ function normalizeStylingDirectiveInputValue(
|
||||||
|
|
||||||
// we only concat values if there is an initial value, otherwise we return the value as is.
|
// we only concat values if there is an initial value, otherwise we return the value as is.
|
||||||
// Note that this is to satisfy backwards-compatibility in Angular.
|
// Note that this is to satisfy backwards-compatibility in Angular.
|
||||||
if (initialValue.length > 0) {
|
if (initialValue.length) {
|
||||||
if (isClassBased) {
|
if (isClassBased) {
|
||||||
value = concatString(initialValue, forceClassesAsString(bindingValue));
|
value = concatString(initialValue, forceClassesAsString(bindingValue));
|
||||||
} else {
|
} else {
|
||||||
|
@ -377,24 +419,21 @@ function normalizeStylingDirectiveInputValue(
|
||||||
/**
|
/**
|
||||||
* Flushes all styling code to the element.
|
* Flushes all styling code to the element.
|
||||||
*
|
*
|
||||||
* This function is designed to be called from the template and hostBindings
|
* This function is designed to be scheduled from any of the four styling instructions
|
||||||
* functions and may be called multiple times depending whether multiple
|
* in this file. When called it will flush all style and class bindings to the element
|
||||||
* sources of styling exist. If called multiple times, only the last call
|
* via the context resolution algorithm.
|
||||||
* to `stlyingApply()` will render styling to the element.
|
|
||||||
*
|
|
||||||
* @codeGenApi
|
|
||||||
*/
|
*/
|
||||||
export function ɵɵstylingApply() {
|
function applyStyling(): void {
|
||||||
const elementIndex = getSelectedIndex();
|
const elementIndex = getSelectedIndex();
|
||||||
const lView = getLView();
|
const lView = getLView();
|
||||||
const tNode = getTNode(elementIndex, lView);
|
const tNode = getTNode(elementIndex, lView);
|
||||||
const renderer = getRenderer(tNode, lView);
|
const renderer = getRenderer(tNode, lView);
|
||||||
const native = getNativeByTNode(tNode, lView) as RElement;
|
const native = getNativeByTNode(tNode, lView) as RElement;
|
||||||
const directiveIndex = getActiveDirectiveStylingIndex();
|
|
||||||
const sanitizer = getCurrentStyleSanitizer();
|
const sanitizer = getCurrentStyleSanitizer();
|
||||||
|
const classesContext = isStylingContext(tNode.classes) ? tNode.classes as TStylingContext : null;
|
||||||
|
const stylesContext = isStylingContext(tNode.styles) ? tNode.styles as TStylingContext : null;
|
||||||
flushStyling(
|
flushStyling(
|
||||||
renderer, lView, getClassesContext(tNode), getStylesContext(tNode), native, directiveIndex,
|
renderer, lView, classesContext, stylesContext, native, getActiveDirectiveId(), sanitizer);
|
||||||
sanitizer);
|
|
||||||
setCurrentStyleSanitizer(null);
|
setCurrentStyleSanitizer(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -417,12 +456,12 @@ export function registerInitialStylingOnTNode(
|
||||||
if (typeof attr == 'number') {
|
if (typeof attr == 'number') {
|
||||||
mode = attr;
|
mode = attr;
|
||||||
} else if (mode == AttributeMarker.Classes) {
|
} else if (mode == AttributeMarker.Classes) {
|
||||||
classes = classes || [''];
|
classes = classes || allocStylingMapArray();
|
||||||
addItemToStylingMap(classes, attr, true);
|
addItemToStylingMap(classes, attr, true);
|
||||||
hasAdditionalInitialStyling = true;
|
hasAdditionalInitialStyling = true;
|
||||||
} else if (mode == AttributeMarker.Styles) {
|
} else if (mode == AttributeMarker.Styles) {
|
||||||
const value = attrs[++i] as string | null;
|
const value = attrs[++i] as string | null;
|
||||||
styles = styles || [''];
|
styles = styles || allocStylingMapArray();
|
||||||
addItemToStylingMap(styles, attr, value);
|
addItemToStylingMap(styles, attr, value);
|
||||||
hasAdditionalInitialStyling = true;
|
hasAdditionalInitialStyling = true;
|
||||||
}
|
}
|
||||||
|
@ -450,33 +489,6 @@ function updateRawValueOnContext(context: TStylingContext | StylingMapArray, val
|
||||||
stylingMapArr[StylingMapArrayIndex.RawValuePosition] = value;
|
stylingMapArr[StylingMapArrayIndex.RawValuePosition] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getActiveDirectiveStylingIndex(): number {
|
|
||||||
// whenever a directive's hostBindings function is called a uniqueId value
|
|
||||||
// is assigned. Normally this is enough to help distinguish one directive
|
|
||||||
// from another for the styling context, but there are situations where a
|
|
||||||
// sub-class directive could inherit and assign styling in concert with a
|
|
||||||
// parent directive. To help the styling code distinguish between a parent
|
|
||||||
// sub-classed directive the inheritance depth is taken into account as well.
|
|
||||||
return getActiveDirectiveId() + getActiveDirectiveSuperClassDepth();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporary function that will update the max directive index value in
|
|
||||||
* both the classes and styles contexts present on the provided `tNode`.
|
|
||||||
*
|
|
||||||
* This code is only used because the `select(n)` code functionality is not
|
|
||||||
* yet 100% functional. The `select(n)` instruction cannot yet evaluate host
|
|
||||||
* bindings function code in sync with the associated template function code.
|
|
||||||
* For this reason the styling algorithm needs to track the last directive index
|
|
||||||
* value so that it knows exactly when to render styling to the element since
|
|
||||||
* `stylingApply()` is called multiple times per CD (`stylingApply` will be
|
|
||||||
* removed once `select(n)` is fixed).
|
|
||||||
*/
|
|
||||||
function updateLastDirectiveIndex(tNode: TNode, directiveIndex: number) {
|
|
||||||
_updateLastDirectiveIndex(getClassesContext(tNode), directiveIndex);
|
|
||||||
_updateLastDirectiveIndex(getStylesContext(tNode), directiveIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStylesContext(tNode: TNode): TStylingContext {
|
function getStylesContext(tNode: TNode): TStylingContext {
|
||||||
return getContext(tNode, false);
|
return getContext(tNode, false);
|
||||||
}
|
}
|
||||||
|
@ -488,10 +500,10 @@ function getClassesContext(tNode: TNode): TStylingContext {
|
||||||
/**
|
/**
|
||||||
* Returns/instantiates a styling context from/to a `tNode` instance.
|
* Returns/instantiates a styling context from/to a `tNode` instance.
|
||||||
*/
|
*/
|
||||||
function getContext(tNode: TNode, isClassBased: boolean) {
|
function getContext(tNode: TNode, isClassBased: boolean): TStylingContext {
|
||||||
let context = isClassBased ? tNode.classes : tNode.styles;
|
let context = isClassBased ? tNode.classes : tNode.styles;
|
||||||
if (!isStylingContext(context)) {
|
if (!isStylingContext(context)) {
|
||||||
context = allocTStylingContext(context);
|
context = allocTStylingContext(context as StylingMapArray | null);
|
||||||
if (ngDevMode) {
|
if (ngDevMode) {
|
||||||
attachStylingDebugObject(context as TStylingContext);
|
attachStylingDebugObject(context as TStylingContext);
|
||||||
}
|
}
|
||||||
|
@ -526,18 +538,14 @@ function resolveStylePropValue(
|
||||||
return resolvedValue;
|
return resolvedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function markStylingStateAsDirty(): void {
|
||||||
* Whether or not a style/class binding update should be applied later.
|
setActiveElementFlag(ActiveElementFlags.ResetStylesOnExit);
|
||||||
*
|
}
|
||||||
* This function will decide whether a binding should be applied immediately
|
|
||||||
* or later (just before the styles/classes are flushed to the element). The
|
/**
|
||||||
* reason why this feature exists is because of super/sub directive inheritance.
|
* Whether or not the style/class binding being applied was executed within a host bindings
|
||||||
* Angular will evaluate host bindings on the super directive first and the sub
|
* function.
|
||||||
* directive, but the styling bindings on the sub directive are of higher priority
|
*/
|
||||||
* than the super directive. For this reason all styling bindings that take place
|
function isHostStyling(): boolean {
|
||||||
* in this circumstance will need to be deferred until later so that they can be
|
return isHostStylingActive(getActiveDirectiveId());
|
||||||
* applied together and in a different order (the algorithm handles that part).
|
|
||||||
*/
|
|
||||||
function deferStylingUpdate(): boolean {
|
|
||||||
return getActiveDirectiveSuperClassHeight() > 0;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,7 @@ import {LView} from '../interfaces/view';
|
||||||
* The `TStylingContext` unites all template styling bindings (i.e.
|
* The `TStylingContext` unites all template styling bindings (i.e.
|
||||||
* `[class]` and `[style]` bindings) as well as all host-level
|
* `[class]` and `[style]` bindings) as well as all host-level
|
||||||
* styling bindings (for components and directives) together into
|
* styling bindings (for components and directives) together into
|
||||||
* a single manifest. It is used each time there are one or more
|
* a single manifest
|
||||||
* styling bindings present for an element.
|
|
||||||
*
|
*
|
||||||
* The styling context is stored on a `TNode` on and there are
|
* The styling context is stored on a `TNode` on and there are
|
||||||
* two instances of it: one for classes and another for styles.
|
* two instances of it: one for classes and another for styles.
|
||||||
|
@ -37,6 +36,10 @@ import {LView} from '../interfaces/view';
|
||||||
* tNode.classes = [ ... a context only for classes ... ];
|
* tNode.classes = [ ... a context only for classes ... ];
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* The styling context is created each time there are one or more
|
||||||
|
* styling bindings (style or class bindings) present for an element,
|
||||||
|
* but is only created once per `TNode`.
|
||||||
|
*
|
||||||
* `tNode.styles` and `tNode.classes` can be an instance of the following:
|
* `tNode.styles` and `tNode.classes` can be an instance of the following:
|
||||||
*
|
*
|
||||||
* ```typescript
|
* ```typescript
|
||||||
|
@ -62,36 +65,48 @@ import {LView} from '../interfaces/view';
|
||||||
* storing actual styling binding values, the lView binding index values
|
* storing actual styling binding values, the lView binding index values
|
||||||
* are stored within the context. (static nature means it is more compact.)
|
* are stored within the context. (static nature means it is more compact.)
|
||||||
*
|
*
|
||||||
|
* The code below shows a breakdown of two instances of `TStylingContext`
|
||||||
|
* (one for `tNode.styles` and another for `tNode.classes`):
|
||||||
|
*
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* // <div [class.active]="c" // lView binding index = 20
|
* // <div [class.active]="c" // lView binding index = 20
|
||||||
* // [style.width]="x" // lView binding index = 21
|
* // [style.width]="x" // lView binding index = 21
|
||||||
* // [style.height]="y"> // lView binding index = 22
|
* // [style.height]="y"> // lView binding index = 22
|
||||||
* tNode.stylesContext = [
|
* // ...
|
||||||
* [], // initial values array
|
* // </div>
|
||||||
* 0, // the context config value
|
* tNode.styles = [
|
||||||
|
* 0, // the context config value (see `TStylingContextConfig`)
|
||||||
|
* 1, // the total amount of sources present (only `1` b/c there are only template
|
||||||
|
* bindings)
|
||||||
|
* [null], // initial values array (an instance of `StylingMapArray`)
|
||||||
*
|
*
|
||||||
* 0b001, // guard mask for width
|
* 0, // config entry for the property (see `TStylingContextPropConfigFlags`)
|
||||||
* 2, // total entries for width
|
* 0b010, // template guard mask for height
|
||||||
* 'width', // the property name
|
* 0, // host bindings guard mask for height
|
||||||
* 21, // the binding location for the "x" binding in the lView
|
|
||||||
* null,
|
|
||||||
*
|
|
||||||
* 0b010, // guard mask for height
|
|
||||||
* 2, // total entries for height
|
|
||||||
* 'height', // the property name
|
* 'height', // the property name
|
||||||
* 22, // the binding location for the "y" binding in the lView
|
* 22, // the binding location for the "y" binding in the lView
|
||||||
* null,
|
* null, // the default value for height
|
||||||
|
*
|
||||||
|
* 0, // config entry for the property (see `TStylingContextPropConfigFlags`)
|
||||||
|
* 0b001, // template guard mask for width
|
||||||
|
* 0, // host bindings guard mask for width
|
||||||
|
* 'width', // the property name
|
||||||
|
* 21, // the binding location for the "x" binding in the lView
|
||||||
|
* null, // the default value for width
|
||||||
* ];
|
* ];
|
||||||
*
|
*
|
||||||
* tNode.classesContext = [
|
* tNode.classes = [
|
||||||
* [], // initial values array
|
* 0, // the context config value (see `TStylingContextConfig`)
|
||||||
* 0, // the context config value
|
* 1, // the total amount of sources present (only `1` b/c there are only template
|
||||||
|
* bindings)
|
||||||
|
* [null], // initial values array (an instance of `StylingMapArray`)
|
||||||
*
|
*
|
||||||
* 0b001, // guard mask for active
|
* 0, // config entry for the property (see `TStylingContextPropConfigFlags`)
|
||||||
* 2, // total entries for active
|
* 0b001, // template guard mask for width
|
||||||
|
* 0, // host bindings guard mask for width
|
||||||
* 'active', // the property name
|
* 'active', // the property name
|
||||||
* 20, // the binding location for the "c" binding in the lView
|
* 20, // the binding location for the "c" binding in the lView
|
||||||
* null,
|
* null, // the default value for the `active` class
|
||||||
* ];
|
* ];
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
@ -100,19 +115,25 @@ import {LView} from '../interfaces/view';
|
||||||
*
|
*
|
||||||
* ```typescript
|
* ```typescript
|
||||||
* context = [
|
* context = [
|
||||||
* CONFIG, // the styling context config value
|
|
||||||
* //...
|
* //...
|
||||||
* guardMask,
|
* configValue,
|
||||||
* totalEntries,
|
* templateGuardMask,
|
||||||
|
* hostBindingsGuardMask,
|
||||||
* propName,
|
* propName,
|
||||||
* bindingIndices...,
|
* ...bindingIndices...,
|
||||||
* defaultValue
|
* defaultValue
|
||||||
|
* //...
|
||||||
* ];
|
* ];
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* Below is a breakdown of each value:
|
* Below is a breakdown of each value:
|
||||||
*
|
*
|
||||||
* - **guardMask**:
|
* - **configValue**:
|
||||||
|
* Property-specific configuration values. The only config setting
|
||||||
|
* that is implemented right now is whether or not to sanitize the
|
||||||
|
* value.
|
||||||
|
*
|
||||||
|
* - **templateGuardMask**:
|
||||||
* A numeric value where each bit represents a binding index
|
* A numeric value where each bit represents a binding index
|
||||||
* location. Each binding index location is assigned based on
|
* location. Each binding index location is assigned based on
|
||||||
* a local counter value that increments each time an instruction
|
* a local counter value that increments each time an instruction
|
||||||
|
@ -136,18 +157,23 @@ import {LView} from '../interfaces/view';
|
||||||
* efficient way to flip all bits on the mask when a special kind
|
* efficient way to flip all bits on the mask when a special kind
|
||||||
* of caching scenario occurs or when there are more than 32 bindings).
|
* of caching scenario occurs or when there are more than 32 bindings).
|
||||||
*
|
*
|
||||||
* - **totalEntries**:
|
* - **hostBindingsGuardMask**:
|
||||||
* Each property present in the contains various binding sources of
|
* Another instance of a guard mask that is specific to host bindings.
|
||||||
* where the styling data could come from. This includes template
|
* This behaves exactly the same way as does the `templateGuardMask`,
|
||||||
* level bindings, directive/component host bindings as well as the
|
* but will not contain any binding information processed in the template.
|
||||||
* default value (or static value) all writing to the same property.
|
* The reason why there are two instances of guard masks (one for the
|
||||||
* This value depicts how many binding source entries exist for the
|
* template and another for host bindings) is because the template bindings
|
||||||
* property.
|
* are processed before host bindings and the state information is not
|
||||||
|
* carried over into the host bindings code. As soon as host bindings are
|
||||||
|
* processed for an element the counter and state-based bit mask values are
|
||||||
|
* set to `0`.
|
||||||
*
|
*
|
||||||
* The reason why the totalEntries value is needed is because the
|
* ```
|
||||||
* styling context is dynamic in size and it's not possible
|
* <div [style.width]="x" // binding index = 21 (counter index = 0)
|
||||||
* for the flushing or update algorithms to know when and where
|
* [style.height]="y" // binding index = 22 (counter index = 1)
|
||||||
* a property starts and ends without it.
|
* dir-that-sets-width // binding index = 30 (counter index = 0)
|
||||||
|
* dir-that-sets-width> // binding index = 31 (counter index = 1)
|
||||||
|
* ```
|
||||||
*
|
*
|
||||||
* - **propName**:
|
* - **propName**:
|
||||||
* The CSS property name or class name (e.g `width` or `active`).
|
* The CSS property name or class name (e.g `width` or `active`).
|
||||||
|
@ -165,15 +191,20 @@ import {LView} from '../interfaces/view';
|
||||||
* - **defaultValue**:
|
* - **defaultValue**:
|
||||||
* This is the default that will always be applied to the element if
|
* This is the default that will always be applied to the element if
|
||||||
* and when all other binding sources return a result that is null.
|
* and when all other binding sources return a result that is null.
|
||||||
* Usually this value is null but it can also be a static value that
|
* Usually this value is `null` but it can also be a static value that
|
||||||
* is intercepted when the tNode is first constructured (e.g.
|
* is intercepted when the tNode is first constructured (e.g.
|
||||||
* `<div style="width:200px">` has a default value of `200px` for
|
* `<div style="width:200px">` has a default value of `200px` for
|
||||||
* the `width` property).
|
* the `width` property).
|
||||||
*
|
*
|
||||||
* Each time a new binding is encountered it is registered into the
|
* Each time a new binding is encountered it is registered into the
|
||||||
* context. The context then is continually updated until the first
|
* context. The context then is continually updated until the first
|
||||||
* styling apply call has been called (this is triggered by the
|
* styling apply call has been called (which is automatically scheduled
|
||||||
* `stylingApply()` instruction for the active element).
|
* to be called once an element exits during change detection). Note that
|
||||||
|
* each entry in the context is stored in alphabetical order.
|
||||||
|
*
|
||||||
|
* Once styling has been flushed for the first time for an element the
|
||||||
|
* context will set as locked (this prevents bindings from being added
|
||||||
|
* to the context later on).
|
||||||
*
|
*
|
||||||
* # How Styles/Classes are Rendered
|
* # How Styles/Classes are Rendered
|
||||||
* Each time a styling instruction (e.g. `[class.name]`, `[style.prop]`,
|
* Each time a styling instruction (e.g. `[class.name]`, `[style.prop]`,
|
||||||
|
@ -190,15 +221,24 @@ import {LView} from '../interfaces/view';
|
||||||
* function updateStyleProp(prop: string, value: string) {
|
* function updateStyleProp(prop: string, value: string) {
|
||||||
* const lView = getLView();
|
* const lView = getLView();
|
||||||
* const bindingIndex = BINDING_INDEX++;
|
* const bindingIndex = BINDING_INDEX++;
|
||||||
* const indexForStyle = localStylesCounter++;
|
*
|
||||||
|
* // update the local counter value
|
||||||
|
* const indexForStyle = stylingState.stylesCount++;
|
||||||
* if (lView[bindingIndex] !== value) {
|
* if (lView[bindingIndex] !== value) {
|
||||||
* lView[bindingIndex] = value;
|
* lView[bindingIndex] = value;
|
||||||
* localBitMaskForStyles |= 1 << indexForStyle;
|
*
|
||||||
|
* // tell the local state that we have updated a style value
|
||||||
|
* // by updating the bit mask
|
||||||
|
* stylingState.bitMaskForStyles |= 1 << indexForStyle;
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* ## The Apply Algorithm
|
* Once all the bindings have updated a `bitMask` value will be populated.
|
||||||
|
* This `bitMask` value is used in the apply algorithm (which is called
|
||||||
|
* context resolution).
|
||||||
|
*
|
||||||
|
* ## The Apply Algorithm (Context Resolution)
|
||||||
* As explained above, each time a binding updates its value, the resulting
|
* As explained above, each time a binding updates its value, the resulting
|
||||||
* value is stored in the `lView` array. These styling values have yet to
|
* value is stored in the `lView` array. These styling values have yet to
|
||||||
* be flushed to the element.
|
* be flushed to the element.
|
||||||
|
@ -283,94 +323,147 @@ import {LView} from '../interfaces/view';
|
||||||
*/
|
*/
|
||||||
export interface TStylingContext extends
|
export interface TStylingContext extends
|
||||||
Array<number|string|number|boolean|null|StylingMapArray|{}> {
|
Array<number|string|number|boolean|null|StylingMapArray|{}> {
|
||||||
|
/** Configuration data for the context */
|
||||||
|
[TStylingContextIndex.ConfigPosition]: TStylingConfig;
|
||||||
|
|
||||||
|
/** The total amount of sources present in the context */
|
||||||
|
[TStylingContextIndex.TotalSourcesPosition]: number;
|
||||||
|
|
||||||
/** Initial value position for static styles */
|
/** Initial value position for static styles */
|
||||||
[TStylingContextIndex.InitialStylingValuePosition]: StylingMapArray;
|
[TStylingContextIndex.InitialStylingValuePosition]: StylingMapArray;
|
||||||
|
|
||||||
/** Configuration data for the context */
|
|
||||||
[TStylingContextIndex.ConfigPosition]: TStylingConfigFlags;
|
|
||||||
|
|
||||||
/** Temporary value used to track directive index entries until
|
|
||||||
the old styling code is fully removed. The reason why this
|
|
||||||
is required is to figure out which directive is last and,
|
|
||||||
when encountered, trigger a styling flush to happen */
|
|
||||||
[TStylingContextIndex.LastDirectiveIndexPosition]: number;
|
|
||||||
|
|
||||||
/** The bit guard value for all map-based bindings on an element */
|
|
||||||
[TStylingContextIndex.MapBindingsBitGuardPosition]: number;
|
|
||||||
|
|
||||||
/** The total amount of map-based bindings present on an element */
|
|
||||||
[TStylingContextIndex.MapBindingsValuesCountPosition]: number;
|
|
||||||
|
|
||||||
/** The prop value for map-based bindings (there actually isn't a
|
|
||||||
* value at all, but this is just used in the context to avoid
|
|
||||||
* having any special code to update the binding information for
|
|
||||||
* map-based entries). */
|
|
||||||
[TStylingContextIndex.MapBindingsPropPosition]: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A series of flags used to configure the config value present within a
|
* A series of flags used to configure the config value present within an instance of
|
||||||
* `TStylingContext` value.
|
* `TStylingContext`.
|
||||||
*/
|
*/
|
||||||
export const enum TStylingConfigFlags {
|
export const enum TStylingConfig {
|
||||||
/**
|
/**
|
||||||
* The initial state of the styling context config
|
* The initial state of the styling context config.
|
||||||
*/
|
*/
|
||||||
Initial = 0b0,
|
Initial = 0b0000000,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A flag which marks the context as being locked.
|
* Whether or not there are prop-based bindings present.
|
||||||
*
|
*
|
||||||
* The styling context is constructed across an element template
|
* Examples include:
|
||||||
* function as well as any associated hostBindings functions. When
|
* 1. `<div [style.prop]="x">`
|
||||||
* this occurs, the context itself is open to mutation and only once
|
* 2. `<div [class.prop]="x">`
|
||||||
* it has been flushed once then it will be locked for good (no extra
|
* 3. `@HostBinding('style.prop') x`
|
||||||
* bindings can be added to it).
|
* 4. `@HostBinding('class.prop') x`
|
||||||
*/
|
*/
|
||||||
Locked = 0b1,
|
HasPropBindings = 0b0000001,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not to store the state between updates in a global storage map.
|
* Whether or not there are map-based bindings present.
|
||||||
*
|
*
|
||||||
* This flag helps the algorithm avoid storing all state values temporarily in
|
* Examples include:
|
||||||
* a storage map (that lives in `state.ts`). The flag is only flipped to true if
|
* 1. `<div [style]="x">`
|
||||||
* and when an element contains style/class bindings that exist both on the
|
* 2. `<div [class]="x">`
|
||||||
* template-level as well as within host bindings on the same element. This is a
|
* 3. `@HostBinding('style') x`
|
||||||
* rare case, and a storage map is required so that the state values can be restored
|
* 4. `@HostBinding('class') x`
|
||||||
* between the template code running and the host binding code executing.
|
|
||||||
*/
|
*/
|
||||||
PersistStateValues = 0b10,
|
HasMapBindings = 0b0000010,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not there are map-based and prop-based bindings present.
|
||||||
|
*
|
||||||
|
* Examples include:
|
||||||
|
* 1. `<div [style]="x" [style.prop]="y">`
|
||||||
|
* 2. `<div [class]="x" [style.prop]="y">`
|
||||||
|
* 3. `<div [style]="x" dir-that-sets-some-prop>`
|
||||||
|
* 4. `<div [class]="x" dir-that-sets-some-class>`
|
||||||
|
*/
|
||||||
|
HasPropAndMapBindings = 0b0000011,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not there are two or more sources for a single property in the context.
|
||||||
|
*
|
||||||
|
* Examples include:
|
||||||
|
* 1. prop + prop: `<div [style.width]="x" dir-that-sets-width>`
|
||||||
|
* 2. map + prop: `<div [style]="x" [style.prop]>`
|
||||||
|
* 3. map + map: `<div [style]="x" dir-that-sets-style>`
|
||||||
|
*/
|
||||||
|
HasCollisions = 0b0000100,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the context contains initial styling values.
|
||||||
|
*
|
||||||
|
* Examples include:
|
||||||
|
* 1. `<div style="width:200px">`
|
||||||
|
* 2. `<div class="one two three">`
|
||||||
|
* 3. `@Directive({ host: { 'style': 'width:200px' } })`
|
||||||
|
* 4. `@Directive({ host: { 'class': 'one two three' } })`
|
||||||
|
*/
|
||||||
|
HasInitialStyling = 0b00001000,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the context contains one or more template bindings.
|
||||||
|
*
|
||||||
|
* Examples include:
|
||||||
|
* 1. `<div [style]="x">`
|
||||||
|
* 2. `<div [style.width]="x">`
|
||||||
|
* 3. `<div [class]="x">`
|
||||||
|
* 4. `<div [class.name]="x">`
|
||||||
|
*/
|
||||||
|
HasTemplateBindings = 0b00010000,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the context contains one or more host bindings.
|
||||||
|
*
|
||||||
|
* Examples include:
|
||||||
|
* 1. `@HostBinding('style') x`
|
||||||
|
* 2. `@HostBinding('style.width') x`
|
||||||
|
* 3. `@HostBinding('class') x`
|
||||||
|
* 4. `@HostBinding('class.name') x`
|
||||||
|
*/
|
||||||
|
HasHostBindings = 0b00100000,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the template bindings are allowed to be registered in the context.
|
||||||
|
*
|
||||||
|
* This flag is after one or more template-based style/class bindings were
|
||||||
|
* set and processed for an element. Once the bindings are processed then a call
|
||||||
|
* to stylingApply is issued and the lock will be put into place.
|
||||||
|
*
|
||||||
|
* Note that this is only set once.
|
||||||
|
*/
|
||||||
|
TemplateBindingsLocked = 0b01000000,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the host bindings are allowed to be registered in the context.
|
||||||
|
*
|
||||||
|
* This flag is after one or more host-based style/class bindings were
|
||||||
|
* set and processed for an element. Once the bindings are processed then a call
|
||||||
|
* to stylingApply is issued and the lock will be put into place.
|
||||||
|
*
|
||||||
|
* Note that this is only set once.
|
||||||
|
*/
|
||||||
|
HostBindingsLocked = 0b10000000,
|
||||||
|
|
||||||
/** A Mask of all the configurations */
|
/** A Mask of all the configurations */
|
||||||
Mask = 0b11,
|
Mask = 0b11111111,
|
||||||
|
|
||||||
/** Total amount of configuration bits used */
|
/** Total amount of configuration bits used */
|
||||||
TotalBits = 2,
|
TotalBits = 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An index of position and offset values used to natigate the `TStylingContext`.
|
* An index of position and offset values used to navigate the `TStylingContext`.
|
||||||
*/
|
*/
|
||||||
export const enum TStylingContextIndex {
|
export const enum TStylingContextIndex {
|
||||||
InitialStylingValuePosition = 0,
|
ConfigPosition = 0,
|
||||||
ConfigPosition = 1,
|
TotalSourcesPosition = 1,
|
||||||
LastDirectiveIndexPosition = 2,
|
InitialStylingValuePosition = 2,
|
||||||
|
ValuesStartPosition = 3,
|
||||||
// index/offset values for map-based entries (i.e. `[style]`
|
|
||||||
// and `[class]` bindings).
|
|
||||||
MapBindingsPosition = 3,
|
|
||||||
MapBindingsBitGuardPosition = 3,
|
|
||||||
MapBindingsValuesCountPosition = 4,
|
|
||||||
MapBindingsPropPosition = 5,
|
|
||||||
MapBindingsBindingsStartPosition = 6,
|
|
||||||
|
|
||||||
// each tuple entry in the context
|
// each tuple entry in the context
|
||||||
// (mask, count, prop, ...bindings||default-value)
|
// (config, templateBitGuard, hostBindingBitGuard, prop, ...bindings||default-value)
|
||||||
ConfigAndGuardOffset = 0,
|
ConfigOffset = 0,
|
||||||
ValuesCountOffset = 1,
|
TemplateBitGuardOffset = 1,
|
||||||
PropOffset = 2,
|
HostBindingsBitGuardOffset = 2,
|
||||||
BindingsStartOffset = 3,
|
PropOffset = 3,
|
||||||
MinTupleLength = 4,
|
BindingsStartOffset = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -387,8 +480,8 @@ export const enum TStylingContextPropConfigFlags {
|
||||||
* A function used to apply or remove styling from an element for a given property.
|
* A function used to apply or remove styling from an element for a given property.
|
||||||
*/
|
*/
|
||||||
export interface ApplyStylingFn {
|
export interface ApplyStylingFn {
|
||||||
(renderer: Renderer3|ProceduralRenderer3|null, element: RElement, prop: string,
|
(renderer: Renderer3|ProceduralRenderer3|null, element: RElement, prop: string, value: any,
|
||||||
value: string|null, bindingIndex?: number|null): void;
|
bindingIndex?: number|null): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -419,12 +512,12 @@ export interface StylingMapArray extends Array<{}|string|number|null> {
|
||||||
* An index of position and offset points for any data stored within a `StylingMapArray` instance.
|
* An index of position and offset points for any data stored within a `StylingMapArray` instance.
|
||||||
*/
|
*/
|
||||||
export const enum StylingMapArrayIndex {
|
export const enum StylingMapArrayIndex {
|
||||||
/** The location of the raw key/value map instance used last to populate the array entries */
|
|
||||||
RawValuePosition = 0,
|
|
||||||
|
|
||||||
/** Where the values start in the array */
|
/** Where the values start in the array */
|
||||||
ValuesStartPosition = 1,
|
ValuesStartPosition = 1,
|
||||||
|
|
||||||
|
/** The location of the raw key/value map instance used last to populate the array entries */
|
||||||
|
RawValuePosition = 0,
|
||||||
|
|
||||||
/** The size of each property/value entry */
|
/** The size of each property/value entry */
|
||||||
TupleSize = 2,
|
TupleSize = 2,
|
||||||
|
|
||||||
|
@ -458,8 +551,9 @@ export const enum StylingMapArrayIndex {
|
||||||
*/
|
*/
|
||||||
export interface SyncStylingMapsFn {
|
export interface SyncStylingMapsFn {
|
||||||
(context: TStylingContext, renderer: Renderer3|ProceduralRenderer3|null, element: RElement,
|
(context: TStylingContext, renderer: Renderer3|ProceduralRenderer3|null, element: RElement,
|
||||||
data: LStylingData, applyStylingFn: ApplyStylingFn, sanitizer: StyleSanitizeFn|null,
|
data: LStylingData, sourceIndex: number, applyStylingFn: ApplyStylingFn,
|
||||||
mode: StylingMapsSyncMode, targetProp?: string|null, defaultValue?: string|null): boolean;
|
sanitizer: StyleSanitizeFn|null, mode: StylingMapsSyncMode, targetProp?: string|null,
|
||||||
|
defaultValue?: boolean|string|null): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -477,4 +571,10 @@ export const enum StylingMapsSyncMode {
|
||||||
|
|
||||||
/** Skip applying the target prop/value entry */
|
/** Skip applying the target prop/value entry */
|
||||||
SkipTargetProp = 0b100,
|
SkipTargetProp = 0b100,
|
||||||
|
|
||||||
|
/** Iterate over inner maps map values in the context */
|
||||||
|
RecurseInnerMaps = 0b1000,
|
||||||
|
|
||||||
|
/** Only check to see if a value was set somewhere in each map */
|
||||||
|
CheckValuesOnly = 0b10000,
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,13 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
import {unwrapSafeValue} from '../../sanitization/bypass';
|
||||||
import {StyleSanitizeFn, StyleSanitizeMode} from '../../sanitization/style_sanitizer';
|
import {StyleSanitizeFn, StyleSanitizeMode} from '../../sanitization/style_sanitizer';
|
||||||
import {ProceduralRenderer3, RElement, Renderer3} from '../interfaces/renderer';
|
import {ProceduralRenderer3, RElement, Renderer3} from '../interfaces/renderer';
|
||||||
|
|
||||||
import {setStylingMapsSyncFn} from './bindings';
|
import {setStylingMapsSyncFn} from './bindings';
|
||||||
import {ApplyStylingFn, LStylingData, StylingMapArray, StylingMapArrayIndex, StylingMapsSyncMode, SyncStylingMapsFn, TStylingContext, TStylingContextIndex} from './interfaces';
|
import {ApplyStylingFn, LStylingData, StylingMapArray, StylingMapArrayIndex, StylingMapsSyncMode, SyncStylingMapsFn, TStylingContext, TStylingContextIndex} from './interfaces';
|
||||||
import {concatString, getBindingValue, getMapProp, getMapValue, getValuesCount, hyphenate, isStylingValueDefined, setMapValue} from './util';
|
import {getBindingValue, getMapProp, getMapValue, getValue, getValuesCount, isStylingValueDefined} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,6 +24,13 @@ import {concatString, getBindingValue, getMapProp, getMapValue, getValuesCount,
|
||||||
* --------
|
* --------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables support for map-based styling bindings (e.g. `[style]` and `[class]` bindings).
|
||||||
|
*/
|
||||||
|
export function activateStylingMapFeature() {
|
||||||
|
setStylingMapsSyncFn(syncStylingMap);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to apply styling values presently within any map-based bindings on an element.
|
* Used to apply styling values presently within any map-based bindings on an element.
|
||||||
*
|
*
|
||||||
|
@ -53,7 +61,7 @@ import {concatString, getBindingValue, getMapProp, getMapValue, getValuesCount,
|
||||||
* value is marked as dirty.
|
* value is marked as dirty.
|
||||||
*
|
*
|
||||||
* Styling values are applied once CD exits the element (which happens when
|
* Styling values are applied once CD exits the element (which happens when
|
||||||
* the `select(n)` instruction is called or the template function exits). When
|
* the `advance(n)` instruction is called or the template function exits). When
|
||||||
* this occurs, all prop-based bindings are applied. If a map-based binding is
|
* this occurs, all prop-based bindings are applied. If a map-based binding is
|
||||||
* present then a special flushing function (called a sync function) is made
|
* present then a special flushing function (called a sync function) is made
|
||||||
* available and it will be called each time a styling property is flushed.
|
* available and it will be called each time a styling property is flushed.
|
||||||
|
@ -105,14 +113,14 @@ import {concatString, getBindingValue, getMapProp, getMapValue, getValuesCount,
|
||||||
*/
|
*/
|
||||||
export const syncStylingMap: SyncStylingMapsFn =
|
export const syncStylingMap: SyncStylingMapsFn =
|
||||||
(context: TStylingContext, renderer: Renderer3 | ProceduralRenderer3 | null, element: RElement,
|
(context: TStylingContext, renderer: Renderer3 | ProceduralRenderer3 | null, element: RElement,
|
||||||
data: LStylingData, applyStylingFn: ApplyStylingFn, sanitizer: StyleSanitizeFn | null,
|
data: LStylingData, sourceIndex: number, applyStylingFn: ApplyStylingFn,
|
||||||
mode: StylingMapsSyncMode, targetProp?: string | null,
|
sanitizer: StyleSanitizeFn | null, mode: StylingMapsSyncMode, targetProp?: string | null,
|
||||||
defaultValue?: string | null): boolean => {
|
defaultValue?: string | boolean | null): boolean => {
|
||||||
let targetPropValueWasApplied = false;
|
let targetPropValueWasApplied = false;
|
||||||
|
|
||||||
// once the map-based styling code is activate it is never deactivated. For this reason a
|
// once the map-based styling code is activate it is never deactivated. For this reason a
|
||||||
// check to see if the current styling context has any map based bindings is required.
|
// check to see if the current styling context has any map based bindings is required.
|
||||||
const totalMaps = getValuesCount(context, TStylingContextIndex.MapBindingsPosition);
|
const totalMaps = getValuesCount(context);
|
||||||
if (totalMaps) {
|
if (totalMaps) {
|
||||||
let runTheSyncAlgorithm = true;
|
let runTheSyncAlgorithm = true;
|
||||||
const loopUntilEnd = !targetProp;
|
const loopUntilEnd = !targetProp;
|
||||||
|
@ -121,7 +129,7 @@ export const syncStylingMap: SyncStylingMapsFn =
|
||||||
// hasn't been flagged to apply values (it only traverses values) then
|
// hasn't been flagged to apply values (it only traverses values) then
|
||||||
// there is no point in iterating over the array because nothing will
|
// there is no point in iterating over the array because nothing will
|
||||||
// be applied to the element.
|
// be applied to the element.
|
||||||
if (loopUntilEnd && (mode & ~StylingMapsSyncMode.ApplyAllValues)) {
|
if (loopUntilEnd && (mode & StylingMapsSyncMode.ApplyAllValues) === 0) {
|
||||||
runTheSyncAlgorithm = false;
|
runTheSyncAlgorithm = false;
|
||||||
targetPropValueWasApplied = true;
|
targetPropValueWasApplied = true;
|
||||||
}
|
}
|
||||||
|
@ -129,7 +137,7 @@ export const syncStylingMap: SyncStylingMapsFn =
|
||||||
if (runTheSyncAlgorithm) {
|
if (runTheSyncAlgorithm) {
|
||||||
targetPropValueWasApplied = innerSyncStylingMap(
|
targetPropValueWasApplied = innerSyncStylingMap(
|
||||||
context, renderer, element, data, applyStylingFn, sanitizer, mode, targetProp || null,
|
context, renderer, element, data, applyStylingFn, sanitizer, mode, targetProp || null,
|
||||||
0, defaultValue || null);
|
sourceIndex, defaultValue || null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loopUntilEnd) {
|
if (loopUntilEnd) {
|
||||||
|
@ -153,15 +161,27 @@ function innerSyncStylingMap(
|
||||||
context: TStylingContext, renderer: Renderer3 | ProceduralRenderer3 | null, element: RElement,
|
context: TStylingContext, renderer: Renderer3 | ProceduralRenderer3 | null, element: RElement,
|
||||||
data: LStylingData, applyStylingFn: ApplyStylingFn, sanitizer: StyleSanitizeFn | null,
|
data: LStylingData, applyStylingFn: ApplyStylingFn, sanitizer: StyleSanitizeFn | null,
|
||||||
mode: StylingMapsSyncMode, targetProp: string | null, currentMapIndex: number,
|
mode: StylingMapsSyncMode, targetProp: string | null, currentMapIndex: number,
|
||||||
defaultValue: string | null): boolean {
|
defaultValue: string | boolean | null): boolean {
|
||||||
let targetPropValueWasApplied = false;
|
const totalMaps = getValuesCount(context) - 1; // maps have no default value
|
||||||
const totalMaps = getValuesCount(context, TStylingContextIndex.MapBindingsPosition);
|
const mapsLimit = totalMaps - 1;
|
||||||
if (currentMapIndex < totalMaps) {
|
const recurseInnerMaps =
|
||||||
const bindingIndex = getBindingValue(
|
currentMapIndex < mapsLimit && (mode & StylingMapsSyncMode.RecurseInnerMaps) !== 0;
|
||||||
context, TStylingContextIndex.MapBindingsPosition, currentMapIndex) as number;
|
const checkValuesOnly = (mode & StylingMapsSyncMode.CheckValuesOnly) !== 0;
|
||||||
const stylingMapArr = data[bindingIndex] as StylingMapArray;
|
|
||||||
|
|
||||||
|
if (checkValuesOnly) {
|
||||||
|
// inner modes do not check values ever (that can only happen
|
||||||
|
// when sourceIndex === 0)
|
||||||
|
mode &= ~StylingMapsSyncMode.CheckValuesOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
let targetPropValueWasApplied = false;
|
||||||
|
if (currentMapIndex <= mapsLimit) {
|
||||||
let cursor = getCurrentSyncCursor(currentMapIndex);
|
let cursor = getCurrentSyncCursor(currentMapIndex);
|
||||||
|
const bindingIndex = getBindingValue(
|
||||||
|
context, TStylingContextIndex.ValuesStartPosition, currentMapIndex) as number;
|
||||||
|
const stylingMapArr = getValue<StylingMapArray>(data, bindingIndex);
|
||||||
|
|
||||||
|
if (stylingMapArr) {
|
||||||
while (cursor < stylingMapArr.length) {
|
while (cursor < stylingMapArr.length) {
|
||||||
const prop = getMapProp(stylingMapArr, cursor);
|
const prop = getMapProp(stylingMapArr, cursor);
|
||||||
const iteratedTooFar = targetProp && prop > targetProp;
|
const iteratedTooFar = targetProp && prop > targetProp;
|
||||||
|
@ -177,10 +197,13 @@ function innerSyncStylingMap(
|
||||||
// even if the code has iterated too far.
|
// even if the code has iterated too far.
|
||||||
const innerMode =
|
const innerMode =
|
||||||
iteratedTooFar ? mode : resolveInnerMapMode(mode, valueIsDefined, isTargetPropMatched);
|
iteratedTooFar ? mode : resolveInnerMapMode(mode, valueIsDefined, isTargetPropMatched);
|
||||||
|
|
||||||
const innerProp = iteratedTooFar ? targetProp : prop;
|
const innerProp = iteratedTooFar ? targetProp : prop;
|
||||||
let valueApplied = innerSyncStylingMap(
|
let valueApplied = recurseInnerMaps ?
|
||||||
|
innerSyncStylingMap(
|
||||||
context, renderer, element, data, applyStylingFn, sanitizer, innerMode, innerProp,
|
context, renderer, element, data, applyStylingFn, sanitizer, innerMode, innerProp,
|
||||||
currentMapIndex + 1, defaultValue);
|
currentMapIndex + 1, defaultValue) :
|
||||||
|
false;
|
||||||
|
|
||||||
if (iteratedTooFar) {
|
if (iteratedTooFar) {
|
||||||
if (!targetPropValueWasApplied) {
|
if (!targetPropValueWasApplied) {
|
||||||
|
@ -190,14 +213,23 @@ function innerSyncStylingMap(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valueApplied && isValueAllowedToBeApplied(mode, isTargetPropMatched)) {
|
if (!valueApplied && isValueAllowedToBeApplied(mode, isTargetPropMatched)) {
|
||||||
const useDefault = isTargetPropMatched && !valueIsDefined;
|
|
||||||
const valueToApply = useDefault ? defaultValue : value;
|
|
||||||
const bindingIndexToApply = useDefault ? bindingIndex : null;
|
|
||||||
const finalValue = sanitizer ?
|
|
||||||
sanitizer(prop, valueToApply, StyleSanitizeMode.ValidateAndSanitize) :
|
|
||||||
valueToApply;
|
|
||||||
applyStylingFn(renderer, element, prop, finalValue, bindingIndexToApply);
|
|
||||||
valueApplied = true;
|
valueApplied = true;
|
||||||
|
|
||||||
|
if (!checkValuesOnly) {
|
||||||
|
const useDefault = isTargetPropMatched && !valueIsDefined;
|
||||||
|
const bindingIndexToApply = isTargetPropMatched ? bindingIndex : null;
|
||||||
|
|
||||||
|
let finalValue: any;
|
||||||
|
if (useDefault) {
|
||||||
|
finalValue = defaultValue;
|
||||||
|
} else {
|
||||||
|
finalValue = sanitizer ?
|
||||||
|
sanitizer(prop, value, StyleSanitizeMode.ValidateAndSanitize) :
|
||||||
|
(value ? unwrapSafeValue(value) : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyStylingFn(renderer, element, prop, finalValue, bindingIndexToApply);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
targetPropValueWasApplied = valueApplied && isTargetPropMatched;
|
targetPropValueWasApplied = valueApplied && isTargetPropMatched;
|
||||||
|
@ -211,25 +243,22 @@ function innerSyncStylingMap(
|
||||||
// through all of the properties in the remaining maps as well. If the current
|
// through all of the properties in the remaining maps as well. If the current
|
||||||
// styling map is too short then there are no values to iterate over. In either
|
// styling map is too short then there are no values to iterate over. In either
|
||||||
// case the follow-up maps need to be iterated over.
|
// case the follow-up maps need to be iterated over.
|
||||||
if (stylingMapArr.length === StylingMapArrayIndex.ValuesStartPosition || !targetProp) {
|
if (recurseInnerMaps &&
|
||||||
return innerSyncStylingMap(
|
(stylingMapArr.length === StylingMapArrayIndex.ValuesStartPosition || !targetProp)) {
|
||||||
|
targetPropValueWasApplied = innerSyncStylingMap(
|
||||||
|
context, renderer, element, data, applyStylingFn, sanitizer, mode, targetProp,
|
||||||
|
currentMapIndex + 1, defaultValue);
|
||||||
|
}
|
||||||
|
} else if (recurseInnerMaps) {
|
||||||
|
targetPropValueWasApplied = innerSyncStylingMap(
|
||||||
context, renderer, element, data, applyStylingFn, sanitizer, mode, targetProp,
|
context, renderer, element, data, applyStylingFn, sanitizer, mode, targetProp,
|
||||||
currentMapIndex + 1, defaultValue);
|
currentMapIndex + 1, defaultValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return targetPropValueWasApplied;
|
return targetPropValueWasApplied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables support for map-based styling bindings (e.g. `[style]` and `[class]` bindings).
|
|
||||||
*/
|
|
||||||
export function activateStylingMapFeature() {
|
|
||||||
setStylingMapsSyncFn(syncStylingMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to determine the mode for the inner recursive call.
|
* Used to determine the mode for the inner recursive call.
|
||||||
*
|
*
|
||||||
|
@ -276,8 +305,8 @@ function resolveInnerMapMode(
|
||||||
* - But do not allow if the current prop is set to be skipped.
|
* - But do not allow if the current prop is set to be skipped.
|
||||||
* 2. Otherwise if the current prop is permitted then allow.
|
* 2. Otherwise if the current prop is permitted then allow.
|
||||||
*/
|
*/
|
||||||
function isValueAllowedToBeApplied(mode: number, isTargetPropMatched: boolean) {
|
function isValueAllowedToBeApplied(mode: StylingMapsSyncMode, isTargetPropMatched: boolean) {
|
||||||
let doApplyValue = (mode & StylingMapsSyncMode.ApplyAllValues) > 0;
|
let doApplyValue = (mode & StylingMapsSyncMode.ApplyAllValues) !== 0;
|
||||||
if (!doApplyValue) {
|
if (!doApplyValue) {
|
||||||
if (mode & StylingMapsSyncMode.ApplyTargetProp) {
|
if (mode & StylingMapsSyncMode.ApplyTargetProp) {
|
||||||
doApplyValue = isTargetPropMatched;
|
doApplyValue = isTargetPropMatched;
|
||||||
|
@ -320,126 +349,3 @@ function getCurrentSyncCursor(mapIndex: number) {
|
||||||
function setCurrentSyncCursor(mapIndex: number, indexValue: number) {
|
function setCurrentSyncCursor(mapIndex: number, indexValue: number) {
|
||||||
MAP_CURSORS[mapIndex] = indexValue;
|
MAP_CURSORS[mapIndex] = indexValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to convert a {key:value} map into a `StylingMapArray` array.
|
|
||||||
*
|
|
||||||
* This function will either generate a new `StylingMapArray` instance
|
|
||||||
* or it will patch the provided `newValues` map value into an
|
|
||||||
* existing `StylingMapArray` value (this only happens if `bindingValue`
|
|
||||||
* is an instance of `StylingMapArray`).
|
|
||||||
*
|
|
||||||
* If a new key/value map is provided with an old `StylingMapArray`
|
|
||||||
* value then all properties will be overwritten with their new
|
|
||||||
* values or with `null`. This means that the array will never
|
|
||||||
* shrink in size (but it will also not be created and thrown
|
|
||||||
* away whenever the {key:value} map entries change).
|
|
||||||
*/
|
|
||||||
export function normalizeIntoStylingMap(
|
|
||||||
bindingValue: null | StylingMapArray,
|
|
||||||
newValues: {[key: string]: any} | string | null | undefined,
|
|
||||||
normalizeProps?: boolean): StylingMapArray {
|
|
||||||
const stylingMapArr: StylingMapArray = Array.isArray(bindingValue) ? bindingValue : [null];
|
|
||||||
stylingMapArr[StylingMapArrayIndex.RawValuePosition] = newValues || null;
|
|
||||||
|
|
||||||
// because the new values may not include all the properties
|
|
||||||
// that the old ones had, all values are set to `null` before
|
|
||||||
// the new values are applied. This way, when flushed, the
|
|
||||||
// styling algorithm knows exactly what style/class values
|
|
||||||
// to remove from the element (since they are `null`).
|
|
||||||
for (let j = StylingMapArrayIndex.ValuesStartPosition; j < stylingMapArr.length;
|
|
||||||
j += StylingMapArrayIndex.TupleSize) {
|
|
||||||
setMapValue(stylingMapArr, j, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
let props: string[]|null = null;
|
|
||||||
let map: {[key: string]: any}|undefined|null;
|
|
||||||
let allValuesTrue = false;
|
|
||||||
if (typeof newValues === 'string') { // [class] bindings allow string values
|
|
||||||
if (newValues.length) {
|
|
||||||
props = newValues.split(/\s+/);
|
|
||||||
allValuesTrue = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
props = newValues ? Object.keys(newValues) : null;
|
|
||||||
map = newValues;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props) {
|
|
||||||
for (let i = 0; i < props.length; i++) {
|
|
||||||
const prop = props[i] as string;
|
|
||||||
const newProp = normalizeProps ? hyphenate(prop) : prop;
|
|
||||||
const value = allValuesTrue ? true : map ![prop];
|
|
||||||
addItemToStylingMap(stylingMapArr, newProp, value, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stylingMapArr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inserts the provided item into the provided styling array at the right spot.
|
|
||||||
*
|
|
||||||
* The `StylingMapArray` type is a sorted key/value array of entries. This means
|
|
||||||
* that when a new entry is inserted it must be placed at the right spot in the
|
|
||||||
* array. This function figures out exactly where to place it.
|
|
||||||
*/
|
|
||||||
export function addItemToStylingMap(
|
|
||||||
stylingMapArr: StylingMapArray, prop: string, value: string | boolean | null,
|
|
||||||
allowOverwrite?: boolean) {
|
|
||||||
for (let j = StylingMapArrayIndex.ValuesStartPosition; j < stylingMapArr.length;
|
|
||||||
j += StylingMapArrayIndex.TupleSize) {
|
|
||||||
const propAtIndex = getMapProp(stylingMapArr, j);
|
|
||||||
if (prop <= propAtIndex) {
|
|
||||||
let applied = false;
|
|
||||||
if (propAtIndex === prop) {
|
|
||||||
const valueAtIndex = stylingMapArr[j];
|
|
||||||
if (allowOverwrite || !isStylingValueDefined(valueAtIndex)) {
|
|
||||||
applied = true;
|
|
||||||
setMapValue(stylingMapArr, j, value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
applied = true;
|
|
||||||
stylingMapArr.splice(j, 0, prop, value);
|
|
||||||
}
|
|
||||||
return applied;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stylingMapArr.push(prop, value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the provided styling map array into a string.
|
|
||||||
*
|
|
||||||
* Classes => `one two three`
|
|
||||||
* Styles => `prop:value; prop2:value2`
|
|
||||||
*/
|
|
||||||
export function stylingMapToString(map: StylingMapArray, isClassBased: boolean): string {
|
|
||||||
let str = '';
|
|
||||||
for (let i = StylingMapArrayIndex.ValuesStartPosition; i < map.length;
|
|
||||||
i += StylingMapArrayIndex.TupleSize) {
|
|
||||||
const prop = getMapProp(map, i);
|
|
||||||
const value = getMapValue(map, i) as string;
|
|
||||||
const attrValue = concatString(prop, isClassBased ? '' : value, ':');
|
|
||||||
str = concatString(str, attrValue, isClassBased ? ' ' : '; ');
|
|
||||||
}
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the provided styling map array into a key value map.
|
|
||||||
*/
|
|
||||||
export function stylingMapToStringMap(map: StylingMapArray | null): {[key: string]: any} {
|
|
||||||
let stringMap: {[key: string]: any} = {};
|
|
||||||
if (map) {
|
|
||||||
for (let i = StylingMapArrayIndex.ValuesStartPosition; i < map.length;
|
|
||||||
i += StylingMapArrayIndex.TupleSize) {
|
|
||||||
const prop = getMapProp(map, i);
|
|
||||||
const value = getMapValue(map, i) as string;
|
|
||||||
stringMap[prop] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return stringMap;
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* 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.
|
* This file contains all state-based logic for styling in Angular.
|
||||||
*
|
*
|
||||||
* Styling in Angular is evaluated with a series of styling-specific
|
* 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
|
* exited in change detection (once all the instructions are run for
|
||||||
* that element).
|
* 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`.
|
* 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.
|
* 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 {
|
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;
|
classesBitMask: number;
|
||||||
|
|
||||||
|
/** The classes update bit index value that is processed during each class binding */
|
||||||
classesIndex: number;
|
classesIndex: number;
|
||||||
|
|
||||||
|
/** The styles update bit mask value that is processed during each style binding */
|
||||||
stylesBitMask: number;
|
stylesBitMask: number;
|
||||||
|
|
||||||
|
/** The styles update bit index value that is processed during each style binding */
|
||||||
stylesIndex: number;
|
stylesIndex: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const STYLING_INDEX_START_VALUE = 1;
|
// these values will get filled in the very first time this is accessed...
|
||||||
export const BIT_MASK_START_VALUE = 0;
|
const _state: StylingState = {
|
||||||
|
element: null,
|
||||||
export function getStylingState(element: any, readFromMap?: boolean): StylingState {
|
directiveIndex: -1,
|
||||||
if (!_stylingElement || element !== _stylingElement) {
|
sourceIndex: -1,
|
||||||
_stylingElement = element;
|
classesBitMask: -1,
|
||||||
if (readFromMap) {
|
classesIndex: -1,
|
||||||
_stylingState = _stateStorage.get(element) || null;
|
stylesBitMask: -1,
|
||||||
ngDevMode && ngDevMode.stylingReadPersistedState++;
|
stylesIndex: -1,
|
||||||
}
|
|
||||||
_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() {
|
export function resetStylingState() {
|
||||||
_stylingState = null;
|
_state.element = 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();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,13 @@
|
||||||
*/
|
*/
|
||||||
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||||
import {RElement} from '../interfaces/renderer';
|
import {RElement} from '../interfaces/renderer';
|
||||||
import {LView} from '../interfaces/view';
|
|
||||||
import {getCurrentStyleSanitizer} from '../state';
|
import {getCurrentStyleSanitizer} from '../state';
|
||||||
import {attachDebugObject} from '../util/debug_utils';
|
import {attachDebugObject} from '../util/debug_utils';
|
||||||
|
|
||||||
import {applyStyling} from './bindings';
|
import {applyStylingViaContext} from './bindings';
|
||||||
import {ApplyStylingFn, LStylingData, TStylingContext, TStylingContextIndex} from './interfaces';
|
import {ApplyStylingFn, LStylingData, TStylingConfig, TStylingContext, TStylingContextIndex} from './interfaces';
|
||||||
import {activateStylingMapFeature} from './map_based_bindings';
|
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 */
|
/** The associated TStylingContext instance */
|
||||||
context: TStylingContext;
|
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
|
* 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
|
* 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;
|
overrideSanitizer(sanitizer: StyleSanitizeFn|null): void;
|
||||||
}
|
}
|
||||||
|
@ -83,9 +94,15 @@ export interface TStylingTupleSummary {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The bit guard mask that is used to compare and protect against
|
* 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
|
* Whether or not the entry requires sanitization
|
||||||
|
@ -93,18 +110,18 @@ export interface TStylingTupleSummary {
|
||||||
sanitizationRequired: boolean;
|
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;
|
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)[];
|
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) {
|
export function attachStylingDebugObject(context: TStylingContext) {
|
||||||
const debug = new TStylingContextDebug(context);
|
const debug = new TStylingContextDebug(context);
|
||||||
|
@ -121,7 +138,8 @@ export function attachStylingDebugObject(context: TStylingContext) {
|
||||||
class TStylingContextDebug {
|
class TStylingContextDebug {
|
||||||
constructor(public readonly context: TStylingContext) {}
|
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.
|
* Returns a detailed summary of each styling entry in the context.
|
||||||
|
@ -130,30 +148,36 @@ class TStylingContextDebug {
|
||||||
*/
|
*/
|
||||||
get entries(): {[prop: string]: TStylingTupleSummary} {
|
get entries(): {[prop: string]: TStylingTupleSummary} {
|
||||||
const context = this.context;
|
const context = this.context;
|
||||||
|
const totalColumns = getValuesCount(context);
|
||||||
const entries: {[prop: string]: TStylingTupleSummary} = {};
|
const entries: {[prop: string]: TStylingTupleSummary} = {};
|
||||||
const start = TStylingContextIndex.MapBindingsPosition;
|
const start = getPropValuesStartPosition(context);
|
||||||
let i = start;
|
let i = start;
|
||||||
while (i < context.length) {
|
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 prop = getProp(context, i);
|
||||||
const guardMask = getGuardMask(context, i);
|
const templateBitMask = getGuardMask(context, i, false);
|
||||||
|
const hostBindingsBitMask = getGuardMask(context, i, true);
|
||||||
const defaultValue = getDefaultValue(context, i);
|
const defaultValue = getDefaultValue(context, i);
|
||||||
const sanitizationRequired = isSanitizationRequired(context, i);
|
const sanitizationRequired = isSanitizationRequired(context, i);
|
||||||
const bindingsStartPosition = i + TStylingContextIndex.BindingsStartOffset;
|
const bindingsStartPosition = i + TStylingContextIndex.BindingsStartOffset;
|
||||||
|
|
||||||
const sources: (number | string | null)[] = [];
|
const sources: (number | string | null)[] = [];
|
||||||
for (let j = 0; j < valuesCount; j++) {
|
|
||||||
sources.push(context[bindingsStartPosition + j] as 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};
|
entries[prop] = {
|
||||||
}
|
prop,
|
||||||
|
templateBitMask,
|
||||||
|
hostBindingsBitMask,
|
||||||
|
sanitizationRequired,
|
||||||
|
valuesCount: sources.length, defaultValue, sources,
|
||||||
|
};
|
||||||
|
|
||||||
i += TStylingContextIndex.BindingsStartOffset + valuesCount;
|
i += TStylingContextIndex.BindingsStartOffset + totalColumns;
|
||||||
}
|
}
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
@ -191,6 +215,29 @@ export class NodeStylingDebug implements DebugStyling {
|
||||||
return entries;
|
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.
|
* 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
|
// element is only used when the styling algorithm attempts to
|
||||||
// style the value (and we mock out the stylingApplyFn anyway).
|
// style the value (and we mock out the stylingApplyFn anyway).
|
||||||
const mockElement = {} as any;
|
const mockElement = {} as any;
|
||||||
const hasMaps = getValuesCount(this.context, TStylingContextIndex.MapBindingsPosition) > 0;
|
const hasMaps = hasConfig(this.context, TStylingConfig.HasMapBindings);
|
||||||
if (hasMaps) {
|
if (hasMaps) {
|
||||||
activateStylingMapFeature();
|
activateStylingMapFeature();
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapFn: ApplyStylingFn =
|
const mapFn: ApplyStylingFn =
|
||||||
(renderer: any, element: RElement, prop: string, value: string | null,
|
(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());
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,35 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
import {unwrapSafeValue} from '../../sanitization/bypass';
|
||||||
import {TNode, TNodeFlags} from '../interfaces/node';
|
import {TNode, TNodeFlags} from '../interfaces/node';
|
||||||
import {NO_CHANGE} from '../tokens';
|
import {NO_CHANGE} from '../tokens';
|
||||||
import {StylingMapArray, StylingMapArrayIndex, TStylingConfigFlags, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags} from './interfaces';
|
|
||||||
|
|
||||||
const MAP_BASED_ENTRY_PROP_NAME = '--MAP--';
|
import {LStylingData, StylingMapArray, StylingMapArrayIndex, TStylingConfig, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags} from './interfaces';
|
||||||
const TEMPLATE_DIRECTIVE_INDEX = 0;
|
|
||||||
|
export const MAP_BASED_ENTRY_PROP_NAME = '[MAP]';
|
||||||
|
export const TEMPLATE_DIRECTIVE_INDEX = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default fallback value for a styling binding.
|
||||||
|
*
|
||||||
|
* A value of `null` is used here which signals to the styling algorithm that
|
||||||
|
* the styling value is not present. This way if there are no other values
|
||||||
|
* detected then it will be removed once the style/class property is dirty and
|
||||||
|
* diffed within the styling algorithm present in `flushStyling`.
|
||||||
|
*/
|
||||||
|
export const DEFAULT_BINDING_VALUE = null;
|
||||||
|
|
||||||
|
export const DEFAULT_BINDING_INDEX = 0;
|
||||||
|
|
||||||
|
const DEFAULT_TOTAL_SOURCES = 1;
|
||||||
|
|
||||||
|
// The first bit value reflects a map-based binding value's bit.
|
||||||
|
// The reason why it's always activated for every entry in the map
|
||||||
|
// is so that if any map-binding values update then all other prop
|
||||||
|
// based bindings will pass the guard check automatically without
|
||||||
|
// any extra code or flags.
|
||||||
|
export const DEFAULT_GUARD_MASK_VALUE = 0b1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance of the `TStylingContext`.
|
* Creates a new instance of the `TStylingContext`.
|
||||||
|
@ -21,84 +44,83 @@ const TEMPLATE_DIRECTIVE_INDEX = 0;
|
||||||
* `TStylingContext` with the initial values (see `interfaces.ts` for more info).
|
* `TStylingContext` with the initial values (see `interfaces.ts` for more info).
|
||||||
*/
|
*/
|
||||||
export function allocTStylingContext(initialStyling?: StylingMapArray | null): TStylingContext {
|
export function allocTStylingContext(initialStyling?: StylingMapArray | null): TStylingContext {
|
||||||
// because map-based bindings deal with a dynamic set of values, there
|
initialStyling = initialStyling || allocStylingMapArray();
|
||||||
// is no way to know ahead of time whether or not sanitization is required.
|
|
||||||
// For this reason the configuration will always mark sanitization as active
|
|
||||||
// (this means that when map-based values are applied then sanitization will
|
|
||||||
// be checked against each property).
|
|
||||||
const mapBasedConfig = TStylingContextPropConfigFlags.SanitizationRequired;
|
|
||||||
return [
|
return [
|
||||||
initialStyling || [''], // empty initial-styling map value
|
TStylingConfig.Initial, // 1) config for the styling context
|
||||||
TStylingConfigFlags.Initial,
|
DEFAULT_TOTAL_SOURCES, // 2) total amount of styling sources (template, directives, etc...)
|
||||||
TEMPLATE_DIRECTIVE_INDEX,
|
initialStyling, // 3) initial styling values
|
||||||
mapBasedConfig,
|
|
||||||
0,
|
|
||||||
MAP_BASED_ENTRY_PROP_NAME,
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function allocStylingMapArray(): StylingMapArray {
|
||||||
* Sets the provided directive as the last directive index in the provided `TStylingContext`.
|
return [''];
|
||||||
*
|
|
||||||
* Styling in Angular can be applied from the template as well as multiple sources of
|
|
||||||
* host bindings. This means that each binding function (the template function or the
|
|
||||||
* hostBindings functions) will generate styling instructions as well as a styling
|
|
||||||
* apply function (i.e. `stylingApply()`). Because host bindings functions and the
|
|
||||||
* template function are independent from one another this means that the styling apply
|
|
||||||
* function will be called multiple times. By tracking the last directive index (which
|
|
||||||
* is what happens in this function) the styling algorithm knows exactly when to flush
|
|
||||||
* styling (which is when the last styling apply function is executed).
|
|
||||||
*/
|
|
||||||
export function updateLastDirectiveIndex(
|
|
||||||
context: TStylingContext, lastDirectiveIndex: number): void {
|
|
||||||
if (lastDirectiveIndex === TEMPLATE_DIRECTIVE_INDEX) {
|
|
||||||
const currentValue = context[TStylingContextIndex.LastDirectiveIndexPosition];
|
|
||||||
if (currentValue > TEMPLATE_DIRECTIVE_INDEX) {
|
|
||||||
// This means that a directive or two contained a host bindings function, but
|
|
||||||
// now the template function also contains styling. When this combination of sources
|
|
||||||
// comes up then we need to tell the context to store the state between updates
|
|
||||||
// (because host bindings evaluation happens after template binding evaluation).
|
|
||||||
markContextToPersistState(context);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
context[TStylingContextIndex.LastDirectiveIndexPosition] = lastDirectiveIndex;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getConfig(context: TStylingContext) {
|
export function getConfig(context: TStylingContext) {
|
||||||
return context[TStylingContextIndex.ConfigPosition];
|
return context[TStylingContextIndex.ConfigPosition];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setConfig(context: TStylingContext, value: number) {
|
export function hasConfig(context: TStylingContext, flag: TStylingConfig) {
|
||||||
|
return (getConfig(context) & flag) !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether or not to apply styles/classes directly or via context resolution.
|
||||||
|
*
|
||||||
|
* There are three cases that are matched here:
|
||||||
|
* 1. context is locked for template or host bindings (depending on `hostBindingsMode`)
|
||||||
|
* 2. There are no collisions (i.e. properties with more than one binding)
|
||||||
|
* 3. There are only "prop" or "map" bindings present, but not both
|
||||||
|
*/
|
||||||
|
export function allowDirectStyling(context: TStylingContext, hostBindingsMode: boolean): boolean {
|
||||||
|
const config = getConfig(context);
|
||||||
|
return ((config & getLockedConfig(hostBindingsMode)) !== 0) &&
|
||||||
|
((config & TStylingConfig.HasCollisions) === 0) &&
|
||||||
|
((config & TStylingConfig.HasPropAndMapBindings) !== TStylingConfig.HasPropAndMapBindings);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setConfig(context: TStylingContext, value: TStylingConfig): void {
|
||||||
context[TStylingContextIndex.ConfigPosition] = value;
|
context[TStylingContextIndex.ConfigPosition] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getProp(context: TStylingContext, index: number) {
|
export function patchConfig(context: TStylingContext, flag: TStylingConfig): void {
|
||||||
|
context[TStylingContextIndex.ConfigPosition] |= flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getProp(context: TStylingContext, index: number): string {
|
||||||
return context[index + TStylingContextIndex.PropOffset] as string;
|
return context[index + TStylingContextIndex.PropOffset] as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPropConfig(context: TStylingContext, index: number): number {
|
function getPropConfig(context: TStylingContext, index: number): number {
|
||||||
return (context[index + TStylingContextIndex.ConfigAndGuardOffset] as number) &
|
return (context[index + TStylingContextIndex.ConfigOffset] as number) &
|
||||||
TStylingContextPropConfigFlags.Mask;
|
TStylingContextPropConfigFlags.Mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSanitizationRequired(context: TStylingContext, index: number) {
|
export function isSanitizationRequired(context: TStylingContext, index: number): boolean {
|
||||||
return (getPropConfig(context, index) & TStylingContextPropConfigFlags.SanitizationRequired) > 0;
|
return (getPropConfig(context, index) & TStylingContextPropConfigFlags.SanitizationRequired) !==
|
||||||
|
0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGuardMask(context: TStylingContext, index: number) {
|
export function getGuardMask(
|
||||||
const configGuardValue = context[index + TStylingContextIndex.ConfigAndGuardOffset] as number;
|
context: TStylingContext, index: number, isHostBinding: boolean): number {
|
||||||
return configGuardValue >> TStylingContextPropConfigFlags.TotalBits;
|
const position = index + (isHostBinding ? TStylingContextIndex.HostBindingsBitGuardOffset :
|
||||||
|
TStylingContextIndex.TemplateBitGuardOffset);
|
||||||
|
return context[position] as number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setGuardMask(context: TStylingContext, index: number, maskValue: number) {
|
export function setGuardMask(
|
||||||
const config = getPropConfig(context, index);
|
context: TStylingContext, index: number, maskValue: number, isHostBinding: boolean) {
|
||||||
const guardMask = maskValue << TStylingContextPropConfigFlags.TotalBits;
|
const position = index + (isHostBinding ? TStylingContextIndex.HostBindingsBitGuardOffset :
|
||||||
context[index + TStylingContextIndex.ConfigAndGuardOffset] = config | guardMask;
|
TStylingContextIndex.TemplateBitGuardOffset);
|
||||||
|
context[position] = maskValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getValuesCount(context: TStylingContext, index: number) {
|
export function getValuesCount(context: TStylingContext): number {
|
||||||
return context[index + TStylingContextIndex.ValuesCountOffset] as number;
|
return getTotalSources(context) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTotalSources(context: TStylingContext): number {
|
||||||
|
return context[TStylingContextIndex.TotalSourcesPosition];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBindingValue(context: TStylingContext, index: number, offset: number) {
|
export function getBindingValue(context: TStylingContext, index: number, offset: number) {
|
||||||
|
@ -106,39 +128,44 @@ export function getBindingValue(context: TStylingContext, index: number, offset:
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultValue(context: TStylingContext, index: number): string|boolean|null {
|
export function getDefaultValue(context: TStylingContext, index: number): string|boolean|null {
|
||||||
const valuesCount = getValuesCount(context, index);
|
return context[index + TStylingContextIndex.BindingsStartOffset + getTotalSources(context)] as
|
||||||
return context[index + TStylingContextIndex.BindingsStartOffset + valuesCount - 1] as string |
|
string |
|
||||||
boolean | null;
|
boolean | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function setDefaultValue(
|
||||||
* Temporary function which determines whether or not a context is
|
context: TStylingContext, index: number, value: string | boolean | null) {
|
||||||
* allowed to be flushed based on the provided directive index.
|
return context[index + TStylingContextIndex.BindingsStartOffset + getTotalSources(context)] =
|
||||||
*/
|
value;
|
||||||
export function allowStylingFlush(context: TStylingContext | null, index: number) {
|
|
||||||
return (context && index === context[TStylingContextIndex.LastDirectiveIndexPosition]) ? true :
|
|
||||||
false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function lockContext(context: TStylingContext) {
|
export function setValue(data: LStylingData, bindingIndex: number, value: any) {
|
||||||
setConfig(context, getConfig(context) | TStylingConfigFlags.Locked);
|
data[bindingIndex] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isContextLocked(context: TStylingContext): boolean {
|
export function getValue<T = any>(data: LStylingData, bindingIndex: number): T|null {
|
||||||
return (getConfig(context) & TStylingConfigFlags.Locked) > 0;
|
return bindingIndex > 0 ? data[bindingIndex] as T : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stateIsPersisted(context: TStylingContext): boolean {
|
export function lockContext(context: TStylingContext, hostBindingsMode: boolean): void {
|
||||||
return (getConfig(context) & TStylingConfigFlags.PersistStateValues) > 0;
|
patchConfig(context, getLockedConfig(hostBindingsMode));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function markContextToPersistState(context: TStylingContext) {
|
export function isContextLocked(context: TStylingContext, hostBindingsMode: boolean): boolean {
|
||||||
setConfig(context, getConfig(context) | TStylingConfigFlags.PersistStateValues);
|
return hasConfig(context, getLockedConfig(hostBindingsMode));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLockedConfig(hostBindingsMode: boolean) {
|
||||||
|
return hostBindingsMode ? TStylingConfig.HostBindingsLocked :
|
||||||
|
TStylingConfig.TemplateBindingsLocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPropValuesStartPosition(context: TStylingContext) {
|
export function getPropValuesStartPosition(context: TStylingContext) {
|
||||||
return TStylingContextIndex.MapBindingsBindingsStartPosition +
|
let startPosition = TStylingContextIndex.ValuesStartPosition;
|
||||||
context[TStylingContextIndex.MapBindingsValuesCountPosition];
|
if (hasConfig(context, TStylingConfig.HasMapBindings)) {
|
||||||
|
startPosition += TStylingContextIndex.BindingsStartOffset + getValuesCount(context);
|
||||||
|
}
|
||||||
|
return startPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isMapBased(prop: string) {
|
export function isMapBased(prop: string) {
|
||||||
|
@ -197,17 +224,23 @@ export function getStylingMapArray(value: TStylingContext | StylingMapArray | nu
|
||||||
StylingMapArray|null {
|
StylingMapArray|null {
|
||||||
return isStylingContext(value) ?
|
return isStylingContext(value) ?
|
||||||
(value as TStylingContext)[TStylingContextIndex.InitialStylingValuePosition] :
|
(value as TStylingContext)[TStylingContextIndex.InitialStylingValuePosition] :
|
||||||
value;
|
value as StylingMapArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isStylingContext(value: TStylingContext | StylingMapArray | null): boolean {
|
export function isStylingContext(value: TStylingContext | StylingMapArray | null): boolean {
|
||||||
// the StylingMapArray is in the format of [initial, prop, string, prop, string]
|
// the StylingMapArray is in the format of [initial, prop, string, prop, string]
|
||||||
// and this is the defining value to distinguish between arrays
|
// and this is the defining value to distinguish between arrays
|
||||||
return Array.isArray(value) &&
|
return Array.isArray(value) && value.length >= TStylingContextIndex.ValuesStartPosition &&
|
||||||
value.length >= TStylingContextIndex.MapBindingsBindingsStartPosition &&
|
|
||||||
typeof value[1] !== 'string';
|
typeof value[1] !== 'string';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isStylingMapArray(value: TStylingContext | StylingMapArray | null): boolean {
|
||||||
|
// the StylingMapArray is in the format of [initial, prop, string, prop, string]
|
||||||
|
// and this is the defining value to distinguish between arrays
|
||||||
|
return Array.isArray(value) &&
|
||||||
|
(typeof(value as StylingMapArray)[StylingMapArrayIndex.ValuesStartPosition] === 'string');
|
||||||
|
}
|
||||||
|
|
||||||
export function getInitialStylingValue(context: TStylingContext | StylingMapArray | null): string {
|
export function getInitialStylingValue(context: TStylingContext | StylingMapArray | null): string {
|
||||||
const map = getStylingMapArray(context);
|
const map = getStylingMapArray(context);
|
||||||
return map && (map[StylingMapArrayIndex.RawValuePosition] as string | null) || '';
|
return map && (map[StylingMapArrayIndex.RawValuePosition] as string | null) || '';
|
||||||
|
@ -225,6 +258,13 @@ export function getMapProp(map: StylingMapArray, index: number): string {
|
||||||
return map[index + StylingMapArrayIndex.PropOffset] as string;
|
return map[index + StylingMapArrayIndex.PropOffset] as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAP_DIRTY_VALUE =
|
||||||
|
typeof ngDevMode !== 'undefined' && ngDevMode ? {} : {MAP_DIRTY_VALUE: true};
|
||||||
|
|
||||||
|
export function setMapAsDirty(map: StylingMapArray): void {
|
||||||
|
map[StylingMapArrayIndex.RawValuePosition] = MAP_DIRTY_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
export function setMapValue(
|
export function setMapValue(
|
||||||
map: StylingMapArray, index: number, value: string | boolean | null): void {
|
map: StylingMapArray, index: number, value: string | boolean | null): void {
|
||||||
map[index + StylingMapArrayIndex.ValueOffset] = value;
|
map[index + StylingMapArrayIndex.ValueOffset] = value;
|
||||||
|
@ -253,3 +293,130 @@ export function forceStylesAsString(styles: {[key: string]: any} | null | undefi
|
||||||
}
|
}
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isHostStylingActive(directiveOrSourceId: number): boolean {
|
||||||
|
return directiveOrSourceId !== TEMPLATE_DIRECTIVE_INDEX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the provided styling map array into a string.
|
||||||
|
*
|
||||||
|
* Classes => `one two three`
|
||||||
|
* Styles => `prop:value; prop2:value2`
|
||||||
|
*/
|
||||||
|
export function stylingMapToString(map: StylingMapArray, isClassBased: boolean): string {
|
||||||
|
let str = '';
|
||||||
|
for (let i = StylingMapArrayIndex.ValuesStartPosition; i < map.length;
|
||||||
|
i += StylingMapArrayIndex.TupleSize) {
|
||||||
|
const prop = getMapProp(map, i);
|
||||||
|
const value = getMapValue(map, i) as string;
|
||||||
|
const attrValue = concatString(prop, isClassBased ? '' : value, ':');
|
||||||
|
str = concatString(str, attrValue, isClassBased ? ' ' : '; ');
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the provided styling map array into a key value map.
|
||||||
|
*/
|
||||||
|
export function stylingMapToStringMap(map: StylingMapArray | null): {[key: string]: any} {
|
||||||
|
let stringMap: {[key: string]: any} = {};
|
||||||
|
if (map) {
|
||||||
|
for (let i = StylingMapArrayIndex.ValuesStartPosition; i < map.length;
|
||||||
|
i += StylingMapArrayIndex.TupleSize) {
|
||||||
|
const prop = getMapProp(map, i);
|
||||||
|
const value = getMapValue(map, i) as string;
|
||||||
|
stringMap[prop] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stringMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the provided item into the provided styling array at the right spot.
|
||||||
|
*
|
||||||
|
* The `StylingMapArray` type is a sorted key/value array of entries. This means
|
||||||
|
* that when a new entry is inserted it must be placed at the right spot in the
|
||||||
|
* array. This function figures out exactly where to place it.
|
||||||
|
*/
|
||||||
|
export function addItemToStylingMap(
|
||||||
|
stylingMapArr: StylingMapArray, prop: string, value: string | boolean | null,
|
||||||
|
allowOverwrite?: boolean) {
|
||||||
|
for (let j = StylingMapArrayIndex.ValuesStartPosition; j < stylingMapArr.length;
|
||||||
|
j += StylingMapArrayIndex.TupleSize) {
|
||||||
|
const propAtIndex = getMapProp(stylingMapArr, j);
|
||||||
|
if (prop <= propAtIndex) {
|
||||||
|
let applied = false;
|
||||||
|
if (propAtIndex === prop) {
|
||||||
|
const valueAtIndex = stylingMapArr[j];
|
||||||
|
if (allowOverwrite || !isStylingValueDefined(valueAtIndex)) {
|
||||||
|
applied = true;
|
||||||
|
setMapValue(stylingMapArr, j, value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
applied = true;
|
||||||
|
stylingMapArr.splice(j, 0, prop, value);
|
||||||
|
}
|
||||||
|
return applied;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stylingMapArr.push(prop, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to convert a {key:value} map into a `StylingMapArray` array.
|
||||||
|
*
|
||||||
|
* This function will either generate a new `StylingMapArray` instance
|
||||||
|
* or it will patch the provided `newValues` map value into an
|
||||||
|
* existing `StylingMapArray` value (this only happens if `bindingValue`
|
||||||
|
* is an instance of `StylingMapArray`).
|
||||||
|
*
|
||||||
|
* If a new key/value map is provided with an old `StylingMapArray`
|
||||||
|
* value then all properties will be overwritten with their new
|
||||||
|
* values or with `null`. This means that the array will never
|
||||||
|
* shrink in size (but it will also not be created and thrown
|
||||||
|
* away whenever the `{key:value}` map entries change).
|
||||||
|
*/
|
||||||
|
export function normalizeIntoStylingMap(
|
||||||
|
bindingValue: null | StylingMapArray,
|
||||||
|
newValues: {[key: string]: any} | string | null | undefined,
|
||||||
|
normalizeProps?: boolean): StylingMapArray {
|
||||||
|
const stylingMapArr: StylingMapArray = Array.isArray(bindingValue) ? bindingValue : [null];
|
||||||
|
stylingMapArr[StylingMapArrayIndex.RawValuePosition] = newValues || null;
|
||||||
|
|
||||||
|
// because the new values may not include all the properties
|
||||||
|
// that the old ones had, all values are set to `null` before
|
||||||
|
// the new values are applied. This way, when flushed, the
|
||||||
|
// styling algorithm knows exactly what style/class values
|
||||||
|
// to remove from the element (since they are `null`).
|
||||||
|
for (let j = StylingMapArrayIndex.ValuesStartPosition; j < stylingMapArr.length;
|
||||||
|
j += StylingMapArrayIndex.TupleSize) {
|
||||||
|
setMapValue(stylingMapArr, j, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
let props: string[]|null = null;
|
||||||
|
let map: {[key: string]: any}|undefined|null;
|
||||||
|
let allValuesTrue = false;
|
||||||
|
if (typeof newValues === 'string') { // [class] bindings allow string values
|
||||||
|
if (newValues.length) {
|
||||||
|
props = newValues.split(/\s+/);
|
||||||
|
allValuesTrue = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
props = newValues ? Object.keys(newValues) : null;
|
||||||
|
map = newValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props) {
|
||||||
|
for (let i = 0; i < props.length; i++) {
|
||||||
|
const prop = props[i] as string;
|
||||||
|
const newProp = normalizeProps ? hyphenate(prop) : prop;
|
||||||
|
const value = allValuesTrue ? true : map ![prop];
|
||||||
|
addItemToStylingMap(stylingMapArr, newProp, value, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stylingMapArr;
|
||||||
|
}
|
||||||
|
|
|
@ -61,9 +61,7 @@ export interface SafeResourceUrl extends SafeValue {}
|
||||||
|
|
||||||
|
|
||||||
abstract class SafeValueImpl implements SafeValue {
|
abstract class SafeValueImpl implements SafeValue {
|
||||||
constructor(public changingThisBreaksApplicationSecurity: string) {
|
constructor(public changingThisBreaksApplicationSecurity: string) {}
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract getTypeName(): string;
|
abstract getTypeName(): string;
|
||||||
|
|
||||||
|
@ -89,10 +87,10 @@ class SafeResourceUrlImpl extends SafeValueImpl implements SafeResourceUrl {
|
||||||
getTypeName() { return BypassType.ResourceUrl; }
|
getTypeName() { return BypassType.ResourceUrl; }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unwrapSafeValue(value: SafeValue): string {
|
export function unwrapSafeValue(value: string | SafeValue): string {
|
||||||
return value instanceof SafeValueImpl ?
|
return value instanceof SafeValueImpl ?
|
||||||
(value as SafeValueImpl).changingThisBreaksApplicationSecurity :
|
(value as SafeValueImpl).changingThisBreaksApplicationSecurity :
|
||||||
'';
|
(value as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,6 @@ declare global {
|
||||||
flushStyling: number;
|
flushStyling: number;
|
||||||
classesApplied: number;
|
classesApplied: number;
|
||||||
stylesApplied: number;
|
stylesApplied: number;
|
||||||
stylingWritePersistedState: number;
|
|
||||||
stylingReadPersistedState: number;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,8 +85,6 @@ export function ngDevModeResetPerfCounters(): NgDevModePerfCounters {
|
||||||
flushStyling: 0,
|
flushStyling: 0,
|
||||||
classesApplied: 0,
|
classesApplied: 0,
|
||||||
stylesApplied: 0,
|
stylesApplied: 0,
|
||||||
stylingWritePersistedState: 0,
|
|
||||||
stylingReadPersistedState: 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
|
// Make sure to refer to ngDevMode as ['ngDevMode'] for closure.
|
||||||
|
|
|
@ -15,6 +15,8 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
import {onlyInIvy} from '@angular/private/testing';
|
import {onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
describe('new styling integration', () => {
|
describe('new styling integration', () => {
|
||||||
|
beforeEach(() => resetStylingCounters());
|
||||||
|
|
||||||
onlyInIvy('ivy resolves styling across directives, components and templates in unison')
|
onlyInIvy('ivy resolves styling across directives, components and templates in unison')
|
||||||
.it('should apply single property styles/classes to the element and default to any static styling values',
|
.it('should apply single property styles/classes to the element and default to any static styling values',
|
||||||
() => {
|
() => {
|
||||||
|
@ -124,6 +126,74 @@ describe('new styling integration', () => {
|
||||||
expect(element.style.width).toEqual('600px');
|
expect(element.style.width).toEqual('600px');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onlyInIvy('ivy resolves styling across directives, components and templates in unison')
|
||||||
|
.it('should only run stylingFlush once when there are no collisions between styling properties',
|
||||||
|
() => {
|
||||||
|
@Directive({selector: '[dir-with-styling]'})
|
||||||
|
class DirWithStyling {
|
||||||
|
@HostBinding('style.font-size') public fontSize = '100px';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'comp-with-styling'})
|
||||||
|
class CompWithStyling {
|
||||||
|
@HostBinding('style.width') public width = '900px';
|
||||||
|
|
||||||
|
@HostBinding('style.height') public height = '900px';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `
|
||||||
|
<comp-with-styling
|
||||||
|
[style.opacity]="opacity"
|
||||||
|
dir-with-styling>...</comp-with-styling>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
class Cmp {
|
||||||
|
opacity: string|null = '0.5';
|
||||||
|
@ViewChild(CompWithStyling, {static: true})
|
||||||
|
compWithStyling: CompWithStyling|null = null;
|
||||||
|
@ViewChild(DirWithStyling, {static: true}) dirWithStyling: DirWithStyling|null = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({declarations: [Cmp, DirWithStyling, CompWithStyling]});
|
||||||
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const component = fixture.componentInstance;
|
||||||
|
const element = fixture.nativeElement.querySelector('comp-with-styling');
|
||||||
|
const node = getDebugNode(element) !;
|
||||||
|
|
||||||
|
const styles = node.styles !;
|
||||||
|
const config = styles.config;
|
||||||
|
expect(config.hasCollisions).toBeFalsy();
|
||||||
|
expect(config.hasMapBindings).toBeFalsy();
|
||||||
|
expect(config.hasPropBindings).toBeTruthy();
|
||||||
|
expect(config.allowDirectStyling).toBeTruthy();
|
||||||
|
|
||||||
|
expect(element.style.opacity).toEqual('0.5');
|
||||||
|
expect(element.style.width).toEqual('900px');
|
||||||
|
expect(element.style.height).toEqual('900px');
|
||||||
|
expect(element.style.fontSize).toEqual('100px');
|
||||||
|
|
||||||
|
// once for the template flush and again for the host bindings
|
||||||
|
expect(ngDevMode !.flushStyling).toEqual(2);
|
||||||
|
resetStylingCounters();
|
||||||
|
|
||||||
|
component.opacity = '0.6';
|
||||||
|
component.compWithStyling !.height = '100px';
|
||||||
|
component.compWithStyling !.width = '100px';
|
||||||
|
component.dirWithStyling !.fontSize = '50px';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(element.style.opacity).toEqual('0.6');
|
||||||
|
expect(element.style.width).toEqual('100px');
|
||||||
|
expect(element.style.height).toEqual('100px');
|
||||||
|
expect(element.style.fontSize).toEqual('50px');
|
||||||
|
|
||||||
|
// there is no need to flush styling since the styles are applied directly
|
||||||
|
expect(ngDevMode !.flushStyling).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
onlyInIvy('ivy resolves styling across directives, components and templates in unison')
|
onlyInIvy('ivy resolves styling across directives, components and templates in unison')
|
||||||
.it('should combine all styling across the template, directive and component host bindings',
|
.it('should combine all styling across the template, directive and component host bindings',
|
||||||
() => {
|
() => {
|
||||||
|
@ -240,6 +310,7 @@ describe('new styling integration', () => {
|
||||||
|
|
||||||
fixture.componentInstance.w3 = null;
|
fixture.componentInstance.w3 = null;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(styles.values).toEqual({
|
expect(styles.values).toEqual({
|
||||||
'width': '200px',
|
'width': '200px',
|
||||||
});
|
});
|
||||||
|
@ -309,16 +380,22 @@ describe('new styling integration', () => {
|
||||||
.it('should apply map-based style and class entries', () => {
|
.it('should apply map-based style and class entries', () => {
|
||||||
@Component({template: '<div [style]="s" [class]="c"></div>'})
|
@Component({template: '<div [style]="s" [class]="c"></div>'})
|
||||||
class Cmp {
|
class Cmp {
|
||||||
public c !: {[key: string]: any};
|
public c: {[key: string]: any}|null = null;
|
||||||
updateClasses(prop: string) {
|
updateClasses(classes: string) {
|
||||||
this.c = {...this.c || {}};
|
const c = this.c || (this.c = {});
|
||||||
this.c[prop] = true;
|
Object.keys(this.c).forEach(className => { c[className] = false; });
|
||||||
|
classes.split(/\s+/).forEach(className => { c[className] = true; });
|
||||||
}
|
}
|
||||||
|
|
||||||
public s !: {[key: string]: any};
|
public s: {[key: string]: any}|null = null;
|
||||||
updateStyles(prop: string, value: string|number|null) {
|
updateStyles(prop: string, value: string|number|null) {
|
||||||
this.s = {...this.s || {}};
|
const s = this.s || (this.s = {});
|
||||||
this.s[prop] = value;
|
Object.assign(s, {[prop]: value});
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.s = null;
|
||||||
|
this.c = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -332,22 +409,47 @@ describe('new styling integration', () => {
|
||||||
|
|
||||||
const element = fixture.nativeElement.querySelector('div');
|
const element = fixture.nativeElement.querySelector('div');
|
||||||
const node = getDebugNode(element) !;
|
const node = getDebugNode(element) !;
|
||||||
const styles = node.styles !;
|
let styles = node.styles !;
|
||||||
const classes = node.classes !;
|
let classes = node.classes !;
|
||||||
|
|
||||||
const stylesSummary = styles.summary;
|
let stylesSummary = styles.summary;
|
||||||
const widthSummary = stylesSummary['width'];
|
let widthSummary = stylesSummary['width'];
|
||||||
expect(widthSummary.prop).toEqual('width');
|
expect(widthSummary.prop).toEqual('width');
|
||||||
expect(widthSummary.value).toEqual('100px');
|
expect(widthSummary.value).toEqual('100px');
|
||||||
|
|
||||||
const heightSummary = stylesSummary['height'];
|
let heightSummary = stylesSummary['height'];
|
||||||
expect(heightSummary.prop).toEqual('height');
|
expect(heightSummary.prop).toEqual('height');
|
||||||
expect(heightSummary.value).toEqual('200px');
|
expect(heightSummary.value).toEqual('200px');
|
||||||
|
|
||||||
const classesSummary = classes.summary;
|
let classesSummary = classes.summary;
|
||||||
const abcSummary = classesSummary['abc'];
|
let abcSummary = classesSummary['abc'];
|
||||||
expect(abcSummary.prop).toEqual('abc');
|
expect(abcSummary.prop).toEqual('abc');
|
||||||
expect(abcSummary.value as any).toEqual(true);
|
expect(abcSummary.value).toBeTruthy();
|
||||||
|
|
||||||
|
comp.reset();
|
||||||
|
comp.updateStyles('width', '500px');
|
||||||
|
comp.updateStyles('height', null);
|
||||||
|
comp.updateClasses('def');
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
styles = node.styles !;
|
||||||
|
classes = node.classes !;
|
||||||
|
|
||||||
|
stylesSummary = styles.summary;
|
||||||
|
widthSummary = stylesSummary['width'];
|
||||||
|
expect(widthSummary.value).toEqual('500px');
|
||||||
|
|
||||||
|
heightSummary = stylesSummary['height'];
|
||||||
|
expect(heightSummary.value).toEqual(null);
|
||||||
|
|
||||||
|
classesSummary = classes.summary;
|
||||||
|
abcSummary = classesSummary['abc'];
|
||||||
|
expect(abcSummary.prop).toEqual('abc');
|
||||||
|
expect(abcSummary.value).toBeFalsy();
|
||||||
|
|
||||||
|
let defSummary = classesSummary['def'];
|
||||||
|
expect(defSummary.prop).toEqual('def');
|
||||||
|
expect(defSummary.value).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
onlyInIvy('ivy resolves styling across directives, components and templates in unison')
|
onlyInIvy('ivy resolves styling across directives, components and templates in unison')
|
||||||
|
@ -385,6 +487,7 @@ describe('new styling integration', () => {
|
||||||
const node = getDebugNode(element) !;
|
const node = getDebugNode(element) !;
|
||||||
|
|
||||||
const styles = node.styles !;
|
const styles = node.styles !;
|
||||||
|
|
||||||
expect(styles.values).toEqual({
|
expect(styles.values).toEqual({
|
||||||
'width': '555px',
|
'width': '555px',
|
||||||
'color': 'red',
|
'color': 'red',
|
||||||
|
@ -509,7 +612,8 @@ describe('new styling integration', () => {
|
||||||
resetStylingCounters();
|
resetStylingCounters();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
assertStyleCounters(1, 0);
|
// the width is applied both in TEMPLATE and in HOST_BINDINGS mode
|
||||||
|
assertStyleCounters(2, 0);
|
||||||
assertStyle(element, 'width', '999px');
|
assertStyle(element, 'width', '999px');
|
||||||
assertStyle(element, 'height', '123px');
|
assertStyle(element, 'height', '123px');
|
||||||
|
|
||||||
|
@ -517,8 +621,8 @@ describe('new styling integration', () => {
|
||||||
resetStylingCounters();
|
resetStylingCounters();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
// both are applied because the map was altered
|
// the width is only applied once
|
||||||
assertStyleCounters(2, 0);
|
assertStyleCounters(1, 0);
|
||||||
assertStyle(element, 'width', '0px');
|
assertStyle(element, 'width', '0px');
|
||||||
assertStyle(element, 'height', '123px');
|
assertStyle(element, 'height', '123px');
|
||||||
|
|
||||||
|
@ -526,8 +630,8 @@ describe('new styling integration', () => {
|
||||||
resetStylingCounters();
|
resetStylingCounters();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
// all three are applied because the map was altered
|
// only the width and color have changed
|
||||||
assertStyleCounters(3, 0);
|
assertStyleCounters(2, 0);
|
||||||
assertStyle(element, 'width', '1000px');
|
assertStyle(element, 'width', '1000px');
|
||||||
assertStyle(element, 'height', '123px');
|
assertStyle(element, 'height', '123px');
|
||||||
assertStyle(element, 'color', 'red');
|
assertStyle(element, 'color', 'red');
|
||||||
|
@ -536,7 +640,9 @@ describe('new styling integration', () => {
|
||||||
resetStylingCounters();
|
resetStylingCounters();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
assertStyleCounters(1, 0);
|
// height gets applied twice and all other
|
||||||
|
// values get applied
|
||||||
|
assertStyleCounters(4, 0);
|
||||||
assertStyle(element, 'width', '1000px');
|
assertStyle(element, 'width', '1000px');
|
||||||
assertStyle(element, 'height', '1000px');
|
assertStyle(element, 'height', '1000px');
|
||||||
assertStyle(element, 'color', 'red');
|
assertStyle(element, 'color', 'red');
|
||||||
|
@ -545,8 +651,7 @@ describe('new styling integration', () => {
|
||||||
resetStylingCounters();
|
resetStylingCounters();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
// all four are applied because the map was altered
|
assertStyleCounters(5, 0);
|
||||||
assertStyleCounters(4, 0);
|
|
||||||
assertStyle(element, 'width', '2000px');
|
assertStyle(element, 'width', '2000px');
|
||||||
assertStyle(element, 'height', '1000px');
|
assertStyle(element, 'height', '1000px');
|
||||||
assertStyle(element, 'color', 'blue');
|
assertStyle(element, 'color', 'blue');
|
||||||
|
@ -557,62 +662,13 @@ describe('new styling integration', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
// all four are applied because the map was altered
|
// all four are applied because the map was altered
|
||||||
assertStyleCounters(3, 1);
|
assertStyleCounters(4, 1);
|
||||||
assertStyle(element, 'width', '2000px');
|
assertStyle(element, 'width', '2000px');
|
||||||
assertStyle(element, 'height', '1000px');
|
assertStyle(element, 'height', '1000px');
|
||||||
assertStyle(element, 'color', 'blue');
|
assertStyle(element, 'color', 'blue');
|
||||||
assertStyle(element, 'opacity', '');
|
assertStyle(element, 'opacity', '');
|
||||||
});
|
});
|
||||||
|
|
||||||
onlyInIvy('ivy resolves styling across directives, components and templates in unison')
|
|
||||||
.it('should only persist state values in a local map if template AND host styling is used together',
|
|
||||||
() => {
|
|
||||||
@Directive({selector: '[dir-that-sets-styling]'})
|
|
||||||
class Dir {
|
|
||||||
@HostBinding('style.width') w = '100px';
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
template: `
|
|
||||||
<div #a dir-that-sets-styling></div>
|
|
||||||
<div #b [style.width]="w"></div>
|
|
||||||
<div #c dir-that-sets-styling [style.width]="w"></div>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
class Cmp {
|
|
||||||
w = '200px';
|
|
||||||
@ViewChild('a', {read: Dir, static: true}) a !: Dir;
|
|
||||||
@ViewChild('c', {read: Dir, static: true}) c !: Dir;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [Cmp, Dir]});
|
|
||||||
const fixture = TestBed.createComponent(Cmp);
|
|
||||||
const comp = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
resetStylingCounters();
|
|
||||||
|
|
||||||
comp.a.w = '999px';
|
|
||||||
comp.w = '999px';
|
|
||||||
comp.c.w = '999px';
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(ngDevMode !.stylingWritePersistedState).toEqual(totalUpdates(1));
|
|
||||||
|
|
||||||
comp.a.w = '888px';
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(ngDevMode !.stylingWritePersistedState).toEqual(totalUpdates(2));
|
|
||||||
|
|
||||||
comp.c.w = '777px';
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(ngDevMode !.stylingWritePersistedState).toEqual(totalUpdates(3));
|
|
||||||
|
|
||||||
function totalUpdates(value: number) {
|
|
||||||
// this is doubled because detectChanges is run twice to
|
|
||||||
// see to check for checkNoChanges
|
|
||||||
return value * 2;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
onlyInIvy('only ivy has style/class bindings debugging support')
|
onlyInIvy('only ivy has style/class bindings debugging support')
|
||||||
.it('should sanitize style values before writing them', () => {
|
.it('should sanitize style values before writing them', () => {
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -910,7 +966,6 @@ describe('new styling integration', () => {
|
||||||
|
|
||||||
TestBed.configureTestingModule({declarations: [Cmp, DirOne, DirTwo]});
|
TestBed.configureTestingModule({declarations: [Cmp, DirOne, DirTwo]});
|
||||||
const fixture = TestBed.createComponent(Cmp);
|
const fixture = TestBed.createComponent(Cmp);
|
||||||
const comp = fixture.componentInstance;
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const dirOne = fixture.nativeElement.querySelector('dir-one');
|
const dirOne = fixture.nativeElement.querySelector('dir-one');
|
||||||
|
|
|
@ -628,7 +628,6 @@ describe('styling', () => {
|
||||||
expect(childDir.parent).toBeAnInstanceOf(TestDir);
|
expect(childDir.parent).toBeAnInstanceOf(TestDir);
|
||||||
expect(testDirDiv.classList).not.toContain('with-button');
|
expect(testDirDiv.classList).not.toContain('with-button');
|
||||||
expect(fixture.debugElement.nativeElement.textContent).toContain('Hello');
|
expect(fixture.debugElement.nativeElement.textContent).toContain('Hello');
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,9 @@
|
||||||
{
|
{
|
||||||
"name": "_currentNamespace"
|
"name": "_currentNamespace"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "_elementExitFn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "_global"
|
"name": "_global"
|
||||||
},
|
},
|
||||||
|
@ -165,7 +168,7 @@
|
||||||
"name": "_selectedIndex"
|
"name": "_selectedIndex"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "_stateStorage"
|
"name": "_state"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "addComponentLogic"
|
"name": "addComponentLogic"
|
||||||
|
@ -176,6 +179,9 @@
|
||||||
{
|
{
|
||||||
"name": "addToViewTree"
|
"name": "addToViewTree"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "allocStylingMapArray"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "appendChild"
|
"name": "appendChild"
|
||||||
},
|
},
|
||||||
|
@ -254,6 +260,9 @@
|
||||||
{
|
{
|
||||||
"name": "executeContentQueries"
|
"name": "executeContentQueries"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "executeElementExitFn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "executeInitAndCheckHooks"
|
"name": "executeInitAndCheckHooks"
|
||||||
},
|
},
|
||||||
|
@ -395,6 +404,9 @@
|
||||||
{
|
{
|
||||||
"name": "getStylingMapArray"
|
"name": "getStylingMapArray"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "hasActiveElementFlag"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hasClassInput"
|
"name": "hasClassInput"
|
||||||
},
|
},
|
||||||
|
@ -572,9 +584,6 @@
|
||||||
{
|
{
|
||||||
"name": "renderView"
|
"name": "renderView"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "resetAllStylingState"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "resetComponentState"
|
"name": "resetComponentState"
|
||||||
},
|
},
|
||||||
|
|
|
@ -134,6 +134,9 @@
|
||||||
{
|
{
|
||||||
"name": "__window"
|
"name": "__window"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "_elementExitFn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "_global"
|
"name": "_global"
|
||||||
},
|
},
|
||||||
|
@ -144,7 +147,7 @@
|
||||||
"name": "_selectedIndex"
|
"name": "_selectedIndex"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "_stateStorage"
|
"name": "_state"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "addToViewTree"
|
"name": "addToViewTree"
|
||||||
|
@ -209,6 +212,9 @@
|
||||||
{
|
{
|
||||||
"name": "executeCheckHooks"
|
"name": "executeCheckHooks"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "executeElementExitFn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "executeInitAndCheckHooks"
|
"name": "executeInitAndCheckHooks"
|
||||||
},
|
},
|
||||||
|
@ -314,6 +320,9 @@
|
||||||
{
|
{
|
||||||
"name": "getSelectedIndex"
|
"name": "getSelectedIndex"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "hasActiveElementFlag"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hasParentInjector"
|
"name": "hasParentInjector"
|
||||||
},
|
},
|
||||||
|
@ -419,9 +428,6 @@
|
||||||
{
|
{
|
||||||
"name": "renderView"
|
"name": "renderView"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "resetAllStylingState"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "resetComponentState"
|
"name": "resetComponentState"
|
||||||
},
|
},
|
||||||
|
|
|
@ -44,6 +44,9 @@
|
||||||
{
|
{
|
||||||
"name": "DECLARATION_VIEW"
|
"name": "DECLARATION_VIEW"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "DEFAULT_BINDING_INDEX"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "DEFAULT_BINDING_VALUE"
|
"name": "DEFAULT_BINDING_VALUE"
|
||||||
},
|
},
|
||||||
|
@ -51,7 +54,7 @@
|
||||||
"name": "DEFAULT_GUARD_MASK_VALUE"
|
"name": "DEFAULT_GUARD_MASK_VALUE"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "DEFAULT_SIZE_VALUE"
|
"name": "DEFAULT_TOTAL_SOURCES"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "DefaultIterableDiffer"
|
"name": "DefaultIterableDiffer"
|
||||||
|
@ -92,6 +95,9 @@
|
||||||
{
|
{
|
||||||
"name": "HOST"
|
"name": "HOST"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "INDEX_START_VALUE"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "INJECTOR"
|
"name": "INJECTOR"
|
||||||
},
|
},
|
||||||
|
@ -111,7 +117,7 @@
|
||||||
"name": "MAP_BASED_ENTRY_PROP_NAME"
|
"name": "MAP_BASED_ENTRY_PROP_NAME"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "MIN_DIRECTIVE_ID"
|
"name": "MAP_DIRTY_VALUE"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "MONKEY_PATCH_KEY_NAME"
|
"name": "MONKEY_PATCH_KEY_NAME"
|
||||||
|
@ -215,9 +221,6 @@
|
||||||
{
|
{
|
||||||
"name": "STYLING_INDEX_FOR_MAP_BINDING"
|
"name": "STYLING_INDEX_FOR_MAP_BINDING"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "STYLING_INDEX_START_VALUE"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "SWITCH_ELEMENT_REF_FACTORY"
|
"name": "SWITCH_ELEMENT_REF_FACTORY"
|
||||||
},
|
},
|
||||||
|
@ -227,6 +230,9 @@
|
||||||
{
|
{
|
||||||
"name": "SWITCH_VIEW_CONTAINER_REF_FACTORY"
|
"name": "SWITCH_VIEW_CONTAINER_REF_FACTORY"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "SafeValueImpl"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "SkipSelf"
|
"name": "SkipSelf"
|
||||||
},
|
},
|
||||||
|
@ -398,6 +404,9 @@
|
||||||
{
|
{
|
||||||
"name": "_devMode"
|
"name": "_devMode"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "_elementExitFn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "_global"
|
"name": "_global"
|
||||||
},
|
},
|
||||||
|
@ -408,16 +417,7 @@
|
||||||
"name": "_selectedIndex"
|
"name": "_selectedIndex"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "_stateStorage"
|
"name": "_state"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "_stylingElement"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "_stylingProp"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "_stylingState"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "_symbolIterator"
|
"name": "_symbolIterator"
|
||||||
|
@ -425,12 +425,6 @@
|
||||||
{
|
{
|
||||||
"name": "activeDirectiveId"
|
"name": "activeDirectiveId"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "activeDirectiveSuperClassDepthPosition"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "activeDirectiveSuperClassHeight"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "addBindingIntoContext"
|
"name": "addBindingIntoContext"
|
||||||
},
|
},
|
||||||
|
@ -440,6 +434,9 @@
|
||||||
{
|
{
|
||||||
"name": "addItemToStylingMap"
|
"name": "addItemToStylingMap"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "addNewSourceColumn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "addRemoveViewFromContainer"
|
"name": "addRemoveViewFromContainer"
|
||||||
},
|
},
|
||||||
|
@ -449,6 +446,9 @@
|
||||||
{
|
{
|
||||||
"name": "addToViewTree"
|
"name": "addToViewTree"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "allocStylingMapArray"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "allocTStylingContext"
|
"name": "allocTStylingContext"
|
||||||
},
|
},
|
||||||
|
@ -456,7 +456,7 @@
|
||||||
"name": "allocateNewContextEntry"
|
"name": "allocateNewContextEntry"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "allowStylingFlush"
|
"name": "allowDirectStyling"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "appendChild"
|
"name": "appendChild"
|
||||||
|
@ -473,6 +473,15 @@
|
||||||
{
|
{
|
||||||
"name": "applyStyling"
|
"name": "applyStyling"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "applyStylingValue"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "applyStylingValueDirectly"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "applyStylingViaContext"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "applyToElementOrContainer"
|
"name": "applyToElementOrContainer"
|
||||||
},
|
},
|
||||||
|
@ -527,9 +536,6 @@
|
||||||
{
|
{
|
||||||
"name": "containerInternal"
|
"name": "containerInternal"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "contextHasUpdates"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "contextLView"
|
"name": "contextLView"
|
||||||
},
|
},
|
||||||
|
@ -587,18 +593,6 @@
|
||||||
{
|
{
|
||||||
"name": "defaultScheduler"
|
"name": "defaultScheduler"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "deferBindingRegistration"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deferStylingUpdate"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deferredBindingQueue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "deleteStylingStateFromStorage"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "destroyLView"
|
"name": "destroyLView"
|
||||||
},
|
},
|
||||||
|
@ -632,6 +626,9 @@
|
||||||
{
|
{
|
||||||
"name": "executeContentQueries"
|
"name": "executeContentQueries"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "executeElementExitFn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "executeInitAndCheckHooks"
|
"name": "executeInitAndCheckHooks"
|
||||||
},
|
},
|
||||||
|
@ -669,10 +666,10 @@
|
||||||
"name": "findExistingListener"
|
"name": "findExistingListener"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "findViaComponent"
|
"name": "findInitialStylingValue"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "flushDeferredBindings"
|
"name": "findViaComponent"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "flushStyling"
|
"name": "flushStyling"
|
||||||
|
@ -692,15 +689,6 @@
|
||||||
{
|
{
|
||||||
"name": "getActiveDirectiveId"
|
"name": "getActiveDirectiveId"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "getActiveDirectiveStylingIndex"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "getActiveDirectiveSuperClassDepth"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "getActiveDirectiveSuperClassHeight"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "getBeforeNodeForView"
|
"name": "getBeforeNodeForView"
|
||||||
},
|
},
|
||||||
|
@ -749,6 +737,9 @@
|
||||||
{
|
{
|
||||||
"name": "getDebugContext"
|
"name": "getDebugContext"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getDefaultValue"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getDirectiveDef"
|
"name": "getDirectiveDef"
|
||||||
},
|
},
|
||||||
|
@ -788,6 +779,9 @@
|
||||||
{
|
{
|
||||||
"name": "getLViewParent"
|
"name": "getLViewParent"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getLockedConfig"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getMapProp"
|
"name": "getMapProp"
|
||||||
},
|
},
|
||||||
|
@ -896,21 +890,33 @@
|
||||||
{
|
{
|
||||||
"name": "getTViewCleanup"
|
"name": "getTViewCleanup"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getTotalSources"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getTypeName"
|
"name": "getTypeName"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "getTypeNameForDebugging"
|
"name": "getTypeNameForDebugging"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getValue"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getValuesCount"
|
"name": "getValuesCount"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "handleError"
|
"name": "handleError"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "hasActiveElementFlag"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hasClassInput"
|
"name": "hasClassInput"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "hasConfig"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hasDirectives"
|
"name": "hasDirectives"
|
||||||
},
|
},
|
||||||
|
@ -1016,6 +1022,12 @@
|
||||||
{
|
{
|
||||||
"name": "isForwardRef"
|
"name": "isForwardRef"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "isHostStyling"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "isHostStylingActive"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "isJsObject"
|
"name": "isJsObject"
|
||||||
},
|
},
|
||||||
|
@ -1088,24 +1100,21 @@
|
||||||
{
|
{
|
||||||
"name": "markAsComponentHost"
|
"name": "markAsComponentHost"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "markContextToPersistState"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "markDirty"
|
"name": "markDirty"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "markDirtyIfOnPush"
|
"name": "markDirtyIfOnPush"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "markStylingStateAsDirty"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "markViewDirty"
|
"name": "markViewDirty"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "matchTemplateAttribute"
|
"name": "matchTemplateAttribute"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "maybeApplyStyling"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "namespaceHTMLInternal"
|
"name": "namespaceHTMLInternal"
|
||||||
},
|
},
|
||||||
|
@ -1142,6 +1151,9 @@
|
||||||
{
|
{
|
||||||
"name": "normalizeBitMaskValue"
|
"name": "normalizeBitMaskValue"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "patchConfig"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "postProcessBaseDirective"
|
"name": "postProcessBaseDirective"
|
||||||
},
|
},
|
||||||
|
@ -1205,6 +1217,9 @@
|
||||||
{
|
{
|
||||||
"name": "renderDetachView"
|
"name": "renderDetachView"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "renderHostBindingsAsStale"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "renderInitialStyling"
|
"name": "renderInitialStyling"
|
||||||
},
|
},
|
||||||
|
@ -1217,9 +1232,6 @@
|
||||||
{
|
{
|
||||||
"name": "renderView"
|
"name": "renderView"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "resetAllStylingState"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "resetComponentState"
|
"name": "resetComponentState"
|
||||||
},
|
},
|
||||||
|
@ -1253,6 +1265,9 @@
|
||||||
{
|
{
|
||||||
"name": "selectView"
|
"name": "selectView"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setActiveElementFlag"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setActiveHostElement"
|
"name": "setActiveHostElement"
|
||||||
},
|
},
|
||||||
|
@ -1265,9 +1280,6 @@
|
||||||
{
|
{
|
||||||
"name": "setClass"
|
"name": "setClass"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "setConfig"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "setCurrentDirectiveDef"
|
"name": "setCurrentDirectiveDef"
|
||||||
},
|
},
|
||||||
|
@ -1277,9 +1289,15 @@
|
||||||
{
|
{
|
||||||
"name": "setCurrentStyleSanitizer"
|
"name": "setCurrentStyleSanitizer"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setDefaultValue"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setDirectiveStylingInput"
|
"name": "setDirectiveStylingInput"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setElementExitFn"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setGuardMask"
|
"name": "setGuardMask"
|
||||||
},
|
},
|
||||||
|
@ -1301,6 +1319,9 @@
|
||||||
{
|
{
|
||||||
"name": "setIsNotParent"
|
"name": "setIsNotParent"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setMapAsDirty"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setMapValue"
|
"name": "setMapValue"
|
||||||
},
|
},
|
||||||
|
@ -1319,18 +1340,15 @@
|
||||||
{
|
{
|
||||||
"name": "setUpAttributes"
|
"name": "setUpAttributes"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setValue"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "shouldSearchParent"
|
"name": "shouldSearchParent"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "stateIsPersisted"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "storeCleanupFn"
|
"name": "storeCleanupFn"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "storeStylingState"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "stringify"
|
"name": "stringify"
|
||||||
},
|
},
|
||||||
|
@ -1340,6 +1358,9 @@
|
||||||
{
|
{
|
||||||
"name": "stylingMapToString"
|
"name": "stylingMapToString"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "stylingProp"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "syncViewWithBlueprint"
|
"name": "syncViewWithBlueprint"
|
||||||
},
|
},
|
||||||
|
@ -1361,26 +1382,23 @@
|
||||||
{
|
{
|
||||||
"name": "unwrapRNode"
|
"name": "unwrapRNode"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "unwrapSafeValue"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "updateBindingData"
|
"name": "updateBindingData"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "updateClassBinding"
|
"name": "updateClassViaContext"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "updateInitialStylingOnContext"
|
"name": "updateInitialStylingOnContext"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "updateLastDirectiveIndex"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "updateLastDirectiveIndex"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "updateRawValueOnContext"
|
"name": "updateRawValueOnContext"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "updateStyleBinding"
|
"name": "updateStyleViaContext"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "viewAttachedToChangeDetector"
|
"name": "viewAttachedToChangeDetector"
|
||||||
|
@ -1439,12 +1457,6 @@
|
||||||
{
|
{
|
||||||
"name": "ɵɵrestoreView"
|
"name": "ɵɵrestoreView"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "ɵɵstyling"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "ɵɵstylingApply"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "ɵɵtemplate"
|
"name": "ɵɵtemplate"
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import {NgForOfContext} from '@angular/common';
|
import {NgForOfContext} from '@angular/common';
|
||||||
|
|
||||||
import {ɵɵdefineComponent} from '../../src/render3/definition';
|
import {ɵɵdefineComponent} from '../../src/render3/definition';
|
||||||
import {RenderFlags, ɵɵattribute, ɵɵclassMap, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵproperty, ɵɵselect, ɵɵstyleMap, ɵɵstyleProp, ɵɵstyleSanitizer, ɵɵstyling, ɵɵstylingApply, ɵɵtemplate, ɵɵtext, ɵɵtextInterpolate1} from '../../src/render3/index';
|
import {RenderFlags, ɵɵattribute, ɵɵclassMap, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵproperty, ɵɵselect, ɵɵstyleMap, ɵɵstyleProp, ɵɵstyleSanitizer, ɵɵtemplate, ɵɵtext, ɵɵtextInterpolate1} from '../../src/render3/index';
|
||||||
import {AttributeMarker} from '../../src/render3/interfaces/node';
|
import {AttributeMarker} from '../../src/render3/interfaces/node';
|
||||||
import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl, getSanitizationBypassType, unwrapSafeValue} from '../../src/sanitization/bypass';
|
import {bypassSanitizationTrustHtml, bypassSanitizationTrustResourceUrl, bypassSanitizationTrustScript, bypassSanitizationTrustStyle, bypassSanitizationTrustUrl, getSanitizationBypassType, unwrapSafeValue} from '../../src/sanitization/bypass';
|
||||||
import {ɵɵdefaultStyleSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization';
|
import {ɵɵdefaultStyleSanitizer, ɵɵsanitizeHtml, ɵɵsanitizeResourceUrl, ɵɵsanitizeScript, ɵɵsanitizeStyle, ɵɵsanitizeUrl} from '../../src/sanitization/sanitization';
|
||||||
|
@ -20,11 +20,7 @@ import {NgForOf} from './common_with_def';
|
||||||
import {ComponentFixture, TemplateFixture} from './render_util';
|
import {ComponentFixture, TemplateFixture} from './render_util';
|
||||||
|
|
||||||
describe('instructions', () => {
|
describe('instructions', () => {
|
||||||
function createAnchor() {
|
function createAnchor() { ɵɵelement(0, 'a'); }
|
||||||
ɵɵelementStart(0, 'a');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDiv(initialClasses?: string[] | null, initialStyles?: string[] | null) {
|
function createDiv(initialClasses?: string[] | null, initialStyles?: string[] | null) {
|
||||||
const attrs: any[] = [];
|
const attrs: any[] = [];
|
||||||
|
@ -34,9 +30,7 @@ describe('instructions', () => {
|
||||||
if (initialStyles) {
|
if (initialStyles) {
|
||||||
attrs.push(AttributeMarker.Styles, ...initialStyles);
|
attrs.push(AttributeMarker.Styles, ...initialStyles);
|
||||||
}
|
}
|
||||||
ɵɵelementStart(0, 'div', attrs);
|
ɵɵelement(0, 'div', attrs);
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createScript() { ɵɵelement(0, 'script'); }
|
function createScript() { ɵɵelement(0, 'script'); }
|
||||||
|
@ -156,7 +150,6 @@ describe('instructions', () => {
|
||||||
t.update(() => {
|
t.update(() => {
|
||||||
ɵɵstyleSanitizer(ɵɵdefaultStyleSanitizer);
|
ɵɵstyleSanitizer(ɵɵdefaultStyleSanitizer);
|
||||||
ɵɵstyleProp('background-image', 'url("http://server")');
|
ɵɵstyleProp('background-image', 'url("http://server")');
|
||||||
ɵɵstylingApply();
|
|
||||||
});
|
});
|
||||||
// nothing is set because sanitizer suppresses it.
|
// nothing is set because sanitizer suppresses it.
|
||||||
expect(t.html).toEqual('<div></div>');
|
expect(t.html).toEqual('<div></div>');
|
||||||
|
@ -164,7 +157,6 @@ describe('instructions', () => {
|
||||||
t.update(() => {
|
t.update(() => {
|
||||||
ɵɵstyleSanitizer(ɵɵdefaultStyleSanitizer);
|
ɵɵstyleSanitizer(ɵɵdefaultStyleSanitizer);
|
||||||
ɵɵstyleProp('background-image', bypassSanitizationTrustStyle('url("http://server2")'));
|
ɵɵstyleProp('background-image', bypassSanitizationTrustStyle('url("http://server2")'));
|
||||||
ɵɵstylingApply();
|
|
||||||
});
|
});
|
||||||
expect((t.hostElement.firstChild as HTMLElement).style.getPropertyValue('background-image'))
|
expect((t.hostElement.firstChild as HTMLElement).style.getPropertyValue('background-image'))
|
||||||
.toEqual('url("http://server2")');
|
.toEqual('url("http://server2")');
|
||||||
|
@ -173,17 +165,12 @@ describe('instructions', () => {
|
||||||
|
|
||||||
describe('styleMap', () => {
|
describe('styleMap', () => {
|
||||||
function createDivWithStyle() {
|
function createDivWithStyle() {
|
||||||
ɵɵelementStart(0, 'div', [AttributeMarker.Styles, 'height', '10px']);
|
ɵɵelement(0, 'div', [AttributeMarker.Styles, 'height', '10px']);
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should add style', () => {
|
it('should add style', () => {
|
||||||
const fixture = new TemplateFixture(createDivWithStyle, () => {}, 1);
|
const fixture = new TemplateFixture(createDivWithStyle, () => {}, 1);
|
||||||
fixture.update(() => {
|
fixture.update(() => { ɵɵstyleMap({'background-color': 'red'}); });
|
||||||
ɵɵstyleMap({'background-color': 'red'});
|
|
||||||
ɵɵstylingApply();
|
|
||||||
});
|
|
||||||
expect(fixture.html).toEqual('<div style="background-color: red; height: 10px;"></div>');
|
expect(fixture.html).toEqual('<div style="background-color: red; height: 10px;"></div>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -205,7 +192,6 @@ describe('instructions', () => {
|
||||||
'filter': 'filter',
|
'filter': 'filter',
|
||||||
'width': 'width'
|
'width': 'width'
|
||||||
});
|
});
|
||||||
ɵɵstylingApply();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const props = detectedValues.sort();
|
const props = detectedValues.sort();
|
||||||
|
@ -216,18 +202,11 @@ describe('instructions', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('elementClass', () => {
|
describe('elementClass', () => {
|
||||||
function createDivWithStyling() {
|
function createDivWithStyling() { ɵɵelement(0, 'div'); }
|
||||||
ɵɵelementStart(0, 'div');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should add class', () => {
|
it('should add class', () => {
|
||||||
const fixture = new TemplateFixture(createDivWithStyling, () => {}, 1);
|
const fixture = new TemplateFixture(createDivWithStyling, () => {}, 1);
|
||||||
fixture.update(() => {
|
fixture.update(() => { ɵɵclassMap('multiple classes'); });
|
||||||
ɵɵclassMap('multiple classes');
|
|
||||||
ɵɵstylingApply();
|
|
||||||
});
|
|
||||||
expect(fixture.html).toEqual('<div class="classes multiple"></div>');
|
expect(fixture.html).toEqual('<div class="classes multiple"></div>');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import {RendererType2} from '../../src/render/api';
|
import {RendererType2} from '../../src/render/api';
|
||||||
import {getLContext} from '../../src/render3/context_discovery';
|
import {getLContext} from '../../src/render3/context_discovery';
|
||||||
import {AttributeMarker, ɵɵadvance, ɵɵattribute, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵhostProperty, ɵɵproperty} from '../../src/render3/index';
|
import {AttributeMarker, ɵɵadvance, ɵɵattribute, ɵɵdefineComponent, ɵɵdefineDirective, ɵɵhostProperty, ɵɵproperty} from '../../src/render3/index';
|
||||||
import {ɵɵallocHostVars, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵprojection, ɵɵprojectionDef, ɵɵstyling, ɵɵstylingApply, ɵɵtemplate, ɵɵtext, ɵɵtextInterpolate} from '../../src/render3/instructions/all';
|
import {ɵɵallocHostVars, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵprojection, ɵɵprojectionDef, ɵɵtemplate, ɵɵtext, ɵɵtextInterpolate} from '../../src/render3/instructions/all';
|
||||||
import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context';
|
import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
|
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
|
||||||
|
@ -630,12 +630,7 @@ describe('element discovery', () => {
|
||||||
vars: 0,
|
vars: 0,
|
||||||
template: (rf: RenderFlags, ctx: StructuredComp) => {
|
template: (rf: RenderFlags, ctx: StructuredComp) => {
|
||||||
if (rf & RenderFlags.Create) {
|
if (rf & RenderFlags.Create) {
|
||||||
ɵɵelementStart(0, 'section');
|
ɵɵelement(0, 'section');
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {ɵɵadvance} from '../../../../src/render3/instructions/advance';
|
import {ɵɵadvance} from '../../../../src/render3/instructions/advance';
|
||||||
import {ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/instructions/element';
|
import {ɵɵelement, ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/instructions/element';
|
||||||
import {refreshView} from '../../../../src/render3/instructions/shared';
|
import {refreshView} from '../../../../src/render3/instructions/shared';
|
||||||
import {RenderFlags} from '../../../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../../../src/render3/interfaces/definition';
|
||||||
import {TVIEW} from '../../../../src/render3/interfaces/view';
|
import {TVIEW} from '../../../../src/render3/interfaces/view';
|
||||||
import {ɵɵclassMap, ɵɵstyleMap, ɵɵstyling, ɵɵstylingApply} from '../../../../src/render3/styling_next/instructions';
|
import {ɵɵclassMap, ɵɵstyleMap} from '../../../../src/render3/styling_next/instructions';
|
||||||
import {setupRootViewWithEmbeddedViews} from '../setup';
|
import {setupRootViewWithEmbeddedViews} from '../setup';
|
||||||
|
|
||||||
`<ng-template>
|
`<ng-template>
|
||||||
|
@ -30,79 +30,49 @@ import {setupRootViewWithEmbeddedViews} from '../setup';
|
||||||
function testTemplate(rf: RenderFlags, ctx: any) {
|
function testTemplate(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
ɵɵelementStart(0, 'div');
|
ɵɵelementStart(0, 'div');
|
||||||
ɵɵelementStart(1, 'div');
|
ɵɵelement(1, 'div');
|
||||||
ɵɵstyling();
|
ɵɵelement(2, 'div');
|
||||||
ɵɵelementEnd();
|
ɵɵelement(3, 'div');
|
||||||
ɵɵelementStart(2, 'div');
|
ɵɵelement(4, 'div');
|
||||||
ɵɵstyling();
|
ɵɵelement(5, 'div');
|
||||||
ɵɵelementEnd();
|
ɵɵelement(6, 'div');
|
||||||
ɵɵelementStart(3, 'div');
|
ɵɵelement(7, 'div');
|
||||||
ɵɵstyling();
|
ɵɵelement(8, 'div');
|
||||||
ɵɵelementEnd();
|
ɵɵelement(9, 'div');
|
||||||
ɵɵelementStart(4, 'div');
|
ɵɵelement(10, 'div');
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(5, 'div');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(6, 'div');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(7, 'div');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(8, 'div');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(9, 'div');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(10, 'div');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleMap({width: '0px', height: '0px'});
|
ɵɵstyleMap({width: '0px', height: '0px'});
|
||||||
ɵɵclassMap('one two');
|
ɵɵclassMap('one two');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleMap({width: '10px', height: '100px'});
|
ɵɵstyleMap({width: '10px', height: '100px'});
|
||||||
ɵɵclassMap('one two');
|
ɵɵclassMap('one two');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleMap({width: '20px', height: '200px'});
|
ɵɵstyleMap({width: '20px', height: '200px'});
|
||||||
ɵɵclassMap('one two');
|
ɵɵclassMap('one two');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleMap({width: '30px', height: '300px'});
|
ɵɵstyleMap({width: '30px', height: '300px'});
|
||||||
ɵɵclassMap('one two');
|
ɵɵclassMap('one two');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleMap({width: '40px', height: '400px'});
|
ɵɵstyleMap({width: '40px', height: '400px'});
|
||||||
ɵɵclassMap('one two');
|
ɵɵclassMap('one two');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleMap({width: '50px', height: '500px'});
|
ɵɵstyleMap({width: '50px', height: '500px'});
|
||||||
ɵɵclassMap('one two');
|
ɵɵclassMap('one two');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleMap({width: '60px', height: '600px'});
|
ɵɵstyleMap({width: '60px', height: '600px'});
|
||||||
ɵɵclassMap('one two');
|
ɵɵclassMap('one two');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleMap({width: '70px', height: '700px'});
|
ɵɵstyleMap({width: '70px', height: '700px'});
|
||||||
ɵɵclassMap('one two');
|
ɵɵclassMap('one two');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleMap({width: '80px', height: '800px'});
|
ɵɵstyleMap({width: '80px', height: '800px'});
|
||||||
ɵɵclassMap('one two');
|
ɵɵclassMap('one two');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleMap({width: '90px', height: '900px'});
|
ɵɵstyleMap({width: '90px', height: '900px'});
|
||||||
ɵɵclassMap('one two');
|
ɵɵclassMap('one two');
|
||||||
ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {ɵɵadvance} from '../../../../src/render3/instructions/advance';
|
import {ɵɵadvance} from '../../../../src/render3/instructions/advance';
|
||||||
import {ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/instructions/element';
|
import {ɵɵelement, ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/instructions/element';
|
||||||
import {refreshView} from '../../../../src/render3/instructions/shared';
|
import {refreshView} from '../../../../src/render3/instructions/shared';
|
||||||
import {RenderFlags} from '../../../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../../../src/render3/interfaces/definition';
|
||||||
import {AttributeMarker} from '../../../../src/render3/interfaces/node';
|
import {AttributeMarker} from '../../../../src/render3/interfaces/node';
|
||||||
import {TVIEW} from '../../../../src/render3/interfaces/view';
|
import {TVIEW} from '../../../../src/render3/interfaces/view';
|
||||||
import {ɵɵclassProp, ɵɵstyleProp, ɵɵstyling, ɵɵstylingApply} from '../../../../src/render3/styling_next/instructions';
|
import {ɵɵclassProp, ɵɵstyleProp} from '../../../../src/render3/styling_next/instructions';
|
||||||
import {setupRootViewWithEmbeddedViews} from '../setup';
|
import {setupRootViewWithEmbeddedViews} from '../setup';
|
||||||
|
|
||||||
`<ng-template>
|
`<ng-template>
|
||||||
|
@ -31,89 +31,50 @@ import {setupRootViewWithEmbeddedViews} from '../setup';
|
||||||
function testTemplate(rf: RenderFlags, ctx: any) {
|
function testTemplate(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
ɵɵelementStart(0, 'div', [AttributeMarker.Classes, 'list']);
|
ɵɵelementStart(0, 'div', [AttributeMarker.Classes, 'list']);
|
||||||
ɵɵelementStart(
|
ɵɵelement(1, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
||||||
1, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
ɵɵelement(2, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
||||||
ɵɵstyling();
|
ɵɵelement(3, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
||||||
ɵɵelementEnd();
|
ɵɵelement(4, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
||||||
ɵɵelementStart(
|
ɵɵelement(5, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
||||||
2, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
ɵɵelement(6, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
||||||
ɵɵstyling();
|
ɵɵelement(7, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
||||||
ɵɵelementEnd();
|
ɵɵelement(8, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
||||||
ɵɵelementStart(
|
ɵɵelement(9, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
||||||
3, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
ɵɵelement(
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(
|
|
||||||
4, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(
|
|
||||||
5, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(
|
|
||||||
6, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(
|
|
||||||
7, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(
|
|
||||||
8, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(
|
|
||||||
9, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(
|
|
||||||
10, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
10, 'div', [AttributeMarker.Classes, 'item', AttributeMarker.Styles, 'width', '50px']);
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('width', '0px');
|
ɵɵstyleProp('width', '0px');
|
||||||
ɵɵclassProp('scale', true);
|
ɵɵclassProp('scale', true);
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('width', '100px');
|
ɵɵstyleProp('width', '100px');
|
||||||
ɵɵclassProp('scale', true);
|
ɵɵclassProp('scale', true);
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('width', '200px');
|
ɵɵstyleProp('width', '200px');
|
||||||
ɵɵclassProp('scale', true);
|
ɵɵclassProp('scale', true);
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('width', '300px');
|
ɵɵstyleProp('width', '300px');
|
||||||
ɵɵclassProp('scale', true);
|
ɵɵclassProp('scale', true);
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('width', '400px');
|
ɵɵstyleProp('width', '400px');
|
||||||
ɵɵclassProp('scale', true);
|
ɵɵclassProp('scale', true);
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('width', '500px');
|
ɵɵstyleProp('width', '500px');
|
||||||
ɵɵclassProp('scale', true);
|
ɵɵclassProp('scale', true);
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('width', '600px');
|
ɵɵstyleProp('width', '600px');
|
||||||
ɵɵclassProp('scale', true);
|
ɵɵclassProp('scale', true);
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('width', '700px');
|
ɵɵstyleProp('width', '700px');
|
||||||
ɵɵclassProp('scale', true);
|
ɵɵclassProp('scale', true);
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('width', '800px');
|
ɵɵstyleProp('width', '800px');
|
||||||
ɵɵclassProp('scale', true);
|
ɵɵclassProp('scale', true);
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('width', '900px');
|
ɵɵstyleProp('width', '900px');
|
||||||
ɵɵclassProp('scale', true);
|
ɵɵclassProp('scale', true);
|
||||||
ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {ɵɵadvance} from '../../../../src/render3/instructions/advance';
|
import {ɵɵadvance} from '../../../../src/render3/instructions/advance';
|
||||||
import {ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/instructions/element';
|
import {ɵɵelement, ɵɵelementEnd, ɵɵelementStart} from '../../../../src/render3/instructions/element';
|
||||||
import {refreshView} from '../../../../src/render3/instructions/shared';
|
import {refreshView} from '../../../../src/render3/instructions/shared';
|
||||||
import {RenderFlags} from '../../../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../../../src/render3/interfaces/definition';
|
||||||
import {TVIEW} from '../../../../src/render3/interfaces/view';
|
import {TVIEW} from '../../../../src/render3/interfaces/view';
|
||||||
import {ɵɵstyleProp, ɵɵstyling, ɵɵstylingApply} from '../../../../src/render3/styling_next/instructions';
|
import {ɵɵstyleProp} from '../../../../src/render3/styling_next/instructions';
|
||||||
import {setupRootViewWithEmbeddedViews} from '../setup';
|
import {setupRootViewWithEmbeddedViews} from '../setup';
|
||||||
|
|
||||||
`<ng-template>
|
`<ng-template>
|
||||||
|
@ -30,69 +30,39 @@ import {setupRootViewWithEmbeddedViews} from '../setup';
|
||||||
function testTemplate(rf: RenderFlags, ctx: any) {
|
function testTemplate(rf: RenderFlags, ctx: any) {
|
||||||
if (rf & 1) {
|
if (rf & 1) {
|
||||||
ɵɵelementStart(0, 'div');
|
ɵɵelementStart(0, 'div');
|
||||||
ɵɵelementStart(1, 'button');
|
ɵɵelement(1, 'button');
|
||||||
ɵɵstyling();
|
ɵɵelement(2, 'button');
|
||||||
ɵɵelementEnd();
|
ɵɵelement(3, 'button');
|
||||||
ɵɵelementStart(2, 'button');
|
ɵɵelement(4, 'button');
|
||||||
ɵɵstyling();
|
ɵɵelement(5, 'button');
|
||||||
ɵɵelementEnd();
|
ɵɵelement(6, 'button');
|
||||||
ɵɵelementStart(3, 'button');
|
ɵɵelement(7, 'button');
|
||||||
ɵɵstyling();
|
ɵɵelement(8, 'button');
|
||||||
ɵɵelementEnd();
|
ɵɵelement(9, 'button');
|
||||||
ɵɵelementStart(4, 'button');
|
ɵɵelement(10, 'button');
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(5, 'button');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(6, 'button');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(7, 'button');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(8, 'button');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(9, 'button');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementStart(10, 'button');
|
|
||||||
ɵɵstyling();
|
|
||||||
ɵɵelementEnd();
|
|
||||||
ɵɵelementEnd();
|
ɵɵelementEnd();
|
||||||
}
|
}
|
||||||
if (rf & 2) {
|
if (rf & 2) {
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('background-color', 'color1');
|
ɵɵstyleProp('background-color', 'color1');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('background-color', 'color2');
|
ɵɵstyleProp('background-color', 'color2');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('background-color', 'color3');
|
ɵɵstyleProp('background-color', 'color3');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('background-color', 'color4');
|
ɵɵstyleProp('background-color', 'color4');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('background-color', 'color5');
|
ɵɵstyleProp('background-color', 'color5');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('background-color', 'color6');
|
ɵɵstyleProp('background-color', 'color6');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('background-color', 'color7');
|
ɵɵstyleProp('background-color', 'color7');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('background-color', 'color8');
|
ɵɵstyleProp('background-color', 'color8');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('background-color', 'color9');
|
ɵɵstyleProp('background-color', 'color9');
|
||||||
ɵɵstylingApply();
|
|
||||||
ɵɵadvance(1);
|
ɵɵadvance(1);
|
||||||
ɵɵstyleProp('background-color', 'color10');
|
ɵɵstyleProp('background-color', 'color10');
|
||||||
ɵɵstylingApply();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {normalizeIntoStylingMap as createMap} from '../../../src/render3/styling_next/map_based_bindings';
|
import {normalizeIntoStylingMap as createMap} from '../../../src/render3/styling_next/util';
|
||||||
|
|
||||||
describe('map-based bindings', () => {
|
describe('map-based bindings', () => {
|
||||||
describe('StylingMapArray construction', () => {
|
describe('StylingMapArray construction', () => {
|
||||||
|
|
|
@ -5,8 +5,10 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {DEFAULT_GUARD_MASK_VALUE, registerBinding} from '@angular/core/src/render3/styling_next/bindings';
|
import {registerBinding} from '@angular/core/src/render3/styling_next/bindings';
|
||||||
import {attachStylingDebugObject} from '@angular/core/src/render3/styling_next/styling_debug';
|
import {attachStylingDebugObject} from '@angular/core/src/render3/styling_next/styling_debug';
|
||||||
|
import {DEFAULT_GUARD_MASK_VALUE} from '@angular/core/src/render3/styling_next/util';
|
||||||
|
|
||||||
import {allocTStylingContext} from '../../../src/render3/styling_next/util';
|
import {allocTStylingContext} from '../../../src/render3/styling_next/util';
|
||||||
|
|
||||||
describe('styling context', () => {
|
describe('styling context', () => {
|
||||||
|
@ -15,33 +17,36 @@ describe('styling context', () => {
|
||||||
const context = debug.context;
|
const context = debug.context;
|
||||||
expect(debug.entries).toEqual({});
|
expect(debug.entries).toEqual({});
|
||||||
|
|
||||||
registerBinding(context, 1, 'width', '100px');
|
registerBinding(context, 1, 0, 'width', '100px');
|
||||||
expect(debug.entries['width']).toEqual({
|
expect(debug.entries['width']).toEqual({
|
||||||
prop: 'width',
|
prop: 'width',
|
||||||
valuesCount: 1,
|
valuesCount: 1,
|
||||||
sanitizationRequired: false,
|
sanitizationRequired: false,
|
||||||
guardMask: buildGuardMask(),
|
templateBitMask: buildGuardMask(),
|
||||||
|
hostBindingsBitMask: buildGuardMask(),
|
||||||
defaultValue: '100px',
|
defaultValue: '100px',
|
||||||
sources: ['100px'],
|
sources: ['100px'],
|
||||||
});
|
});
|
||||||
|
|
||||||
registerBinding(context, 2, 'width', 20);
|
registerBinding(context, 2, 0, 'width', 20);
|
||||||
expect(debug.entries['width']).toEqual({
|
expect(debug.entries['width']).toEqual({
|
||||||
prop: 'width',
|
prop: 'width',
|
||||||
sanitizationRequired: false,
|
sanitizationRequired: false,
|
||||||
valuesCount: 2,
|
valuesCount: 2,
|
||||||
guardMask: buildGuardMask(2),
|
templateBitMask: buildGuardMask(2),
|
||||||
|
hostBindingsBitMask: buildGuardMask(),
|
||||||
defaultValue: '100px',
|
defaultValue: '100px',
|
||||||
sources: [20, '100px'],
|
sources: [20, '100px'],
|
||||||
});
|
});
|
||||||
|
|
||||||
registerBinding(context, 3, 'height', 10);
|
registerBinding(context, 3, 0, 'height', 10);
|
||||||
registerBinding(context, 4, 'height', 15);
|
registerBinding(context, 4, 1, 'height', 15);
|
||||||
expect(debug.entries['height']).toEqual({
|
expect(debug.entries['height']).toEqual({
|
||||||
prop: 'height',
|
prop: 'height',
|
||||||
valuesCount: 3,
|
valuesCount: 3,
|
||||||
sanitizationRequired: false,
|
sanitizationRequired: false,
|
||||||
guardMask: buildGuardMask(3, 4),
|
templateBitMask: buildGuardMask(3),
|
||||||
|
hostBindingsBitMask: buildGuardMask(4),
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
sources: [10, 15, null],
|
sources: [10, 15, null],
|
||||||
});
|
});
|
||||||
|
@ -52,13 +57,14 @@ describe('styling context', () => {
|
||||||
const context = debug.context;
|
const context = debug.context;
|
||||||
expect(debug.entries).toEqual({});
|
expect(debug.entries).toEqual({});
|
||||||
|
|
||||||
registerBinding(context, 1, 'width', 123);
|
registerBinding(context, 1, 0, 'width', 123);
|
||||||
registerBinding(context, 1, 'width', 123);
|
registerBinding(context, 1, 0, 'width', 123);
|
||||||
expect(debug.entries['width']).toEqual({
|
expect(debug.entries['width']).toEqual({
|
||||||
prop: 'width',
|
prop: 'width',
|
||||||
valuesCount: 2,
|
valuesCount: 2,
|
||||||
sanitizationRequired: false,
|
sanitizationRequired: false,
|
||||||
guardMask: buildGuardMask(1),
|
templateBitMask: buildGuardMask(1),
|
||||||
|
hostBindingsBitMask: buildGuardMask(),
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
sources: [123, null],
|
sources: [123, null],
|
||||||
});
|
});
|
||||||
|
@ -68,33 +74,36 @@ describe('styling context', () => {
|
||||||
const debug = makeContextWithDebug();
|
const debug = makeContextWithDebug();
|
||||||
const context = debug.context;
|
const context = debug.context;
|
||||||
|
|
||||||
registerBinding(context, 1, 'width', null);
|
registerBinding(context, 1, 0, 'width', null);
|
||||||
const x = debug.entries['width'];
|
const x = debug.entries['width'];
|
||||||
expect(debug.entries['width']).toEqual({
|
expect(debug.entries['width']).toEqual({
|
||||||
prop: 'width',
|
prop: 'width',
|
||||||
valuesCount: 1,
|
valuesCount: 1,
|
||||||
sanitizationRequired: false,
|
sanitizationRequired: false,
|
||||||
guardMask: buildGuardMask(),
|
templateBitMask: buildGuardMask(),
|
||||||
|
hostBindingsBitMask: buildGuardMask(),
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
sources: [null]
|
sources: [null]
|
||||||
});
|
});
|
||||||
|
|
||||||
registerBinding(context, 1, 'width', '100px');
|
registerBinding(context, 1, 0, 'width', '100px');
|
||||||
expect(debug.entries['width']).toEqual({
|
expect(debug.entries['width']).toEqual({
|
||||||
prop: 'width',
|
prop: 'width',
|
||||||
valuesCount: 1,
|
valuesCount: 1,
|
||||||
sanitizationRequired: false,
|
sanitizationRequired: false,
|
||||||
guardMask: buildGuardMask(),
|
templateBitMask: buildGuardMask(),
|
||||||
|
hostBindingsBitMask: buildGuardMask(),
|
||||||
defaultValue: '100px',
|
defaultValue: '100px',
|
||||||
sources: ['100px']
|
sources: ['100px']
|
||||||
});
|
});
|
||||||
|
|
||||||
registerBinding(context, 1, 'width', '200px');
|
registerBinding(context, 1, 0, 'width', '200px');
|
||||||
expect(debug.entries['width']).toEqual({
|
expect(debug.entries['width']).toEqual({
|
||||||
prop: 'width',
|
prop: 'width',
|
||||||
valuesCount: 1,
|
valuesCount: 1,
|
||||||
sanitizationRequired: false,
|
sanitizationRequired: false,
|
||||||
guardMask: buildGuardMask(),
|
templateBitMask: buildGuardMask(),
|
||||||
|
hostBindingsBitMask: buildGuardMask(),
|
||||||
defaultValue: '100px',
|
defaultValue: '100px',
|
||||||
sources: ['100px']
|
sources: ['100px']
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,7 +18,7 @@ describe('styling debugging tools', () => {
|
||||||
const data: any[] = [];
|
const data: any[] = [];
|
||||||
const d = new NodeStylingDebug(context, data);
|
const d = new NodeStylingDebug(context, data);
|
||||||
|
|
||||||
registerBinding(context, 0, 'width', null);
|
registerBinding(context, 0, 0, 'width', null);
|
||||||
expect(d.summary).toEqual({
|
expect(d.summary).toEqual({
|
||||||
width: {
|
width: {
|
||||||
prop: 'width',
|
prop: 'width',
|
||||||
|
@ -27,7 +27,7 @@ describe('styling debugging tools', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
registerBinding(context, 0, 'width', '100px');
|
registerBinding(context, 0, 0, 'width', '100px');
|
||||||
expect(d.summary).toEqual({
|
expect(d.summary).toEqual({
|
||||||
width: {
|
width: {
|
||||||
prop: 'width',
|
prop: 'width',
|
||||||
|
@ -39,7 +39,7 @@ describe('styling debugging tools', () => {
|
||||||
const someBindingIndex1 = 1;
|
const someBindingIndex1 = 1;
|
||||||
data[someBindingIndex1] = '200px';
|
data[someBindingIndex1] = '200px';
|
||||||
|
|
||||||
registerBinding(context, 0, 'width', someBindingIndex1);
|
registerBinding(context, 0, 0, 'width', someBindingIndex1);
|
||||||
expect(d.summary).toEqual({
|
expect(d.summary).toEqual({
|
||||||
width: {
|
width: {
|
||||||
prop: 'width',
|
prop: 'width',
|
||||||
|
@ -51,7 +51,7 @@ describe('styling debugging tools', () => {
|
||||||
const someBindingIndex2 = 2;
|
const someBindingIndex2 = 2;
|
||||||
data[someBindingIndex2] = '500px';
|
data[someBindingIndex2] = '500px';
|
||||||
|
|
||||||
registerBinding(context, 0, 'width', someBindingIndex2);
|
registerBinding(context, 0, 1, 'width', someBindingIndex2);
|
||||||
expect(d.summary).toEqual({
|
expect(d.summary).toEqual({
|
||||||
width: {
|
width: {
|
||||||
prop: 'width',
|
prop: 'width',
|
||||||
|
|
|
@ -1076,10 +1076,6 @@ export declare function ɵɵstylePropInterpolateV(prop: string, values: any[], v
|
||||||
|
|
||||||
export declare function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn | null): void;
|
export declare function ɵɵstyleSanitizer(sanitizer: StyleSanitizeFn | null): void;
|
||||||
|
|
||||||
export declare function ɵɵstyling(): void;
|
|
||||||
|
|
||||||
export declare function ɵɵstylingApply(): void;
|
|
||||||
|
|
||||||
export declare function ɵɵtemplate(index: number, templateFn: ComponentTemplate<any> | null, consts: number, vars: number, tagName?: string | null, attrs?: TAttributes | null, localRefs?: string[] | null, localRefExtractor?: LocalRefExtractor): void;
|
export declare function ɵɵtemplate(index: number, templateFn: ComponentTemplate<any> | null, consts: number, vars: number, tagName?: string | null, attrs?: TAttributes | null, localRefs?: string[] | null, localRefExtractor?: LocalRefExtractor): void;
|
||||||
|
|
||||||
export declare function ɵɵtemplateRefExtractor(tNode: TNode, currentView: LView): ViewEngine_TemplateRef<unknown> | null;
|
export declare function ɵɵtemplateRefExtractor(tNode: TNode, currentView: LView): ViewEngine_TemplateRef<unknown> | null;
|
||||||
|
|
Loading…
Reference in New Issue