Reputation: 533
How can I access the "content" of a component from within the component class itself?
I would like to do something like this:
<upper>my text to transform to upper case</upper>
How can I get the content or the upper tag within my component like I would use @Input
for attributes?
@Component({
selector: 'upper',
template: `<ng-content></ng-content>`
})
export class UpperComponent {
@Input
content: String;
}
PS: I know I could use pipes for the upper case transformation, this is only an example, I don't want to create an upper component, just know how to access the component's content from with the component class.
Upvotes: 53
Views: 63755
Reputation: 2034
Another approach, although hacky, I'll admit:
@Component({
selector: "upper",
template: `<span style="display:none;" #contentWrapper>
<ng-content></ng-content>
</span>
<div class="some-class"> {{ myText }} </div> `,
})
export class UpperComponent implements AfterContentInit {
public myText: string;
@ViewChild("contentWrapper")
public contentWrapper: ElementRef;
constructor() {}
public ngAfterContentChecked() {
const newText =
this.contentWrapper.nativeElement.innerHTML;
if (newText !== this.myText) {
// do some extra stuff with the text you got
this.myText = newText;
}
}
}
What this allows you to do is to have more complex Html contetn, styles and all.
Then use it like this:
<upper> make me upper please </upper>
Pros:
Cons:
innerHTML
Upvotes: 0
Reputation: 641
You can also use TemplateRef
, which was what worked me in the end because I was using a service to initialize my component.
In your-component.component.html
<!-- Modal Component & Content -->
<ng-template #modal>
... modal content here ...
</ng-template>
In your-component.component.ts
@ViewChild('modal') modalContentRef!: TemplateRef<any>;
... pass your modalContentRef into your child ...
... For me here, the service was the middleman - [Not shown] ...
In modal.component.html
<div class="modal-container">
<!-- Content will be rendered here in the ng-container -->
<ng-container *ngTemplateOutlet="modalContentRef">
</ng-container>
</div>
In modal.component.ts
@Input() modalContentRef: TemplateRef<any> | null = null;
This how I ended up solving the problem with a TemplateRef
, since ng-content
could not be used since a service was creating the modal
Upvotes: 1
Reputation: 545
Good morning,
I've been doing a little research on this topic because something similar has happened to me in my project. What I have discovered is that there are two decorators that can help you for this solution: ViewChildren and ContentChildren. The difference mainly according to what I have found in the network is that ViewChildren accesses the interior of the component and ContentChildren accesses the DOM or the component itself.
To access the upper element from the ng-content you must change the upper element leaving it like this:
<upper #upper>my text to transform to upper case</upper>
And then simply to access the interior (In the component that has the ng-content):
@ViewChildren('upper') upper: QueryList<ElementRef>
And to access the component in general (In the component that has the ng-content):
@ContentChildren('upper') upper: QueryList<ElementRef>
Where did you get the information from: https://netbasal.com/understanding-viewchildren-contentchildren-and-querylist-in-angular-896b0c689f6e
Upvotes: 2
Reputation: 8506
class SomeDir implements AfterContentInit {
@ContentChildren(ChildDirective) contentChildren : QueryList<ChildDirective>;
ngAfterContentInit() {
// contentChildren is set
}
}
Note that if you do console.log(contentChildren), it will only work on ngAfterContentInit or a later event.
Upvotes: 2
Reputation: 657118
If you want to get a reference to a component of the transcluded content, you can use:
@Component({
selector: 'upper',
template: `<ng-content></ng-content>`
})
export class UpperComponent {
@ContentChild(SomeComponent) content: SomeComponent;
}
If you wrap <ng-content>
then you can access access to the transcluded content like
@Component({
selector: 'upper',
template: `
<div #contentWrapper>
<ng-content></ng-content>
</div>`
})
export class UpperComponent {
@ViewChild('contentWrapper') content: ElementRef;
ngAfterViewInit() {
console.debug(this.content.nativeElement);
}
}
Upvotes: 59
Reputation: 202148
You need to leverage the @ContentChild
decorator for this.
@Component({
selector: 'upper',
template: `<ng-content></ng-content>`
})
export class UpperComponent {
@Input
content: String;
@ContentChild(...)
element: any;
}
Edit
I investigated a bit more your issue and it's not possible to use @ContentChild
here since you don't have a root inner DOM element.
You need to leverage the DOM directly. Here is a working solution:
@Component({
selector: 'upper',
template: `<ng-content></ng-content>`
})
export class UpperComponent {
constructor(private elt:ElementRef, private renderer:Renderer) {
}
ngAfterViewInit() {
var textNode = this.elt.nativeElement.childNodes[0];
var textInput = textNode.nodeValue;
this.renderer.setText(textNode, textInput.toUpperCase());
}
}
See this plunkr for more details: https://plnkr.co/edit/KBxWOnyvLovboGWDGfat?p=preview
Upvotes: 37