@ContentChild doesn't work in @angular/elements with slots
See original GitHub issueWhich @angular/* package(s) are the source of the bug?
core, elements
Is this a regression?
No
Description
I apologize in advance for the longer post. I’m trying to convert a reusable angular component into a web component using @angular/elements
. Everything works fine up to the point where I try to expose slots through the parent component, which have to be accessed through it. Everything works great in the angular application context, but the elements build cannot recognize the child slots as @ContentChildren()
. I have created a small example to explain the issue I’m facing.
I have a parent component looking like this:
wrapper.component.html:
<h1>Wrapper's own content</h1>
<button (click)="changeChildText()">Change child text</button>
<ng-content select="app-child-standalone"></ng-content>
wrapper.component.ts:
import { Component, ContentChild } from '@angular/core';
import { ChildStandaloneComponent } from './child-standalone/child-standalone.component';
@Component({
selector: 'app-wrapper',
templateUrl: './wrapper.component.html',
styleUrls: ['./wrapper.component.scss']
})
export class WrapperComponent {
// ContentChild evaluated properly in the angular application
// but is undefined in plain html
@ContentChild(ChildStandaloneComponent)
public child: ChildStandaloneComponent;
constructor() { }
public changeChildText() {
if (this.child) {
this.child.text = "Parent modified text.";
}
}
}
The child component looks like this:
child-standalone.component.html:
<p>child-standalone's own static content</p>
<span>{{text}}</span>
<ng-content></ng-content>
child-standalone.component.ts:
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'app-child-standalone',
templateUrl: './child-standalone.component.html',
styleUrls: ['./child-standalone.component.scss']
})
export class ChildStandaloneComponent {
@Input()
public text = "Default input text.";
constructor() { }
}
I build both components as elements and use them in a plain html:
ngDoBootstrap() {
const wrapper = createCustomElement(WrapperComponent, { injector: this.injector });
customElements.define("app-wrapper", wrapper);
const child = createCustomElement(ChildStandaloneComponent, { injector: this.injector });
customElements.define("app-child-standalone", child);
}
<body>
<app-wrapper>
<app-child-standalone id="childComp">
<button onclick="changeText()">Change from child</button>
</app-child-standalone>
</app-wrapper>
<script>
let changeText = () => {
childComp.text = 'Changed directly from child through ng-content.';
}
</script>
</body>
The line with the ContentChild is the one that isn’t working in the app-wrapper
element and I cannot get the parent component to manipulate the child component because the ContentChild is undefined
. How can I make my components evaluate user-provided child slot content in elements as well?
Please provide a link to a minimal reproduction of the bug
No response
Please provide the exception or error you saw
No response
Please provide the environment you discovered this bug in (run ng version
)
Angular CLI: 13.2.5
Node: 16.11.0
Package Manager: npm 8.1.3
OS: win32 x64
Angular: 13.2.5
... animations, cli, common, compiler, compiler-cli, core
... elements, forms, language-service, platform-browser
... platform-browser-dynamic, router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1302.5
@angular-devkit/build-angular 13.2.5
@angular-devkit/core 13.2.5
@angular-devkit/schematics 13.2.5
@schematics/angular 13.2.5
ng-packagr 13.2.1
rxjs 6.6.7
typescript 4.5.5
Anything else?
I was actually able to work this around and make it work in both angular and web component scenarios, but the workaround seems like a hack to me. Let me know if this is how I should be doing this, or whether this is simply a bug, or potentially a feature request.
import { Component, ContentChild } from '@angular/core';
import { ChildStandaloneComponent } from './child-standalone/child-standalone.component';
@Component({
selector: 'app-wrapper',
templateUrl: './wrapper.component.html',
styleUrls: ['./wrapper.component.scss']
})
export class WrapperComponent {
@ContentChild(ChildStandaloneComponent)
public child: ChildStandaloneComponent | Element;
// This works because once I see the child component is not evaluated correctly, I extract it from the DOM
public get childComponent(): ChildStandaloneComponent | Element {
if (!this.child) {
const collection = document.getElementsByTagName('app-child-standalone');
this.child = collection?.length ? collection.item(0) : null;
}
return this.child
}
constructor() { }
public changeChildText() {
if (this.childComponent) {
(this.childComponent as ChildStandaloneComponent).text = "Parent modified text.";
}
}
}
Issue Analytics
- State:
- Created 2 years ago
- Reactions:6
- Comments:5 (4 by maintainers)
Top GitHub Comments
The selector of a
ContentQuery
is currently used for specyfing either a directive or reference name, it’s not a CSS-like selector like@Component.selector
is. I don’t know if this could be supported in the future.This issue has been automatically locked due to inactivity. Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
This action has been performed automatically by a bot.