diff --git a/packages/core/src/render3/node_selector_matcher.ts b/packages/core/src/render3/node_selector_matcher.ts index 107e2b1917..b291587bc2 100644 --- a/packages/core/src/render3/node_selector_matcher.ts +++ b/packages/core/src/render3/node_selector_matcher.ts @@ -205,6 +205,14 @@ function findAttrIndexInNode( return i; } else if (maybeAttrName === AttributeMarker.Bindings) { bindingsMode = true; + } else if (maybeAttrName === AttributeMarker.Classes) { + let value = attrs[++i]; + // We should skip classes here because we have a separate mechanism for + // matching classes in projection mode. + while (typeof value === 'string') { + value = attrs[++i]; + } + continue; } else if (maybeAttrName === AttributeMarker.Template) { // We do not care about Template attributes in this scenario. break; diff --git a/packages/core/test/acceptance/directive_spec.ts b/packages/core/test/acceptance/directive_spec.ts index 92aa4230b2..6764573e7b 100644 --- a/packages/core/test/acceptance/directive_spec.ts +++ b/packages/core/test/acceptance/directive_spec.ts @@ -58,6 +58,30 @@ describe('directives', () => { expect(nodesWithDirective.length).toBe(1); }); + it('should match a mix of bound directives and classes', () => { + TestBed.configureTestingModule({declarations: [TestComponent, TitleDirective]}); + TestBed.overrideTemplate(TestComponent, ` +
+ `); + + const fixture = TestBed.createComponent(TestComponent); + const nodesWithDirective = fixture.debugElement.queryAllNodes(By.directive(TitleDirective)); + + expect(nodesWithDirective.length).toBe(1); + }); + + it('should NOT match classes to directive selectors', () => { + TestBed.configureTestingModule({declarations: [TestComponent, TitleDirective]}); + TestBed.overrideTemplate(TestComponent, ` +
+ `); + + const fixture = TestBed.createComponent(TestComponent); + const nodesWithDirective = fixture.debugElement.queryAllNodes(By.directive(TitleDirective)); + + expect(nodesWithDirective.length).toBe(0); + }); + }); -}); \ No newline at end of file +}); diff --git a/packages/core/test/render3/node_selector_matcher_spec.ts b/packages/core/test/render3/node_selector_matcher_spec.ts index 294f43df17..c186e5069d 100644 --- a/packages/core/test/render3/node_selector_matcher_spec.ts +++ b/packages/core/test/render3/node_selector_matcher_spec.ts @@ -201,6 +201,30 @@ describe('css selector matching', () => { .toBeFalsy( `Selector '[directive=value]' should not match `); }); + + it('should match bound attributes that come after classes', () => { + expect(isMatching( + 'span', + [ + AttributeMarker.Classes, 'my-class', 'other-class', AttributeMarker.Bindings, + 'title', 'directive' + ], + ['', 'directive', ''])) + .toBeTruthy( + `Selector '[directive]' should match `); + }); + + it('should match NOT match classes when looking for directives', () => { + expect(isMatching( + 'span', + [ + AttributeMarker.Classes, 'directive', 'other-class', AttributeMarker.Bindings, + 'title' + ], + ['', 'directive', ''])) + .toBeFalsy( + `Selector '[directive]' should NOT match `); + }); }); describe('class matching', () => {