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 {load, store} from './instructions';
import {PipeDef, PipeDefList} from './interfaces/definition'; 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 {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunctionV} from './pure_function';
import {getBindingRoot, getLView} from './state'; import {getLView} from './state';
import {NO_CHANGE} from './tokens'; import {NO_CHANGE} from './tokens';
@ -171,7 +171,11 @@ function isPure(index: number): boolean {
function unwrapValue(newValue: any): any { function unwrapValue(newValue: any): any {
if (WrappedValue.isWrapped(newValue)) { if (WrappedValue.isWrapped(newValue)) {
newValue = WrappedValue.unwrap(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; return newValue;
} }

View File

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