refactor(compiler): element references not resolved when selectorless matcher is passed in (#61100)

Fixes that the template binder didn't resolve references to DOM nodes (e.g. `<div #ref></div>` if the matcher being passed in isn't a `SelectorMatcher`.

PR Close #61100
This commit is contained in:
Kristiyan Kostadinov 2025-05-02 13:39:02 +02:00 committed by Andrew Kushnir
parent 7d2a6b3864
commit 2c17145520
2 changed files with 39 additions and 13 deletions

View file

@ -203,17 +203,15 @@ export class R3TargetBinder<DirectiveT extends DirectiveMeta> implements TargetB
// - bindings: Map of inputs, outputs, and attributes to the directive/element that claims
// them. TODO(alxhub): handle multiple directives claiming an input/output/etc.
// - references: Map of #references to their targets.
if (this.directiveMatcher !== null) {
DirectiveBinder.apply(
target.template,
this.directiveMatcher,
directives,
eagerDirectives,
missingDirectives,
bindings,
references,
);
}
DirectiveBinder.apply(
target.template,
this.directiveMatcher,
directives,
eagerDirectives,
missingDirectives,
bindings,
references,
);
// Finally, run the TemplateBinder to bind references, variables, and other entities within the
// template. This extracts all the metadata that doesn't depend on directive matching.
@ -502,7 +500,7 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
private isInDeferBlock = false;
private constructor(
private directiveMatcher: DirectiveMatcher<DirectiveT>,
private directiveMatcher: DirectiveMatcher<DirectiveT> | null,
private directives: MatchedDirectives<DirectiveT>,
private eagerDirectives: DirectiveT[],
private missingDirectives: Set<string>,
@ -524,7 +522,7 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
*/
static apply<DirectiveT extends DirectiveMeta>(
template: Node[],
directiveMatcher: DirectiveMatcher<DirectiveT>,
directiveMatcher: DirectiveMatcher<DirectiveT> | null,
directives: MatchedDirectives<DirectiveT>,
eagerDirectives: DirectiveT[],
missingDirectives: Set<string>,
@ -642,6 +640,12 @@ class DirectiveBinder<DirectiveT extends DirectiveMeta> implements Visitor {
const cssSelector = createCssSelectorFromNode(node);
this.directiveMatcher.match(cssSelector, (_, results) => directives.push(...results));
this.trackSelectorBasedBindingsAndDirectives(node, directives);
} else {
node.references.forEach((ref) => {
if (ref.value.trim() === '') {
this.references.set(ref, node);
}
});
}
node.directives.forEach((directive) => directive.visit(this));

View file

@ -443,6 +443,17 @@ describe('t2 binding', () => {
expect(res.getDefinitionNodeOfSymbol(secondLet)).toBe(secondBranch);
});
it('should resolve an element reference without a directive matcher', () => {
const template = parseTemplate('<div #foo></div>', '');
const binder = new R3TargetBinder(null);
const res = binder.bind({template: template.nodes});
const node = template.nodes[0] as a.Component;
const reference = node.references[0];
const result = res.getReferenceTarget(reference) as a.Element;
expect(result instanceof a.Element).toBe(true);
expect(result.name).toBe('div');
});
describe('matching inputs to consuming directives', () => {
it('should work for bound attributes', () => {
const template = parseTemplate('<div hasInput [inputBinding]="myValue"></div>', '', {});
@ -1170,6 +1181,17 @@ describe('t2 binding', () => {
expect(result.directive.name).toBe('Dir');
});
it('should resolve a reference on an element when using a selectorless matcher', () => {
const template = parseTemplate('<div #foo></div>', '', options);
const binder = new R3TargetBinder(makeSelectorlessMatcher([]));
const res = binder.bind({template: template.nodes});
const node = template.nodes[0] as a.Component;
const reference = node.references[0];
const result = res.getReferenceTarget(reference) as a.Element;
expect(result instanceof a.Element).toBe(true);
expect(result.name).toBe('div');
});
it('should get consumer of component bindings', () => {
const template = parseTemplate(
'<MyComp [input]="value" static="value" (output)="doStuff()" [doesNotExist]="value" [attr.input]="value"/>',