refactor(ivy): simplify bind instruction to reuse bindingUpdated logic (#23881)

Added runtime and compiler testcases for interpolated bindings, which verify
that NO_CHANGE is properly handled in `bind`.

PR Close #23881
This commit is contained in:
JoostK 2018-05-13 21:01:37 +02:00 committed by Victor Berchet
parent 27d811a7ce
commit b99ef2b80a
3 changed files with 43 additions and 19 deletions

View File

@ -1266,7 +1266,7 @@ export function text(index: number, value?: any): void {
/**
* Create text node with binding
* Bindings should be handled externally with the proper bind(1-8) method
* Bindings should be handled externally with the proper interpolation(1-8) method
*
* @param index Index of the node in the data array.
* @param value Stringified value to write.
@ -2151,21 +2151,8 @@ function initBindings() {
*
* @param value Value to diff
*/
export function bind<T>(value: T | NO_CHANGE): T|NO_CHANGE {
if (currentView.bindingStartIndex < 0) {
initBindings();
return data[currentView.bindingIndex++] = value;
}
const changed: boolean =
value !== NO_CHANGE && isDifferent(data[currentView.bindingIndex], value);
if (changed) {
throwErrorIfNoChangesMode(
creationMode, checkNoChangesMode, data[currentView.bindingIndex], value);
data[currentView.bindingIndex] = value;
}
currentView.bindingIndex++;
return changed ? value : NO_CHANGE;
export function bind<T>(value: T): T|NO_CHANGE {
return bindingUpdated(value) ? value : NO_CHANGE;
}
/**

View File

@ -35,6 +35,43 @@ describe('instructions', () => {
elementEnd();
}
describe('bind', () => {
it('should update bindings when value changes', () => {
const t = new TemplateFixture(createAnchor);
t.update(() => elementProperty(0, 'title', bind('Hello')));
expect(t.html).toEqual('<a title="Hello"></a>');
t.update(() => elementProperty(0, 'title', bind('World')));
expect(t.html).toEqual('<a title="World"></a>');
expect(ngDevMode).toHaveProperties({
firstTemplatePass: 1,
tNode: 2, // 1 for hostElement + 1 for the template under test
tView: 1,
rendererCreateElement: 1,
rendererSetProperty: 2
});
});
it('should not update bindings when value does not change', () => {
const idempotentUpdate = () => elementProperty(0, 'title', bind('Hello'));
const t = new TemplateFixture(createAnchor, idempotentUpdate);
t.update();
expect(t.html).toEqual('<a title="Hello"></a>');
t.update();
expect(t.html).toEqual('<a title="Hello"></a>');
expect(ngDevMode).toHaveProperties({
firstTemplatePass: 1,
tNode: 2, // 1 for hostElement + 1 for the template under test
tView: 1,
rendererCreateElement: 1,
rendererSetProperty: 1
});
});
});
describe('elementAttribute', () => {
it('should use sanitizer function', () => {
const t = new TemplateFixture(createDiv);

View File

@ -58,9 +58,9 @@ export class TemplateFixture extends BaseFixture {
/**
*
* @param createBlock Instructions which go into the creation block:
* `if (creationMode) { __here__ }`.
* @param updateBlock Optional instructions which go after the creation block:
* `if (creationMode) { ... } __here__`.
* `if (rf & RenderFlags.Create) { __here__ }`.
* @param updateBlock Optional instructions which go into the update block:
* `if (rf & RenderFlags.Update) { __here__ }`.
*/
constructor(
private createBlock: () => void, private updateBlock: () => void = noop,