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 {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 {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 {getStylingContext} from './styling/util';
|
||||
import {NO_CHANGE} from './tokens';
|
||||
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
|
||||
* clean.
|
||||
|
@ -1216,9 +1217,6 @@ export function elementStylingApply(index: number, directive?: {}): void {
|
|||
export function elementStyleProp(
|
||||
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
|
||||
suffix?: string, directive?: {}): void {
|
||||
if (directive != undefined)
|
||||
return hackImplementationOfElementStyleProp(
|
||||
index, styleIndex, value, suffix, directive); // supported in next PR
|
||||
let valueToAdd: string|null = null;
|
||||
if (value !== null) {
|
||||
if (suffix) {
|
||||
|
@ -1233,7 +1231,11 @@ export function elementStyleProp(
|
|||
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,
|
||||
styleDeclarations: (string | boolean | InitialStylingFlags)[] | null,
|
||||
styleSanitizer: StyleSanitizeFn | null, directive: {}): void {
|
||||
const node = getNativeByTNode(getPreviousOrParentTNode(), getLView());
|
||||
const node = getNativeByTNode(getPreviousOrParentTNode(), getLView()) as RElement;
|
||||
ngDevMode && assertDefined(node, 'expecting parent DOM node');
|
||||
const hostStylingHackMap: HostStylingHackMap =
|
||||
((node as any).hostStylingHack || ((node as any).hostStylingHack = new Map()));
|
||||
const squashedClassDeclarations = hackSquashDeclaration(classDeclarations);
|
||||
hostStylingHackMap.set(directive, {
|
||||
classDeclarations: hackSquashDeclaration(classDeclarations),
|
||||
classDeclarations: squashedClassDeclarations,
|
||||
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):
|
||||
|
@ -1330,9 +1358,15 @@ function hackImplementationOfElementStylingApply(index: number, directive?: {}):
|
|||
}
|
||||
|
||||
function hackImplementationOfElementStyleProp(
|
||||
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
|
||||
suffix?: string, directive?: {}): void {
|
||||
throw new Error('unimplemented. Should not be needed by ViewEngine compatibility');
|
||||
index: number, styleIndex: number, value: string | null, suffix?: string,
|
||||
directive?: {}): void {
|
||||
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>(
|
||||
|
|
|
@ -594,7 +594,7 @@ export function renderStyleAndClassBindings(
|
|||
* @param renderer
|
||||
* @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,
|
||||
sanitizer: StyleSanitizeFn | null, store?: BindingStore | null,
|
||||
playerBuilder?: ClassAndStylePlayerBuilder<any>| null) {
|
||||
|
|
|
@ -785,6 +785,9 @@
|
|||
{
|
||||
"name": "hackImplementationOfElementStylingMap"
|
||||
},
|
||||
{
|
||||
"name": "hackSetStaticClasses"
|
||||
},
|
||||
{
|
||||
"name": "hackSquashDeclaration"
|
||||
},
|
||||
|
|
|
@ -818,6 +818,9 @@
|
|||
{
|
||||
"name": "hackImplementationOfElementStylingApply"
|
||||
},
|
||||
{
|
||||
"name": "hackSetStaticClasses"
|
||||
},
|
||||
{
|
||||
"name": "hackSquashDeclaration"
|
||||
},
|
||||
|
|
|
@ -2000,6 +2000,9 @@
|
|||
{
|
||||
"name": "hackImplementationOfElementStylingApply"
|
||||
},
|
||||
{
|
||||
"name": "hackSetStaticClasses"
|
||||
},
|
||||
{
|
||||
"name": "hackSquashDeclaration"
|
||||
},
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
import {ElementRef, EventEmitter} from '@angular/core';
|
||||
|
||||
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 {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {RenderFlags, InitialStylingFlags} from '../../src/render3/interfaces/definition';
|
||||
import {pureFunction1, pureFunction2} from '../../src/render3/pure_function';
|
||||
|
||||
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;
|
||||
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…
Reference in New Issue