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
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue