From 2c1714552065acee5c50cd37bfc6f29a6701b8ea Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Fri, 2 May 2025 13:39:02 +0200 Subject: [PATCH] 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. `
` if the matcher being passed in isn't a `SelectorMatcher`. PR Close #61100 --- .../compiler/src/render3/view/t2_binder.ts | 30 +++++++++++-------- .../test/render3/view/binding_spec.ts | 22 ++++++++++++++ 2 files changed, 39 insertions(+), 13 deletions(-) diff --git a/packages/compiler/src/render3/view/t2_binder.ts b/packages/compiler/src/render3/view/t2_binder.ts index 51d11b1ba1e..f33c70b8a12 100644 --- a/packages/compiler/src/render3/view/t2_binder.ts +++ b/packages/compiler/src/render3/view/t2_binder.ts @@ -203,17 +203,15 @@ export class R3TargetBinder 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 implements Visitor { private isInDeferBlock = false; private constructor( - private directiveMatcher: DirectiveMatcher, + private directiveMatcher: DirectiveMatcher | null, private directives: MatchedDirectives, private eagerDirectives: DirectiveT[], private missingDirectives: Set, @@ -524,7 +522,7 @@ class DirectiveBinder implements Visitor { */ static apply( template: Node[], - directiveMatcher: DirectiveMatcher, + directiveMatcher: DirectiveMatcher | null, directives: MatchedDirectives, eagerDirectives: DirectiveT[], missingDirectives: Set, @@ -642,6 +640,12 @@ class DirectiveBinder 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)); diff --git a/packages/compiler/test/render3/view/binding_spec.ts b/packages/compiler/test/render3/view/binding_spec.ts index 2f998857388..88fe7586ab9 100644 --- a/packages/compiler/test/render3/view/binding_spec.ts +++ b/packages/compiler/test/render3/view/binding_spec.ts @@ -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('
', ''); + 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('
', '', {}); @@ -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('
', '', 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( '',