feat(ivy): properly apply class="", [class], [class.foo] and [attr.class] bindings (#24822)
PR Close #24822
This commit is contained in:
@ -15,7 +15,6 @@ import {ComponentDefInternal, InitialStylingFlags} from '../../../src/render3/in
|
||||
import {ComponentFixture, renderComponent, toHtml} from '../render_util';
|
||||
|
||||
|
||||
|
||||
/// See: `normative.md`
|
||||
describe('elements', () => {
|
||||
// Saving type as $any$, etc to simplify testing for compiler, as types aren't saved
|
||||
@ -271,6 +270,7 @@ describe('elements', () => {
|
||||
});
|
||||
|
||||
it('should bind to a specific class', () => {
|
||||
const c1: (string | InitialStylingFlags | boolean)[] = ['foo'];
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
@Component({selector: 'my-component', template: `<div [class.foo]="someFlag"></div>`})
|
||||
@ -283,10 +283,13 @@ describe('elements', () => {
|
||||
factory: function MyComponent_Factory() { return new MyComponent(); },
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵEe(0, 'div');
|
||||
$r3$.ɵE(0, 'div');
|
||||
$r3$.ɵs(null, c1);
|
||||
$r3$.ɵe();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵkn(0, 'foo', $r3$.ɵb(ctx.someFlag));
|
||||
$r3$.ɵcp(0, 0, ctx.someFlag);
|
||||
$r3$.ɵsa(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -320,13 +323,13 @@ describe('elements', () => {
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'div');
|
||||
$r3$.ɵs(1, c0);
|
||||
$r3$.ɵs(c0);
|
||||
$r3$.ɵe();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵsp(1, 0, ctx.someColor);
|
||||
$r3$.ɵsp(1, 1, ctx.someWidth, 'px');
|
||||
$r3$.ɵsa(1);
|
||||
$r3$.ɵsp(0, 0, ctx.someColor);
|
||||
$r3$.ɵsp(0, 1, ctx.someWidth, 'px');
|
||||
$r3$.ɵsa(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -353,7 +356,9 @@ describe('elements', () => {
|
||||
it('should bind to many and keep order', () => {
|
||||
type $MyComponent$ = MyComponent;
|
||||
|
||||
const c0 = ['color', InitialStylingFlags.INITIAL_STYLES, 'color', 'red'];
|
||||
const c0 = ['color', InitialStylingFlags.VALUES_MODE, 'color', 'red'];
|
||||
const c1 = ['foo'];
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template:
|
||||
@ -369,12 +374,13 @@ describe('elements', () => {
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'div');
|
||||
$r3$.ɵs(1, c0);
|
||||
$r3$.ɵs(c0, c1);
|
||||
$r3$.ɵe();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(0, 'id', $r3$.ɵb(ctx.someString + 1));
|
||||
$r3$.ɵkn(0, 'foo', $r3$.ɵb(ctx.someString == 'initial'));
|
||||
$r3$.ɵcp(0, 0, ctx.someString == 'initial');
|
||||
$r3$.ɵsa(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -406,13 +412,12 @@ describe('elements', () => {
|
||||
template: function StyleComponent_Template(rf: $RenderFlags$, ctx: $StyleComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'div');
|
||||
$r3$.ɵs(1);
|
||||
$r3$.ɵs();
|
||||
$r3$.ɵe();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵk(0, $r3$.ɵb(ctx.classExp));
|
||||
$r3$.ɵsm(1, ctx.styleExp);
|
||||
$r3$.ɵsa(1);
|
||||
$r3$.ɵsm(0, ctx.styleExp, ctx.classExp);
|
||||
$r3$.ɵsa(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -44,17 +44,17 @@ describe('compiler sanitization', () => {
|
||||
template: function MyComponent_Template(rf: $RenderFlags$, ctx: $MyComponent$) {
|
||||
if (rf & 1) {
|
||||
$r3$.ɵE(0, 'div');
|
||||
$r3$.ɵs(1, ['background-image']);
|
||||
$r3$.ɵs(['background-image']);
|
||||
$r3$.ɵe();
|
||||
$r3$.ɵEe(2, 'img');
|
||||
$r3$.ɵEe(1, 'img');
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵp(0, 'innerHTML', $r3$.ɵb(ctx.innerHTML), $r3$.ɵsanitizeHtml);
|
||||
$r3$.ɵp(0, 'hidden', $r3$.ɵb(ctx.hidden));
|
||||
$r3$.ɵsp(1, 0, ctx.style, $r3$.ɵsanitizeStyle);
|
||||
$r3$.ɵsa(1);
|
||||
$r3$.ɵp(2, 'src', $r3$.ɵb(ctx.url), $r3$.ɵsanitizeUrl);
|
||||
$r3$.ɵa(2, 'srcset', $r3$.ɵb(ctx.url), $r3$.ɵsanitizeUrl);
|
||||
$r3$.ɵsp(0, 0, ctx.style, $r3$.ɵsanitizeStyle);
|
||||
$r3$.ɵsa(0);
|
||||
$r3$.ɵp(1, 'src', $r3$.ɵb(ctx.url), $r3$.ɵsanitizeUrl);
|
||||
$r3$.ɵa(1, 'srcset', $r3$.ɵb(ctx.url), $r3$.ɵsanitizeUrl);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -7,8 +7,9 @@
|
||||
*/
|
||||
|
||||
import {defineComponent, defineDirective} from '../../src/render3/index';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, elementAttribute, elementClassNamed, elementEnd, elementProperty, elementStart, embeddedViewEnd, embeddedViewStart, load, text, textBinding} from '../../src/render3/instructions';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {bind, container, containerRefreshEnd, containerRefreshStart, elementAttribute, elementClassProp, elementEnd, elementProperty, elementStart, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, load, text, textBinding} from '../../src/render3/instructions';
|
||||
import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
|
||||
import {ComponentFixture, createComponent, renderToHtml} from './render_util';
|
||||
|
||||
describe('exports', () => {
|
||||
@ -212,13 +213,15 @@ describe('exports', () => {
|
||||
function Template(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'div');
|
||||
elementStyling(null, [InitialStylingFlags.VALUES_MODE, 'red', true]);
|
||||
elementEnd();
|
||||
elementStart(1, 'input', ['type', 'checkbox', 'checked', 'true'], ['myInput', '']);
|
||||
elementEnd();
|
||||
}
|
||||
const tmp = load(2) as any;
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementClassNamed(0, 'red', bind(tmp.checked));
|
||||
elementClassProp(0, 0, tmp.checked);
|
||||
elementStylingApply(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {NgForOfContext} from '@angular/common';
|
||||
|
||||
import {RenderFlags, directiveInject} from '../../src/render3';
|
||||
import {defineComponent} from '../../src/render3/definition';
|
||||
import {bind, container, element, elementAttribute, elementClass, elementEnd, elementProperty, elementStart, elementStyle, elementStyleProp, elementStyling, elementStylingApply, interpolation1, renderTemplate, text, textBinding} from '../../src/render3/instructions';
|
||||
import {bind, container, element, elementAttribute, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, elementStylingMap, interpolation1, renderTemplate, text, textBinding} from '../../src/render3/instructions';
|
||||
import {InitialStylingFlags} from '../../src/render3/interfaces/definition';
|
||||
import {AttributeMarker, LElementNode, LNode} from '../../src/render3/interfaces/node';
|
||||
import {RElement, domRendererFactory3} from '../../src/render3/interfaces/renderer';
|
||||
@ -23,13 +23,13 @@ import {ComponentFixture, TemplateFixture} from './render_util';
|
||||
describe('instructions', () => {
|
||||
function createAnchor() {
|
||||
elementStart(0, 'a');
|
||||
elementStyling(1);
|
||||
elementStyling();
|
||||
elementEnd();
|
||||
}
|
||||
|
||||
function createDiv(initialStyles?: (string | number)[]) {
|
||||
elementStart(0, 'div');
|
||||
elementStyling(1, initialStyles && Array.isArray(initialStyles) ? initialStyles : null);
|
||||
elementStyling(initialStyles && Array.isArray(initialStyles) ? initialStyles : null);
|
||||
elementEnd();
|
||||
}
|
||||
|
||||
@ -193,15 +193,15 @@ describe('instructions', () => {
|
||||
it('should use sanitizer function', () => {
|
||||
const t = new TemplateFixture(() => { return createDiv(['background-image']); });
|
||||
t.update(() => {
|
||||
elementStyleProp(1, 0, 'url("http://server")', sanitizeStyle);
|
||||
elementStylingApply(1);
|
||||
elementStyleProp(0, 0, 'url("http://server")', sanitizeStyle);
|
||||
elementStylingApply(0);
|
||||
});
|
||||
// nothing is set because sanitizer suppresses it.
|
||||
expect(t.html).toEqual('<div></div>');
|
||||
|
||||
t.update(() => {
|
||||
elementStyleProp(1, 0, bypassSanitizationTrustStyle('url("http://server")'), sanitizeStyle);
|
||||
elementStylingApply(1);
|
||||
elementStyleProp(0, 0, bypassSanitizationTrustStyle('url("http://server")'), sanitizeStyle);
|
||||
elementStylingApply(0);
|
||||
});
|
||||
expect((t.hostElement.firstChild as HTMLElement).style.getPropertyValue('background-image'))
|
||||
.toEqual('url("http://server")');
|
||||
@ -211,25 +211,33 @@ describe('instructions', () => {
|
||||
describe('elementStyleMap', () => {
|
||||
function createDivWithStyle() {
|
||||
elementStart(0, 'div');
|
||||
elementStyling(1, ['height', InitialStylingFlags.INITIAL_STYLES, 'height', '10px']);
|
||||
elementStyling(['height', InitialStylingFlags.VALUES_MODE, 'height', '10px']);
|
||||
elementEnd();
|
||||
}
|
||||
|
||||
it('should add style', () => {
|
||||
const fixture = new TemplateFixture(createDivWithStyle);
|
||||
fixture.update(() => {
|
||||
elementStyle(1, {'background-color': 'red'});
|
||||
elementStylingApply(1);
|
||||
elementStylingMap(0, {'background-color': 'red'});
|
||||
elementStylingApply(0);
|
||||
});
|
||||
expect(fixture.html).toEqual('<div style="height: 10px; background-color: red;"></div>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('elementClass', () => {
|
||||
function createDivWithStyling() {
|
||||
elementStart(0, 'div');
|
||||
elementStyling();
|
||||
elementEnd();
|
||||
}
|
||||
|
||||
it('should add class', () => {
|
||||
const fixture = new TemplateFixture(createDiv);
|
||||
fixture.update(() => elementClass(0, 'multiple classes'));
|
||||
const fixture = new TemplateFixture(createDivWithStyling);
|
||||
fixture.update(() => {
|
||||
elementStylingMap(0, null, 'multiple classes');
|
||||
elementStylingApply(0);
|
||||
});
|
||||
expect(fixture.html).toEqual('<div class="multiple classes"></div>');
|
||||
});
|
||||
});
|
||||
|
@ -9,7 +9,8 @@
|
||||
import {RenderFlags} from '@angular/core/src/render3';
|
||||
|
||||
import {defineComponent, defineDirective} from '../../src/render3/index';
|
||||
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, elementAttribute, elementClassNamed, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, loadDirective, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
|
||||
import {NO_CHANGE, bind, container, containerRefreshEnd, containerRefreshStart, elementAttribute, elementClassProp, elementEnd, elementProperty, elementStart, elementStyleProp, elementStyling, elementStylingApply, embeddedViewEnd, embeddedViewStart, interpolation1, interpolation2, interpolation3, interpolation4, interpolation5, interpolation6, interpolation7, interpolation8, interpolationV, load, loadDirective, projection, projectionDef, text, textBinding} from '../../src/render3/instructions';
|
||||
import {InitialStylingFlags} from '../../src/render3/interfaces/definition';
|
||||
import {HEADER_OFFSET} from '../../src/render3/interfaces/view';
|
||||
import {sanitizeUrl} from '../../src/sanitization/sanitization';
|
||||
import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
|
||||
@ -747,12 +748,12 @@ describe('render3 integration test', () => {
|
||||
function Template(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'span');
|
||||
elementStyling(1, ['border-color']);
|
||||
elementStyling(['border-color']);
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementStyleProp(1, 0, ctx);
|
||||
elementStylingApply(1);
|
||||
elementStyleProp(0, 0, ctx);
|
||||
elementStylingApply(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -766,12 +767,12 @@ describe('render3 integration test', () => {
|
||||
function Template(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'span');
|
||||
elementStyling(1, ['font-size']);
|
||||
elementStyling(['font-size']);
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementStyleProp(1, 0, ctx, 'px');
|
||||
elementStylingApply(1);
|
||||
elementStyleProp(0, 0, ctx, 'px');
|
||||
elementStylingApply(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -787,10 +788,12 @@ describe('render3 integration test', () => {
|
||||
function Template(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'span');
|
||||
elementStyling(null, ['active']);
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementClassNamed(0, 'active', bind(ctx));
|
||||
elementClassProp(0, 0, ctx);
|
||||
elementStylingApply(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -809,11 +812,14 @@ describe('render3 integration test', () => {
|
||||
it('should work correctly with existing static classes', () => {
|
||||
function Template(rf: RenderFlags, ctx: any) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
elementStart(0, 'span', ['class', 'existing']);
|
||||
elementStart(0, 'span');
|
||||
elementStyling(
|
||||
null, ['existing', 'active', InitialStylingFlags.VALUES_MODE, 'existing', true]);
|
||||
elementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
elementClassNamed(0, 'active', bind(ctx));
|
||||
elementClassProp(0, 1, ctx);
|
||||
elementStylingApply(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user