fix(ivy): temporary hack to set host styles and static classes (#27385)
PR Close #27385
This commit is contained in:
parent
7d89cff545
commit
2a39425e48
@ -38,13 +38,14 @@ import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
|||||||
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
|
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
|
||||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||||
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCreationMode, getCurrentDirectiveDef, getElementDepthCount, getFirstTemplatePass, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode} from './state';
|
import {decreaseElementDepthCount, enterView, getBindingsEnabled, getCheckNoChangesMode, getContextLView, getCreationMode, getCurrentDirectiveDef, getElementDepthCount, getFirstTemplatePass, getIsParent, getLView, getPreviousOrParentTNode, increaseElementDepthCount, leaveView, nextContextImpl, resetComponentState, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setFirstTemplatePass, setIsParent, setPreviousOrParentTNode} from './state';
|
||||||
import {createStylingContextTemplate, renderStyleAndClassBindings, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
|
import {createStylingContextTemplate, renderStyleAndClassBindings, setStyle, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
|
||||||
import {BoundPlayerFactory} from './styling/player_factory';
|
import {BoundPlayerFactory} from './styling/player_factory';
|
||||||
import {getStylingContext} from './styling/util';
|
import {getStylingContext} from './styling/util';
|
||||||
import {NO_CHANGE} from './tokens';
|
import {NO_CHANGE} from './tokens';
|
||||||
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getRootContext, getRootView, getTNode, isComponent, isComponentDef, loadInternal, readElementValue, readPatchedLView, stringify} from './util';
|
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getRootContext, getRootView, getTNode, isComponent, isComponentDef, loadInternal, readElementValue, readPatchedLView, stringify} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A permanent marker promise which signifies that the current CD tree is
|
* A permanent marker promise which signifies that the current CD tree is
|
||||||
* clean.
|
* clean.
|
||||||
@ -1216,9 +1217,6 @@ export function elementStylingApply(index: number, directive?: {}): void {
|
|||||||
export function elementStyleProp(
|
export function elementStyleProp(
|
||||||
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
|
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
|
||||||
suffix?: string, directive?: {}): void {
|
suffix?: string, directive?: {}): void {
|
||||||
if (directive != undefined)
|
|
||||||
return hackImplementationOfElementStyleProp(
|
|
||||||
index, styleIndex, value, suffix, directive); // supported in next PR
|
|
||||||
let valueToAdd: string|null = null;
|
let valueToAdd: string|null = null;
|
||||||
if (value !== null) {
|
if (value !== null) {
|
||||||
if (suffix) {
|
if (suffix) {
|
||||||
@ -1233,7 +1231,11 @@ export function elementStyleProp(
|
|||||||
valueToAdd = value as any as string;
|
valueToAdd = value as any as string;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateElementStyleProp(getStylingContext(index, getLView()), styleIndex, valueToAdd);
|
if (directive != undefined) {
|
||||||
|
hackImplementationOfElementStyleProp(index, styleIndex, valueToAdd, suffix, directive);
|
||||||
|
} else {
|
||||||
|
updateElementStyleProp(getStylingContext(index, getLView()), styleIndex, valueToAdd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1293,14 +1295,40 @@ function hackImplementationOfElementStyling(
|
|||||||
classDeclarations: (string | boolean | InitialStylingFlags)[] | null,
|
classDeclarations: (string | boolean | InitialStylingFlags)[] | null,
|
||||||
styleDeclarations: (string | boolean | InitialStylingFlags)[] | null,
|
styleDeclarations: (string | boolean | InitialStylingFlags)[] | null,
|
||||||
styleSanitizer: StyleSanitizeFn | null, directive: {}): void {
|
styleSanitizer: StyleSanitizeFn | null, directive: {}): void {
|
||||||
const node = getNativeByTNode(getPreviousOrParentTNode(), getLView());
|
const node = getNativeByTNode(getPreviousOrParentTNode(), getLView()) as RElement;
|
||||||
ngDevMode && assertDefined(node, 'expecting parent DOM node');
|
ngDevMode && assertDefined(node, 'expecting parent DOM node');
|
||||||
const hostStylingHackMap: HostStylingHackMap =
|
const hostStylingHackMap: HostStylingHackMap =
|
||||||
((node as any).hostStylingHack || ((node as any).hostStylingHack = new Map()));
|
((node as any).hostStylingHack || ((node as any).hostStylingHack = new Map()));
|
||||||
|
const squashedClassDeclarations = hackSquashDeclaration(classDeclarations);
|
||||||
hostStylingHackMap.set(directive, {
|
hostStylingHackMap.set(directive, {
|
||||||
classDeclarations: hackSquashDeclaration(classDeclarations),
|
classDeclarations: squashedClassDeclarations,
|
||||||
styleDeclarations: hackSquashDeclaration(styleDeclarations), styleSanitizer
|
styleDeclarations: hackSquashDeclaration(styleDeclarations), styleSanitizer
|
||||||
});
|
});
|
||||||
|
hackSetStaticClasses(node, squashedClassDeclarations);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hackSetStaticClasses(node: RElement, classDeclarations: (string | boolean)[]) {
|
||||||
|
// Static classes need to be set here because static classes don't generate
|
||||||
|
// elementClassProp instructions.
|
||||||
|
const lView = getLView();
|
||||||
|
const staticClassStartIndex =
|
||||||
|
classDeclarations.indexOf(InitialStylingFlags.VALUES_MODE as any) + 1;
|
||||||
|
const renderer = lView[RENDERER];
|
||||||
|
|
||||||
|
for (let i = staticClassStartIndex; i < classDeclarations.length; i += 2) {
|
||||||
|
const className = classDeclarations[i] as string;
|
||||||
|
const value = classDeclarations[i + 1];
|
||||||
|
// if value is true, then this is a static class and we should set it now.
|
||||||
|
// class bindings are set separately in elementClassProp.
|
||||||
|
if (value === true) {
|
||||||
|
if (isProceduralRenderer(renderer)) {
|
||||||
|
renderer.addClass(node, className);
|
||||||
|
} else {
|
||||||
|
const classList = (node as HTMLElement).classList;
|
||||||
|
classList.add(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function hackSquashDeclaration(declarations: (string | boolean | InitialStylingFlags)[] | null):
|
function hackSquashDeclaration(declarations: (string | boolean | InitialStylingFlags)[] | null):
|
||||||
@ -1330,9 +1358,15 @@ function hackImplementationOfElementStylingApply(index: number, directive?: {}):
|
|||||||
}
|
}
|
||||||
|
|
||||||
function hackImplementationOfElementStyleProp(
|
function hackImplementationOfElementStyleProp(
|
||||||
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
|
index: number, styleIndex: number, value: string | null, suffix?: string,
|
||||||
suffix?: string, directive?: {}): void {
|
directive?: {}): void {
|
||||||
throw new Error('unimplemented. Should not be needed by ViewEngine compatibility');
|
const lView = getLView();
|
||||||
|
const node = getNativeByIndex(index, lView);
|
||||||
|
ngDevMode && assertDefined(node, 'could not locate node');
|
||||||
|
const hostStylingHack: HostStylingHack = (node as any).hostStylingHack.get(directive);
|
||||||
|
const styleName = hostStylingHack.styleDeclarations[styleIndex];
|
||||||
|
const renderer = lView[RENDERER];
|
||||||
|
setStyle(node, styleName, value as string, renderer, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hackImplementationOfElementStylingMap<T>(
|
function hackImplementationOfElementStylingMap<T>(
|
||||||
|
@ -594,7 +594,7 @@ export function renderStyleAndClassBindings(
|
|||||||
* @param renderer
|
* @param renderer
|
||||||
* @param store an optional key/value map that will be used as a context to render styles on
|
* @param store an optional key/value map that will be used as a context to render styles on
|
||||||
*/
|
*/
|
||||||
function setStyle(
|
export function setStyle(
|
||||||
native: any, prop: string, value: string | null, renderer: Renderer3,
|
native: any, prop: string, value: string | null, renderer: Renderer3,
|
||||||
sanitizer: StyleSanitizeFn | null, store?: BindingStore | null,
|
sanitizer: StyleSanitizeFn | null, store?: BindingStore | null,
|
||||||
playerBuilder?: ClassAndStylePlayerBuilder<any>| null) {
|
playerBuilder?: ClassAndStylePlayerBuilder<any>| null) {
|
||||||
|
@ -785,6 +785,9 @@
|
|||||||
{
|
{
|
||||||
"name": "hackImplementationOfElementStylingMap"
|
"name": "hackImplementationOfElementStylingMap"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "hackSetStaticClasses"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hackSquashDeclaration"
|
"name": "hackSquashDeclaration"
|
||||||
},
|
},
|
||||||
|
@ -818,6 +818,9 @@
|
|||||||
{
|
{
|
||||||
"name": "hackImplementationOfElementStylingApply"
|
"name": "hackImplementationOfElementStylingApply"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "hackSetStaticClasses"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hackSquashDeclaration"
|
"name": "hackSquashDeclaration"
|
||||||
},
|
},
|
||||||
|
@ -2000,6 +2000,9 @@
|
|||||||
{
|
{
|
||||||
"name": "hackImplementationOfElementStylingApply"
|
"name": "hackImplementationOfElementStylingApply"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "hackSetStaticClasses"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hackSquashDeclaration"
|
"name": "hackSquashDeclaration"
|
||||||
},
|
},
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
import {ElementRef, EventEmitter} from '@angular/core';
|
import {ElementRef, EventEmitter} from '@angular/core';
|
||||||
|
|
||||||
import {AttributeMarker, defineComponent, template, defineDirective, InheritDefinitionFeature, ProvidersFeature, NgOnChangesFeature, QueryList} from '../../src/render3/index';
|
import {AttributeMarker, defineComponent, template, defineDirective, InheritDefinitionFeature, ProvidersFeature, NgOnChangesFeature, QueryList} from '../../src/render3/index';
|
||||||
import {allocHostVars, bind, directiveInject, element, elementEnd, elementProperty, elementStart, listener, load, text, textBinding, loadQueryList, registerContentQuery} from '../../src/render3/instructions';
|
import {allocHostVars, bind, directiveInject, element, elementEnd, elementProperty, elementStyleProp, elementStyling, elementStylingApply, elementStart, listener, load, text, textBinding, loadQueryList, registerContentQuery} from '../../src/render3/instructions';
|
||||||
import {query, queryRefresh} from '../../src/render3/query';
|
import {query, queryRefresh} from '../../src/render3/query';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags, InitialStylingFlags} from '../../src/render3/interfaces/definition';
|
||||||
import {pureFunction1, pureFunction2} from '../../src/render3/pure_function';
|
import {pureFunction1, pureFunction2} from '../../src/render3/pure_function';
|
||||||
|
|
||||||
import {ComponentFixture, TemplateFixture, createComponent, createDirective} from './render_util';
|
import {ComponentFixture, TemplateFixture, createComponent, createDirective} from './render_util';
|
||||||
@ -978,4 +978,95 @@ describe('host bindings', () => {
|
|||||||
const hostBindingEl = fixture.hostElement.querySelector('host-binding-comp') as HTMLElement;
|
const hostBindingEl = fixture.hostElement.querySelector('host-binding-comp') as HTMLElement;
|
||||||
expect(hostBindingEl.id).toEqual('after-content');
|
expect(hostBindingEl.id).toEqual('after-content');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('styles', () => {
|
||||||
|
|
||||||
|
it('should bind to host styles', () => {
|
||||||
|
let hostBindingDir !: HostBindingToStyles;
|
||||||
|
/**
|
||||||
|
* host: {
|
||||||
|
* '[style.width.px]': 'width'
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
class HostBindingToStyles {
|
||||||
|
width = 2;
|
||||||
|
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: HostBindingToStyles,
|
||||||
|
selectors: [['host-binding-to-styles']],
|
||||||
|
factory: () => hostBindingDir = new HostBindingToStyles(),
|
||||||
|
consts: 0,
|
||||||
|
vars: 0,
|
||||||
|
hostBindings: (rf: RenderFlags, ctx: HostBindingToStyles, elIndex: number) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
allocHostVars(0); // this is wrong, but necessary until FW-761 gets in
|
||||||
|
elementStyling(null, ['width'], null, ctx);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
elementStyleProp(0, 0, ctx.width, 'px', ctx);
|
||||||
|
elementStylingApply(0, ctx);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: (rf: RenderFlags, cmp: HostBindingToStyles) => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** <host-binding-to-styles></host-binding-to-styles> */
|
||||||
|
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
element(0, 'host-binding-to-styles');
|
||||||
|
}
|
||||||
|
}, 1, 0, [HostBindingToStyles]);
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(App);
|
||||||
|
const hostBindingEl =
|
||||||
|
fixture.hostElement.querySelector('host-binding-to-styles') as HTMLElement;
|
||||||
|
expect(hostBindingEl.style.width).toEqual('2px');
|
||||||
|
|
||||||
|
hostBindingDir.width = 5;
|
||||||
|
fixture.update();
|
||||||
|
expect(hostBindingEl.style.width).toEqual('5px');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should apply static host classes', () => {
|
||||||
|
/**
|
||||||
|
* host: {
|
||||||
|
* 'class': 'mat-toolbar'
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
class StaticHostClass {
|
||||||
|
static ngComponentDef = defineComponent({
|
||||||
|
type: StaticHostClass,
|
||||||
|
selectors: [['static-host-class']],
|
||||||
|
factory: () => new StaticHostClass(),
|
||||||
|
consts: 0,
|
||||||
|
vars: 0,
|
||||||
|
hostBindings: (rf: RenderFlags, ctx: StaticHostClass, elIndex: number) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
allocHostVars(0); // this is wrong, but necessary until FW-761 gets in
|
||||||
|
elementStyling(
|
||||||
|
['mat-toolbar', InitialStylingFlags.VALUES_MODE, 'mat-toolbar', true], null, null,
|
||||||
|
ctx);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
elementStylingApply(0, ctx);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
template: (rf: RenderFlags, cmp: StaticHostClass) => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** <static-host-class></static-host-class> */
|
||||||
|
const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
element(0, 'static-host-class');
|
||||||
|
}
|
||||||
|
}, 1, 0, [StaticHostClass]);
|
||||||
|
|
||||||
|
const fixture = new ComponentFixture(App);
|
||||||
|
const hostBindingEl = fixture.hostElement.querySelector('static-host-class') as HTMLElement;
|
||||||
|
expect(hostBindingEl.className).toEqual('mat-toolbar');
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user