fix(ivy): Class selector directives execute properly on container elements (#29383)
PR Close #29383
This commit is contained in:
parent
68a9fe817c
commit
10734ac607
|
@ -547,6 +547,13 @@ export function elementContainerStart(
|
||||||
const tNode =
|
const tNode =
|
||||||
createNodeAtIndex(index, TNodeType.ElementContainer, native, tagName, attrs || null);
|
createNodeAtIndex(index, TNodeType.ElementContainer, native, tagName, attrs || null);
|
||||||
|
|
||||||
|
|
||||||
|
if (attrs) {
|
||||||
|
// While ng-container doesn't necessarily support styling, we use the style context to identify
|
||||||
|
// and execute directives on the ng-container.
|
||||||
|
setNodeStylingTemplate(tView, tNode, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
appendChild(native, tNode, lView);
|
appendChild(native, tNode, lView);
|
||||||
createDirectivesAndLocals(tView, lView, localRefs);
|
createDirectivesAndLocals(tView, lView, localRefs);
|
||||||
attachPatchData(native, lView);
|
attachPatchData(native, lView);
|
||||||
|
@ -559,6 +566,25 @@ export function elementContainerStart(
|
||||||
executeContentQueries(tView, tNode, lView);
|
executeContentQueries(tView, tNode, lView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appropriately sets `stylingTemplate` on a TNode
|
||||||
|
*
|
||||||
|
* Does not apply styles to DOM nodes
|
||||||
|
*
|
||||||
|
* @param tNode The node whose `stylingTemplate` to set
|
||||||
|
* @param attrs The attribute array source to set the attributes from
|
||||||
|
* @param attrsStartIndex Optional start index to start processing the `attrs` from
|
||||||
|
*/
|
||||||
|
function setNodeStylingTemplate(
|
||||||
|
tView: TView, tNode: TNode, attrs: TAttributes, attrsStartIndex: number) {
|
||||||
|
if (tView.firstTemplatePass && !tNode.stylingTemplate) {
|
||||||
|
const stylingAttrsStartIndex = attrsStylingIndexOf(attrs, attrsStartIndex);
|
||||||
|
if (stylingAttrsStartIndex >= 0) {
|
||||||
|
tNode.stylingTemplate = initializeStaticStylingContext(attrs, stylingAttrsStartIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function executeContentQueries(tView: TView, tNode: TNode, lView: LView) {
|
function executeContentQueries(tView: TView, tNode: TNode, lView: LView) {
|
||||||
if (isContentQueryHost(tNode)) {
|
if (isContentQueryHost(tNode)) {
|
||||||
const start = tNode.directiveStart;
|
const start = tNode.directiveStart;
|
||||||
|
@ -636,12 +662,7 @@ export function elementStart(
|
||||||
// value is evaluated). When the template is allocated (when it turns into a context)
|
// value is evaluated). When the template is allocated (when it turns into a context)
|
||||||
// then the styling template is locked and cannot be further extended (it can only be
|
// then the styling template is locked and cannot be further extended (it can only be
|
||||||
// instantiated into a context per element)
|
// instantiated into a context per element)
|
||||||
if (tView.firstTemplatePass && !tNode.stylingTemplate) {
|
setNodeStylingTemplate(tView, tNode, attrs, lastAttrIndex);
|
||||||
const stylingAttrsStartIndex = attrsStylingIndexOf(attrs, lastAttrIndex);
|
|
||||||
if (stylingAttrsStartIndex >= 0) {
|
|
||||||
tNode.stylingTemplate = initializeStaticStylingContext(attrs, stylingAttrsStartIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tNode.stylingTemplate) {
|
if (tNode.stylingTemplate) {
|
||||||
// the initial style/class values are rendered immediately after having been
|
// the initial style/class values are rendered immediately after having been
|
||||||
|
|
|
@ -166,5 +166,63 @@ describe('projection', () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(fixture.nativeElement).toHaveText('inline()ng-template(onetwothree)');
|
expect(fixture.nativeElement).toHaveText('inline()ng-template(onetwothree)');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('on containers', () => {
|
||||||
|
it('should work when matching attributes', () => {
|
||||||
|
let xDirectives = 0;
|
||||||
|
@Component({selector: 'selector-proj', template: '<ng-content select="[x]"></ng-content>'})
|
||||||
|
class SelectedNgContentComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: '[x]'})
|
||||||
|
class XDirective {
|
||||||
|
constructor() { xDirectives++; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'main-selector',
|
||||||
|
template:
|
||||||
|
'<selector-proj><ng-container x="true">Hello world!</ng-container></selector-proj>'
|
||||||
|
})
|
||||||
|
class SelectorMainComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{declarations: [XDirective, SelectedNgContentComp, SelectorMainComp]});
|
||||||
|
const fixture = TestBed.createComponent<SelectorMainComp>(SelectorMainComp);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('Hello world!');
|
||||||
|
expect(xDirectives).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work when matching classes', () => {
|
||||||
|
let xDirectives = 0;
|
||||||
|
@Component({selector: 'selector-proj', template: '<ng-content select=".x"></ng-content>'})
|
||||||
|
class SelectedNgContentComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: '.x'})
|
||||||
|
class XDirective {
|
||||||
|
constructor() { xDirectives++; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'main-selector',
|
||||||
|
template:
|
||||||
|
'<selector-proj><ng-container class="x">Hello world!</ng-container></selector-proj>'
|
||||||
|
})
|
||||||
|
class SelectorMainComp {
|
||||||
|
}
|
||||||
|
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{declarations: [XDirective, SelectedNgContentComp, SelectorMainComp]});
|
||||||
|
const fixture = TestBed.createComponent<SelectorMainComp>(SelectorMainComp);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement).toHaveText('Hello world!');
|
||||||
|
expect(xDirectives).toEqual(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -659,6 +659,9 @@
|
||||||
{
|
{
|
||||||
"name": "setIsParent"
|
"name": "setIsParent"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setNodeStylingTemplate"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setPreviousOrParentTNode"
|
"name": "setPreviousOrParentTNode"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1271,6 +1271,9 @@
|
||||||
{
|
{
|
||||||
"name": "setIsParent"
|
"name": "setIsParent"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setNodeStylingTemplate"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setPlayerBuilder"
|
"name": "setPlayerBuilder"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue