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:
parent
27d811a7ce
commit
b99ef2b80a
|
@ -1266,7 +1266,7 @@ export function text(index: number, value?: any): void {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create text node with binding
|
* 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 index Index of the node in the data array.
|
||||||
* @param value Stringified value to write.
|
* @param value Stringified value to write.
|
||||||
|
@ -2151,21 +2151,8 @@ function initBindings() {
|
||||||
*
|
*
|
||||||
* @param value Value to diff
|
* @param value Value to diff
|
||||||
*/
|
*/
|
||||||
export function bind<T>(value: T | NO_CHANGE): T|NO_CHANGE {
|
export function bind<T>(value: T): T|NO_CHANGE {
|
||||||
if (currentView.bindingStartIndex < 0) {
|
return bindingUpdated(value) ? value : NO_CHANGE;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -35,6 +35,43 @@ describe('instructions', () => {
|
||||||
elementEnd();
|
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', () => {
|
describe('elementAttribute', () => {
|
||||||
it('should use sanitizer function', () => {
|
it('should use sanitizer function', () => {
|
||||||
const t = new TemplateFixture(createDiv);
|
const t = new TemplateFixture(createDiv);
|
||||||
|
|
|
@ -58,9 +58,9 @@ export class TemplateFixture extends BaseFixture {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param createBlock Instructions which go into the creation block:
|
* @param createBlock Instructions which go into the creation block:
|
||||||
* `if (creationMode) { __here__ }`.
|
* `if (rf & RenderFlags.Create) { __here__ }`.
|
||||||
* @param updateBlock Optional instructions which go after the creation block:
|
* @param updateBlock Optional instructions which go into the update block:
|
||||||
* `if (creationMode) { ... } __here__`.
|
* `if (rf & RenderFlags.Update) { __here__ }`.
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
private createBlock: () => void, private updateBlock: () => void = noop,
|
private createBlock: () => void, private updateBlock: () => void = noop,
|
||||||
|
|
Loading…
Reference in New Issue