Skip to content

Commit d9a1a7d

Browse files
fix(core): properly execute content queries for root components (angular#54457)
Prior to this fix an incorrect view instance (a dynamically created component one instead of the root view) was passed to the content query function. Having incorrect view instance meant that a component instance could not be found. This is a pre-existing bug, introduction of signal-based queries just surfaced it. Fixes angular#54450 PR Close angular#54457
1 parent 62be680 commit d9a1a7d

File tree

3 files changed

+43
-6
lines changed

3 files changed

+43
-6
lines changed

packages/core/src/render3/component_ref.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ import {CONTEXT, HEADER_OFFSET, INJECTOR, LView, LViewEnvironment, LViewFlags, T
4646
import {MATH_ML_NAMESPACE, SVG_NAMESPACE} from './namespaces';
4747
import {createElementNode, setupStaticAttributes, writeDirectClass} from './node_manipulation';
4848
import {extractAttrsAndClassesFromSelector, stringifyCSSSelectorList} from './node_selector_matcher';
49-
import {EffectScheduler} from './reactivity/effect';
5049
import {enterView, getCurrentTNode, getLView, leaveView} from './state';
5150
import {computeStaticStyling} from './styling/static_styling';
5251
import {mergeHostAttrs, setUpAttributes} from './util/attrs_utils';
@@ -504,7 +503,7 @@ function createRootComponent<T>(
504503

505504
// We want to generate an empty QueryList for root content queries for backwards
506505
// compatibility with ViewEngine.
507-
executeContentQueries(tView, rootTNode, componentView);
506+
executeContentQueries(tView, rootTNode, rootLView);
508507

509508
return component;
510509
}

packages/core/src/render3/instructions/shared.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ import {attachPatchData} from '../context_discovery';
3030
import {getFactoryDef} from '../definition_factory';
3131
import {diPublicInInjector, getNodeInjectable, getOrCreateNodeInjectorForNode} from '../di';
3232
import {throwMultipleComponentError} from '../errors';
33+
import {AttributeMarker} from '../interfaces/attribute_marker';
3334
import {CONTAINER_HEADER_OFFSET, LContainer} from '../interfaces/container';
3435
import {ComponentDef, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, HostBindingsFunction, HostDirectiveBindingMap, HostDirectiveDefs, PipeDefListOrFactory, RenderFlags, ViewQueriesFunction} from '../interfaces/definition';
3536
import {NodeInjectorFactory} from '../interfaces/injector';
37+
import {InputFlags} from '../interfaces/input_flags';
3638
import {getUniqueLViewId} from '../interfaces/lview_tracking';
3739
import {InitialInputData, InitialInputs, LocalRefExtractor, NodeInputBindings, NodeOutputBindings, TAttributes, TConstantsOrFactory, TContainerNode, TDirectiveHostNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjectionNode} from '../interfaces/node';
3840
import {Renderer} from '../interfaces/renderer';
@@ -56,8 +58,6 @@ import {selectIndexInternal} from './advance';
5658
import {ɵɵdirectiveInject} from './di';
5759
import {handleUnknownPropertyError, isPropertyValid, matchingSchemas} from './element_validation';
5860
import {writeToDirectiveInput} from './write_to_directive_input';
59-
import { AttributeMarker } from '../interfaces/attribute_marker';
60-
import { InputFlags } from '../interfaces/input_flags';
6161

6262
/**
6363
* Invoke `HostBindingsFunction`s for view.
@@ -287,7 +287,11 @@ export function executeContentQueries(tView: TView, tNode: TNode, lView: LView)
287287
for (let directiveIndex = start; directiveIndex < end; directiveIndex++) {
288288
const def = tView.data[directiveIndex] as DirectiveDef<any>;
289289
if (def.contentQueries) {
290-
def.contentQueries(RenderFlags.Create, lView[directiveIndex], directiveIndex);
290+
const directiveInstance = lView[directiveIndex];
291+
ngDevMode &&
292+
assertDefined(
293+
directiveIndex, 'Incorrect reference to a directive defining a content query');
294+
def.contentQueries(RenderFlags.Create, directiveInstance, directiveIndex);
291295
}
292296
}
293297
} finally {

packages/core/test/acceptance/authoring/signal_queries_spec.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {Component, computed, contentChild, contentChildren, Directive, ElementRef, viewChild, viewChildren} from '@angular/core';
9+
import {Component, computed, contentChild, contentChildren, createComponent, Directive, ElementRef, EnvironmentInjector, viewChild, viewChildren} from '@angular/core';
1010
import {TestBed} from '@angular/core/testing';
1111
import {By} from '@angular/platform-browser';
1212

@@ -441,4 +441,38 @@ describe('queries as signals', () => {
441441
expect(recomputeCount).toBe(1);
442442
});
443443
});
444+
445+
describe('dynamic component creation', () => {
446+
it('should return empty results for content queries of dynamically created components', () => {
447+
// https://github.com/angular/angular/issues/54450
448+
@Component({
449+
selector: 'query-cmp',
450+
standalone: true,
451+
template: ``,
452+
})
453+
class QueryComponent {
454+
elements = contentChildren('el');
455+
element = contentChild('el');
456+
}
457+
458+
@Component({
459+
standalone: true,
460+
template: ``,
461+
})
462+
class TestComponent {
463+
constructor(private _envInjector: EnvironmentInjector) {}
464+
465+
createDynamic() {
466+
return createComponent(QueryComponent, {environmentInjector: this._envInjector});
467+
}
468+
}
469+
470+
const fixture = TestBed.createComponent(TestComponent);
471+
const cmpRef = fixture.componentInstance.createDynamic();
472+
cmpRef.changeDetectorRef.detectChanges();
473+
474+
expect(cmpRef.instance.elements()).toEqual([]);
475+
expect(cmpRef.instance.element()).toBeUndefined();
476+
});
477+
});
444478
});

0 commit comments

Comments
 (0)