feat(ivy): support WrappedValue in pipes (FW-726) (#27409)
PR Close #27409
This commit is contained in:
@ -6,13 +6,16 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {WrappedValue} from '../change_detection/change_detection_util';
|
||||||
import {PipeTransform} from '../change_detection/pipe_transform';
|
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 {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 {getLView} from './state';
|
import {getBindingRoot, getLView} from './state';
|
||||||
|
import {NO_CHANGE} from './tokens';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a pipe.
|
* Create a pipe.
|
||||||
@ -74,8 +77,9 @@ function getPipeDef(name: string, registry: PipeDefList | null): PipeDef<any> {
|
|||||||
*/
|
*/
|
||||||
export function pipeBind1(index: number, slotOffset: number, v1: any): any {
|
export function pipeBind1(index: number, slotOffset: number, v1: any): any {
|
||||||
const pipeInstance = load<PipeTransform>(index);
|
const pipeInstance = load<PipeTransform>(index);
|
||||||
return isPure(index) ? pureFunction1(slotOffset, pipeInstance.transform, v1, pipeInstance) :
|
return unwrapValue(
|
||||||
pipeInstance.transform(v1);
|
isPure(index) ? pureFunction1(slotOffset, pipeInstance.transform, v1, pipeInstance) :
|
||||||
|
pipeInstance.transform(v1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,8 +95,9 @@ export function pipeBind1(index: number, slotOffset: number, v1: any): any {
|
|||||||
*/
|
*/
|
||||||
export function pipeBind2(index: number, slotOffset: number, v1: any, v2: any): any {
|
export function pipeBind2(index: number, slotOffset: number, v1: any, v2: any): any {
|
||||||
const pipeInstance = load<PipeTransform>(index);
|
const pipeInstance = load<PipeTransform>(index);
|
||||||
return isPure(index) ? pureFunction2(slotOffset, pipeInstance.transform, v1, v2, pipeInstance) :
|
return unwrapValue(
|
||||||
pipeInstance.transform(v1, v2);
|
isPure(index) ? pureFunction2(slotOffset, pipeInstance.transform, v1, v2, pipeInstance) :
|
||||||
|
pipeInstance.transform(v1, v2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -109,9 +114,9 @@ export function pipeBind2(index: number, slotOffset: number, v1: any, v2: any):
|
|||||||
*/
|
*/
|
||||||
export function pipeBind3(index: number, slotOffset: number, v1: any, v2: any, v3: any): any {
|
export function pipeBind3(index: number, slotOffset: number, v1: any, v2: any, v3: any): any {
|
||||||
const pipeInstance = load<PipeTransform>(index);
|
const pipeInstance = load<PipeTransform>(index);
|
||||||
return isPure(index) ?
|
return unwrapValue(
|
||||||
pureFunction3(slotOffset, pipeInstance.transform, v1, v2, v3, pipeInstance) :
|
isPure(index) ? pureFunction3(slotOffset, pipeInstance.transform, v1, v2, v3, pipeInstance) :
|
||||||
pipeInstance.transform(v1, v2, v3);
|
pipeInstance.transform(v1, v2, v3));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,9 +135,10 @@ export function pipeBind3(index: number, slotOffset: number, v1: any, v2: any, v
|
|||||||
export function pipeBind4(
|
export function pipeBind4(
|
||||||
index: number, slotOffset: number, v1: any, v2: any, v3: any, v4: any): any {
|
index: number, slotOffset: number, v1: any, v2: any, v3: any, v4: any): any {
|
||||||
const pipeInstance = load<PipeTransform>(index);
|
const pipeInstance = load<PipeTransform>(index);
|
||||||
return isPure(index) ?
|
return unwrapValue(
|
||||||
pureFunction4(slotOffset, pipeInstance.transform, v1, v2, v3, v4, pipeInstance) :
|
isPure(index) ?
|
||||||
pipeInstance.transform(v1, v2, v3, v4);
|
pureFunction4(slotOffset, pipeInstance.transform, v1, v2, v3, v4, pipeInstance) :
|
||||||
|
pipeInstance.transform(v1, v2, v3, v4));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -147,10 +153,26 @@ export function pipeBind4(
|
|||||||
*/
|
*/
|
||||||
export function pipeBindV(index: number, slotOffset: number, values: any[]): any {
|
export function pipeBindV(index: number, slotOffset: number, values: any[]): any {
|
||||||
const pipeInstance = load<PipeTransform>(index);
|
const pipeInstance = load<PipeTransform>(index);
|
||||||
return isPure(index) ? pureFunctionV(slotOffset, pipeInstance.transform, values, pipeInstance) :
|
return unwrapValue(
|
||||||
pipeInstance.transform.apply(pipeInstance, values);
|
isPure(index) ? pureFunctionV(slotOffset, pipeInstance.transform, values, pipeInstance) :
|
||||||
|
pipeInstance.transform.apply(pipeInstance, values));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isPure(index: number): boolean {
|
function isPure(index: number): boolean {
|
||||||
return (<PipeDef<any>>getLView()[TVIEW].data[index + HEADER_OFFSET]).pure;
|
return (<PipeDef<any>>getLView()[TVIEW].data[index + HEADER_OFFSET]).pure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unwrap the output of a pipe transformation.
|
||||||
|
* In order to trick change detection into considering that the new value is always different from
|
||||||
|
* the old one, the old value is overwritten by NO_CHANGE.
|
||||||
|
*
|
||||||
|
* @param newValue the pipe transformation output.
|
||||||
|
*/
|
||||||
|
function unwrapValue(newValue: any): any {
|
||||||
|
if (WrappedValue.isWrapped(newValue)) {
|
||||||
|
newValue = WrappedValue.unwrap(newValue);
|
||||||
|
getLView()[getBindingRoot()] = NO_CHANGE;
|
||||||
|
}
|
||||||
|
return newValue;
|
||||||
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive as _Directive, InjectionToken, OnChanges, OnDestroy, Pipe as _Pipe, PipeTransform, createInjector, defineInjectable, defineInjector, ɵNgModuleDef as NgModuleDef, ɵdefineComponent as defineComponent, ɵdirectiveInject as directiveInject} from '@angular/core';
|
import {Directive as _Directive, InjectionToken, OnChanges, OnDestroy, Pipe as _Pipe, PipeTransform, WrappedValue, createInjector, defineInjectable, defineInjector, ɵNgModuleDef as NgModuleDef, ɵdefineComponent as defineComponent, ɵdirectiveInject as directiveInject} from '@angular/core';
|
||||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
|
||||||
import {defineDirective, definePipe} from '../../src/render3/definition';
|
import {defineDirective, definePipe} from '../../src/render3/definition';
|
||||||
@ -15,7 +15,7 @@ import {RenderFlags} from '../../src/render3/interfaces/definition';
|
|||||||
import {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render3/pipe';
|
import {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render3/pipe';
|
||||||
|
|
||||||
import {RenderLog, getRendererFactory2, patchLoggingRenderer2} from './imported_renderer2';
|
import {RenderLog, getRendererFactory2, patchLoggingRenderer2} from './imported_renderer2';
|
||||||
import {ComponentFixture, createComponent, getDirectiveOnNode, renderToHtml} from './render_util';
|
import {ComponentFixture, TemplateFixture, createComponent, getDirectiveOnNode, renderToHtml} from './render_util';
|
||||||
|
|
||||||
const Directive: typeof _Directive = function(...args: any[]): any {
|
const Directive: typeof _Directive = function(...args: any[]): any {
|
||||||
// In test we use @Directive for documentation only so it's safe to mock out the implementation.
|
// In test we use @Directive for documentation only so it's safe to mock out the implementation.
|
||||||
@ -416,6 +416,45 @@ describe('pipe', () => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('WrappedValue', () => {
|
||||||
|
@Pipe({name: 'wrappingPipe'})
|
||||||
|
class WrappingPipe implements PipeTransform {
|
||||||
|
transform(value: any) { return new WrappedValue('Bar'); }
|
||||||
|
|
||||||
|
static ngPipeDef = definePipe({
|
||||||
|
name: 'wrappingPipe',
|
||||||
|
type: WrappingPipe,
|
||||||
|
factory: function WrappingPipe_Factory() { return new WrappingPipe(); },
|
||||||
|
pure: false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createTemplate() {
|
||||||
|
text(0);
|
||||||
|
pipe(1, 'wrappingPipe');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTemplate() { textBinding(0, interpolation1('', pipeBind1(1, 1, null), '')); }
|
||||||
|
|
||||||
|
it('should unwrap', () => {
|
||||||
|
const fixture =
|
||||||
|
new TemplateFixture(createTemplate, updateTemplate, 2, 3, undefined, [WrappingPipe]);
|
||||||
|
expect(fixture.html).toEqual('Bar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should force change detection', () => {
|
||||||
|
const fixture =
|
||||||
|
new TemplateFixture(createTemplate, updateTemplate, 2, 3, undefined, [WrappingPipe]);
|
||||||
|
expect(fixture.html).toEqual('Bar');
|
||||||
|
|
||||||
|
fixture.hostElement.childNodes[0] !.textContent = 'Foo';
|
||||||
|
expect(fixture.html).toEqual('Foo');
|
||||||
|
|
||||||
|
fixture.update();
|
||||||
|
expect(fixture.html).toEqual('Bar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@Pipe({name: 'countingPipe'})
|
@Pipe({name: 'countingPipe'})
|
||||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user