fix(ivy): pipe returning WrappedValue should invalidate correct binding (#28044)

When a pipe returns an instance of WrappedValue we should "invalidate" value
of a binding where the pipe in question is used.

Before this change we've always wrtten the invalidation value (NO_CHANGE) to
the binding root this invalidating the first binding in a LView. This commit
corrects the binding index calculation so the binding with a pipe is invalidated.

PR Close #28044
This commit is contained in:
Pawel Kozlowski 2019-01-10 16:10:11 +01:00 committed by Andrew Kushnir
parent dffcb9cda3
commit afaea110c7
2 changed files with 26 additions and 23 deletions

View File

@ -11,9 +11,9 @@ import {PipeTransform} from '../change_detection/pipe_transform';
import {load, store} from './instructions';
import {PipeDef, PipeDefList} from './interfaces/definition';
import {HEADER_OFFSET, TVIEW} from './interfaces/view';
import {BINDING_INDEX, HEADER_OFFSET, TVIEW} from './interfaces/view';
import {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunctionV} from './pure_function';
import {getBindingRoot, getLView} from './state';
import {getLView} from './state';
import {NO_CHANGE} from './tokens';
@ -171,7 +171,11 @@ function isPure(index: number): boolean {
function unwrapValue(newValue: any): any {
if (WrappedValue.isWrapped(newValue)) {
newValue = WrappedValue.unwrap(newValue);
getLView()[getBindingRoot()] = NO_CHANGE;
const lView = getLView();
// The NO_CHANGE value needs to be written at the index where the impacted binding value is
// stored
const bindingToInvalidateIdx = lView[BINDING_INDEX];
lView[bindingToInvalidateIdx] = NO_CHANGE;
}
return newValue;
}

View File

@ -537,28 +537,27 @@ const TEST_COMPILER_PROVIDERS: Provider[] = [
expect(renderLog.log).toEqual(['someProp=Megatron']);
}));
fixmeIvy('FW-820: Pipes returning WrappedValue corrupt unrelated bindings ')
.it('should record unwrapped values via ngOnChanges', fakeAsync(() => {
const ctx = createCompFixture(
'<div [testDirective]="\'aName\' | wrappedPipe" [a]="1" [b]="2 | wrappedPipe"></div>');
const dir: TestDirective = queryDirs(ctx.debugElement, TestDirective)[0];
ctx.detectChanges(false);
dir.changes = {};
ctx.detectChanges(false);
it('should record unwrapped values via ngOnChanges', fakeAsync(() => {
const ctx = createCompFixture(
'<div [testDirective]="\'aName\' | wrappedPipe" [a]="1" [b]="2 | wrappedPipe"></div>');
const dir: TestDirective = queryDirs(ctx.debugElement, TestDirective)[0];
ctx.detectChanges(false);
dir.changes = {};
ctx.detectChanges(false);
// Note: the binding for `b` did not change and has no ValueWrapper,
// and should therefore stay unchanged.
expect(dir.changes).toEqual({
'name': new SimpleChange('aName', 'aName', false),
'b': new SimpleChange(2, 2, false)
});
// Note: the binding for `a` did not change and has no ValueWrapper,
// and should therefore stay unchanged.
expect(dir.changes).toEqual({
'name': new SimpleChange('aName', 'aName', false),
'b': new SimpleChange(2, 2, false)
});
ctx.detectChanges(false);
expect(dir.changes).toEqual({
'name': new SimpleChange('aName', 'aName', false),
'b': new SimpleChange(2, 2, false)
});
}));
ctx.detectChanges(false);
expect(dir.changes).toEqual({
'name': new SimpleChange('aName', 'aName', false),
'b': new SimpleChange(2, 2, false)
});
}));
it('should call pure pipes only if the arguments change', fakeAsync(() => {
const ctx = _bindSimpleValue('name | countingPipe', Person);