fix(ivy): incorrect ChangeDetectorRef injected into pipes used in component inputs (#31438)

When injecting a `ChangeDetectorRef` into a pipe, the expected result is that the ref will be tied to the component in which the pipe is being used. This works for most cases, however when a pipe is used inside a property binding of a component (see test case as an example), the current `TNode` is pointing to component's host so we end up injecting the inner component's view. These changes fix the issue by only looking up the component view of the `TNode` if the `TNode` is a parent.

This PR resolves FW-1419.

PR Close #31438
This commit is contained in:
crisbeto
2019-07-12 20:15:12 +02:00
committed by Kara Erickson
parent f50dede8f7
commit 0aff4a6919
16 changed files with 246 additions and 30 deletions

View File

@ -65,6 +65,7 @@ export type Provider = any;
export enum R3ResolvedDependencyType {
Token = 0,
Attribute = 1,
ChangeDetectorRef = 2,
}
export interface R3DependencyMetadataFacade {

View File

@ -33,6 +33,7 @@ export {
RenderFlags as ɵRenderFlags,
ɵɵdirectiveInject,
ɵɵinjectAttribute,
ɵɵinjectPipeChangeDetectorRef,
ɵɵgetFactoryOf,
ɵɵgetInheritedFactory,
ɵɵsetComponentScope,

View File

@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectorRef} from '../../change_detection/change_detector_ref';
import {CompilerFacade, R3DependencyMetadataFacade, getCompilerFacade} from '../../compiler/compiler_facade';
import {Type} from '../../interface/type';
import {ReflectionCapabilities} from '../../reflection/reflection_capabilities';
@ -66,6 +67,9 @@ function reflectDependency(compiler: CompilerFacade, dep: any | any[]): R3Depend
}
meta.token = param.attributeName;
meta.resolved = compiler.R3ResolvedDependencyType.Attribute;
} else if (param === ChangeDetectorRef) {
meta.token = param;
meta.resolved = compiler.R3ResolvedDependencyType.ChangeDetectorRef;
} else {
setTokenAndResolvedType(param);
}

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectFlags, InjectionToken} from '../di';
import {InjectionToken} from '../di/injection_token';
import {Injector} from '../di/injector';
import {injectRootLimpMode, setInjectImplementation} from '../di/injector_compatibility';
import {getInjectableDef, getInjectorDef} from '../di/interface/defs';
import {InjectFlags} from '../di/interface/injector';
import {Type} from '../interface/type';
import {assertDefined, assertEqual} from '../util/assert';

View File

@ -198,7 +198,7 @@ export {
ɵɵpureFunctionV,
} from './pure_function';
export {ɵɵtemplateRefExtractor} from './view_engine_compatibility_prebound';
export {ɵɵtemplateRefExtractor, ɵɵinjectPipeChangeDetectorRef} from './view_engine_compatibility_prebound';
export {ɵɵresolveWindow, ɵɵresolveDocument, ɵɵresolveBody} from './util/misc_utils';

View File

@ -42,6 +42,7 @@ export const angularCoreEnv: {[name: string]: Function} =
'ɵɵgetInheritedFactory': r3.ɵɵgetInheritedFactory,
'ɵɵinject': ɵɵinject,
'ɵɵinjectAttribute': r3.ɵɵinjectAttribute,
'ɵɵinjectPipeChangeDetectorRef': r3.ɵɵinjectPipeChangeDetectorRef,
'ɵɵtemplateRefExtractor': r3.ɵɵtemplateRefExtractor,
'ɵɵNgOnChangesFeature': r3.ɵɵNgOnChangesFeature,
'ɵɵProvidersFeature': r3.ɵɵProvidersFeature,

View File

@ -362,8 +362,8 @@ export function createContainerRef(
/** Returns a ChangeDetectorRef (a.k.a. a ViewRef) */
export function injectChangeDetectorRef(): ViewEngine_ChangeDetectorRef {
return createViewRef(getPreviousOrParentTNode(), getLView(), null);
export function injectChangeDetectorRef(isPipe = false): ViewEngine_ChangeDetectorRef {
return createViewRef(getPreviousOrParentTNode(), getLView(), isPipe);
}
/**
@ -371,15 +371,15 @@ export function injectChangeDetectorRef(): ViewEngine_ChangeDetectorRef {
*
* @param hostTNode The node that is requesting a ChangeDetectorRef
* @param hostView The view to which the node belongs
* @param context The context for this change detector ref
* @param isPipe Whether the view is being injected into a pipe.
* @returns The ChangeDetectorRef to use
*/
export function createViewRef(
hostTNode: TNode, hostView: LView, context: any): ViewEngine_ChangeDetectorRef {
if (isComponent(hostTNode)) {
function createViewRef(
hostTNode: TNode, hostView: LView, isPipe: boolean): ViewEngine_ChangeDetectorRef {
if (isComponent(hostTNode) && !isPipe) {
const componentIndex = hostTNode.directiveStart;
const componentView = getComponentViewByIndex(hostTNode.index, hostView);
return new ViewRef(componentView, context, componentIndex);
return new ViewRef(componentView, null, componentIndex);
} else if (
hostTNode.type === TNodeType.Element || hostTNode.type === TNodeType.Container ||
hostTNode.type === TNodeType.ElementContainer) {

View File

@ -7,12 +7,14 @@
*/
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
import {InjectFlags} from '../di/interface/injector';
import {ElementRef as ViewEngine_ElementRef} from '../linker/element_ref';
import {TemplateRef as ViewEngine_TemplateRef} from '../linker/template_ref';
import {TNode} from './interfaces/node';
import {LView} from './interfaces/view';
import {createTemplateRef} from './view_engine_compatibility';
import {createTemplateRef, injectChangeDetectorRef} from './view_engine_compatibility';
@ -25,3 +27,18 @@ import {createTemplateRef} from './view_engine_compatibility';
export function ɵɵtemplateRefExtractor(tNode: TNode, currentView: LView) {
return createTemplateRef(ViewEngine_TemplateRef, ViewEngine_ElementRef, tNode, currentView);
}
/**
* Returns the appropriate `ChangeDetectorRef` for a pipe.
*
* @codeGenApi
*/
export function ɵɵinjectPipeChangeDetectorRef(flags = InjectFlags.Default): ChangeDetectorRef|null {
const value = injectChangeDetectorRef(true);
if (value == null && !(flags & InjectFlags.Optional)) {
throw new Error(`No provider for ChangeDetectorRef!`);
} else {
return value;
}
}