Skip to content

Commit

Permalink
fix(target-observer): fix dom binding update when initial value match…
Browse files Browse the repository at this point in the history
…es empty string

fixes #423
  • Loading branch information
fkleuver committed Feb 8, 2019
1 parent bfff190 commit 38fdc71
Show file tree
Hide file tree
Showing 2 changed files with 181 additions and 0 deletions.
180 changes: 180 additions & 0 deletions packages/jit-html/test/integration/html-attributes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { InjectArray } from '@aurelia/kernel';
import { Aurelia, CustomElementResource as CE, ICustomElement, INode, LifecycleFlags as LF } from '@aurelia/runtime';
import { expect } from 'chai';
import { TestContext } from '../util';
import { eachCartesianJoin } from './util';

interface IThing {
enabled?: boolean;
}
type VM<T> = T & ICustomElement<Node> & {
el: Element;
parent?: VM<T>;
};
interface Spec {
t: string;
}

describe('html attribute', function() {
function setup() {
const ctx = TestContext.createHTMLTestContext();

const host = ctx.createElement('div');
const au = new Aurelia(ctx.container);
return { ctx, host, au };
}

describe('class', function () {
describe('on a surrogate', function () {
interface ThingSpec extends Spec {
createThing(): IThing;
}
interface ExpressionSpec extends Spec {
expression: string;
getExpectedClassNames(thing: IThing): string[];
}

const thingSpecs: ThingSpec[] = [
{ t: '1', createThing() { return { enabled: false }; } },
{ t: '2', createThing() { return { enabled: true }; } },
{ t: '3', createThing() { return { }; } }
];
const expressionSpecs: ExpressionSpec[] = [
{
t: '1',
expression: '${thing.enabled?\'class-1\':\'\'}',
getExpectedClassNames(thing: IThing) {
return thing && thing.enabled ? ['au', 'class-1'] : ['au'];
}
},
{
t: '2',
expression: '${thing.enabled?\'class-1\':\'class-2\'}',
getExpectedClassNames(thing: IThing) {
return thing && thing.enabled ? ['au', 'class-1'] : ['au', 'class-2'];
}
},
{
t: '3',
expression: '${thing.enabled?\'class-1\':\'\'} class-3',
getExpectedClassNames(thing: IThing) {
return thing && thing.enabled ? ['au', 'class-3', 'class-1'] : ['au', 'class-3'];
}
},
{
t: '4',
expression: '${thing.enabled?\'class-1\':\'class-2\'} class-3',
getExpectedClassNames(thing: IThing) {
return thing && thing.enabled ? ['au', 'class-3', 'class-1'] : ['au', 'class-2', 'class-3'];
}
},
{
t: '5',
expression: 'class-0 ${thing.enabled?\'class-1\':\'\'}',
getExpectedClassNames(thing: IThing) {
return thing && thing.enabled ? ['au', 'class-0', 'class-1'] : ['au', 'class-0'];
}
},
{
t: '6',
expression: 'class-0 ${thing.enabled?\'class-1\':\'class-2\'}',
getExpectedClassNames(thing: IThing) {
return thing && thing.enabled ? ['au', 'class-0', 'class-1'] : ['au', 'class-0', 'class-2'];
}
},
{
t: '7',
expression: 'class-0 ${thing.enabled?\'class-1\':\'\'} class-3',
getExpectedClassNames(thing: IThing) {
return thing && thing.enabled ? ['au', 'class-0', 'class-3', 'class-1'] : ['au', 'class-0', 'class-3'];
}
},
{
t: '8',
expression: 'class-0 ${thing.enabled?\'class-1\':\'class-2\'} class-3',
getExpectedClassNames(thing: IThing) {
return thing && thing.enabled ? ['au', 'class-0', 'class-3', 'class-1'] : ['au', 'class-0', 'class-2', 'class-3'];
}
},
];

eachCartesianJoin(
[thingSpecs, thingSpecs, thingSpecs, thingSpecs, expressionSpecs],
function (thingSpec1, thingSpec2, thingSpec3, thingSpec4, expressionSpec) {
it(`${thingSpec1.t} ${thingSpec2.t} ${thingSpec3.t} ${thingSpec4.t} ${expressionSpec.t}`, function() {
const { createThing: createThing1 } = thingSpec1;
const { createThing: createThing2 } = thingSpec2;
const { createThing: createThing3 } = thingSpec3;
const { createThing: createThing4 } = thingSpec4;
const { expression, getExpectedClassNames } = expressionSpec;

const thing1 = createThing1();
const thing2 = createThing2();
const thing3 = createThing3();
const thing4 = createThing4();

const { host, au } = setup();
// verify that the assertions inside change handler / lifecycles were actually performed
let assertionCount = 0;

const component = CE.define(
{
name: 'app',
template: `<foo parent.bind="$this"></foo>`,
dependencies: [
CE.define(
{
name: 'foo',
template: `<template class="${expression}"></template>`,
bindables: ['thing', 'parent']
},
class {
public static readonly inject: InjectArray = [INode];
public thing: IThing;
constructor(public el: Element) {
this.thing = thing1;
}

public attached(): void {
this.thing = thing2;
this.thing = thing3;
this.thing = thing4;
}

public thingChanged(this: VM<this>, newValue: IThing, oldValue: IThing): void {
this.verifyClassName(oldValue);
this.$lifecycle.processFlushQueue(LF.none);
this.verifyClassName(newValue);
}

private verifyClassName(this: VM<this>, thing: IThing): void {
const actualClassNames: string[] = [];
this.el.classList.forEach(c => { actualClassNames.push(c); });
const expectedClassNames = getExpectedClassNames(thing);
expect(actualClassNames.length, `actualClassNames.length #${++assertionCount}`).to.equal(expectedClassNames.length);
for (const expectedClassName of expectedClassNames) {
expect(actualClassNames, `actualClassNames #${++assertionCount}`).to.include(expectedClassName);
}
}
}
)
]
},
class {
public static readonly inject: InjectArray = [INode];
constructor(public el: Element) {}
}
);

au.app({ host, component });
au.start();

expect(assertionCount, 'assertionCount').to.be.gte(12);

au.stop();
});
}
);
});
});
});
1 change: 1 addition & 0 deletions packages/runtime/src/observation/target-observer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ function setValue(this: BindingTargetAccessor, newValue: unknown, flags: Lifecyc
const currentValue = this.currentValue;
newValue = newValue === null || newValue === undefined ? this.defaultValue : newValue;
if (currentValue !== newValue) {
this.oldValue = this.currentValue;
this.currentValue = newValue;
if ((flags & (LifecycleFlags.fromFlush | LifecycleFlags.fromBind)) &&
!(this.isDOMObserver && (flags & LifecycleFlags.doNotUpdateDOM))) {
Expand Down

0 comments on commit 38fdc71

Please sign in to comment.