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:
@ -97,6 +97,11 @@ export enum R3ResolvedDependencyType {
|
||||
* The token expression is a string representing the attribute name.
|
||||
*/
|
||||
Attribute = 1,
|
||||
|
||||
/**
|
||||
* Injecting the `ChangeDetectorRef` token. Needs special handling when injected into a pipe.
|
||||
*/
|
||||
ChangeDetectorRef = 2,
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,8 +143,8 @@ export interface R3DependencyMetadata {
|
||||
/**
|
||||
* Construct a factory function expression for the given `R3FactoryMetadata`.
|
||||
*/
|
||||
export function compileFactoryFunction(meta: R3FactoryMetadata):
|
||||
{factory: o.Expression, statements: o.Statement[]} {
|
||||
export function compileFactoryFunction(
|
||||
meta: R3FactoryMetadata, isPipe = false): {factory: o.Expression, statements: o.Statement[]} {
|
||||
const t = o.variable('t');
|
||||
const statements: o.Statement[] = [];
|
||||
|
||||
@ -155,7 +160,8 @@ export function compileFactoryFunction(meta: R3FactoryMetadata):
|
||||
if (meta.deps !== null) {
|
||||
// There is a constructor (either explicitly or implicitly defined).
|
||||
if (meta.deps !== 'invalid') {
|
||||
ctorExpr = new o.InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.injectFn));
|
||||
ctorExpr =
|
||||
new o.InstantiateExpr(typeForCtor, injectDependencies(meta.deps, meta.injectFn, isPipe));
|
||||
}
|
||||
} else {
|
||||
const baseFactory = o.variable(`ɵ${meta.name}_BaseFactory`);
|
||||
@ -203,7 +209,7 @@ export function compileFactoryFunction(meta: R3FactoryMetadata):
|
||||
} else if (isDelegatedMetadata(meta)) {
|
||||
// This type is created with a delegated factory. If a type parameter is not specified, call
|
||||
// the factory instead.
|
||||
const delegateArgs = injectDependencies(meta.delegateDeps, meta.injectFn);
|
||||
const delegateArgs = injectDependencies(meta.delegateDeps, meta.injectFn, isPipe);
|
||||
// Either call `new delegate(...)` or `delegate(...)` depending on meta.useNewForDelegate.
|
||||
const factoryExpr = new (
|
||||
meta.delegateType === R3FactoryDelegateType.Class ?
|
||||
@ -232,30 +238,38 @@ export function compileFactoryFunction(meta: R3FactoryMetadata):
|
||||
}
|
||||
|
||||
function injectDependencies(
|
||||
deps: R3DependencyMetadata[], injectFn: o.ExternalReference): o.Expression[] {
|
||||
return deps.map(dep => compileInjectDependency(dep, injectFn));
|
||||
deps: R3DependencyMetadata[], injectFn: o.ExternalReference, isPipe: boolean): o.Expression[] {
|
||||
return deps.map(dep => compileInjectDependency(dep, injectFn, isPipe));
|
||||
}
|
||||
|
||||
function compileInjectDependency(
|
||||
dep: R3DependencyMetadata, injectFn: o.ExternalReference): o.Expression {
|
||||
dep: R3DependencyMetadata, injectFn: o.ExternalReference, isPipe: boolean): o.Expression {
|
||||
// Interpret the dependency according to its resolved type.
|
||||
switch (dep.resolved) {
|
||||
case R3ResolvedDependencyType.Token: {
|
||||
case R3ResolvedDependencyType.Token:
|
||||
case R3ResolvedDependencyType.ChangeDetectorRef:
|
||||
// Build up the injection flags according to the metadata.
|
||||
const flags = InjectFlags.Default | (dep.self ? InjectFlags.Self : 0) |
|
||||
(dep.skipSelf ? InjectFlags.SkipSelf : 0) | (dep.host ? InjectFlags.Host : 0) |
|
||||
(dep.optional ? InjectFlags.Optional : 0);
|
||||
|
||||
// Build up the arguments to the injectFn call.
|
||||
const injectArgs = [dep.token];
|
||||
// If this dependency is optional or otherwise has non-default flags, then additional
|
||||
// parameters describing how to inject the dependency must be passed to the inject function
|
||||
// that's being used.
|
||||
if (flags !== InjectFlags.Default || dep.optional) {
|
||||
injectArgs.push(o.literal(flags));
|
||||
let flagsParam: o.LiteralExpr|null =
|
||||
(flags !== InjectFlags.Default || dep.optional) ? o.literal(flags) : null;
|
||||
|
||||
// We have a separate instruction for injecting ChangeDetectorRef into a pipe.
|
||||
if (isPipe && dep.resolved === R3ResolvedDependencyType.ChangeDetectorRef) {
|
||||
return o.importExpr(R3.injectPipeChangeDetectorRef).callFn(flagsParam ? [flagsParam] : []);
|
||||
}
|
||||
|
||||
// Build up the arguments to the injectFn call.
|
||||
const injectArgs = [dep.token];
|
||||
if (flagsParam) {
|
||||
injectArgs.push(flagsParam);
|
||||
}
|
||||
return o.importExpr(injectFn).callFn(injectArgs);
|
||||
}
|
||||
case R3ResolvedDependencyType.Attribute:
|
||||
// In the case of attributes, the attribute name in question is given as the token.
|
||||
return o.importExpr(R3.injectAttribute).callFn([dep.token]);
|
||||
|
@ -215,6 +215,9 @@ export class Identifiers {
|
||||
|
||||
static injectAttribute: o.ExternalReference = {name: 'ɵɵinjectAttribute', moduleName: CORE};
|
||||
|
||||
static injectPipeChangeDetectorRef:
|
||||
o.ExternalReference = {name: 'ɵɵinjectPipeChangeDetectorRef', moduleName: CORE};
|
||||
|
||||
static directiveInject: o.ExternalReference = {name: 'ɵɵdirectiveInject', moduleName: CORE};
|
||||
|
||||
static templateRefExtractor:
|
||||
|
@ -57,12 +57,14 @@ export function compilePipeFromMetadata(metadata: R3PipeMetadata) {
|
||||
// e.g. `type: MyPipe`
|
||||
definitionMapValues.push({key: 'type', value: metadata.type, quoted: false});
|
||||
|
||||
const templateFactory = compileFactoryFunction({
|
||||
name: metadata.name,
|
||||
type: metadata.type,
|
||||
deps: metadata.deps,
|
||||
injectFn: R3.directiveInject,
|
||||
});
|
||||
const templateFactory = compileFactoryFunction(
|
||||
{
|
||||
name: metadata.name,
|
||||
type: metadata.type,
|
||||
deps: metadata.deps,
|
||||
injectFn: R3.directiveInject,
|
||||
},
|
||||
true);
|
||||
definitionMapValues.push({key: 'factory', value: templateFactory.factory, quoted: false});
|
||||
|
||||
// e.g. `pure: true`
|
||||
|
Reference in New Issue
Block a user