Access text (not instance of another component) with ContentChild

814 Views Asked by At

How can I access a string of text given within the tags of a component

<my-custom-component>THIS TEXT</my-custom-component>

Within a template, I can use ng-content, or if it is an instance of some other class I can access it within the component definition like demonstrated in these examples. However I am interested in detecting if there is a string of text there or not, which I believe would make providedText undefined. However, I am always getting undefined.

@ContentChild(Element, { static: true }) providedText: Text | undefined;

I have tried Text as the first element passed to @ContentChild. Passing any will not work (I don't know why).

StackBlitz

I am interested mostly in finding if there is a string or undefined, but am also curious why ContentChild(Text... isn't working.

Edit:

I have added a potential solution, but it seems pretty imperfect, so I hope something better comes along.


Edit 2:

I now understand that @ContentChild is not a mechanism for selecting whatever native HTML I want without wiring it up to Angular’s dependency graph with a ref, directive, etc.

I am still curious if my proposed solution below is a bad idea for any reason.

3

There are 3 best solutions below

5
1252748 On

My solution for now (since I wish to capture all transcluded content) is to wrap ng-content in a containing element, then get its innerText.

@Component({
  selector: "app-parent",
  template: `
    <span #transcludedContainerRef>
      <ng-content></ng-content>
    </span>
  `
})
export class ParentComponent implements AfterViewInit {
  @ViewChild("transcludedContainerRef", { static: false })
  transcludedContainerRef: ElementRef | undefined;
  buttonText: string;
  ngAfterViewInit() {
    const isButtonTextPresent = this.transcludedContainerRef.nativeElement
      .innerText;
    if (isButtonTextPresent) {
      console.log(isButtonTextPresent); // successfully logs content
    }else {
      console.log('No text set');
    }
  }
}

It does feel hacky, but it works. I am holding out for something better.

0
Eliseo On

it's difficult if I don't know about your <my-custom-component>

In general if your custom component it's only

<ng-content></ng-content>

You can inject in constructor the elementRef

constructor(public el:ElementRef){}

From a parent

<hello >
Start editing to see some magic happen :)
</hello>

You can use

  @ViewChild(HelloComponent,{static:false}) helloComponent:HelloComponent
  click()
  {
    console.log(this.helloComponent.el.nativeElement.innerHTML)
  }

If your component has any variable -or ViewContent-, you can access this variables in a similar way

0
tutorialfeed On

So the other way to read the inner text from the component is that child component emit the value whatever it get's as input from other component. See below:

hello.component.ts

import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';

@Component({
  selector: 'hello',
  template: `<h1>Hello {{name}}!</h1>`,
  styles: [`h1 { font-family: Lato; }`]
})

export class HelloComponent implements OnInit {
  @Input() name: string;
  @Output() innerText: EventEmitter<string> = new EventEmitter();

  ngOnInit() { 
    this.innerText.emit(this.name);
  }
}

app.component.ts

   import { Component, ContentChild, AfterContentInit, OnInit } from "@angular/core";

        @Component({
        selector: "app-parent",
        template: "content from <code>app-parent</code>"
        })
        export class ParentComponent implements AfterContentInit {

        @ContentChild(Element, { static: true }) providedText: Text | undefined;
        ngAfterContentInit() {
            console.log("ngAfterContentInit Content text: ", this.providedText);
        }
        }

        @Component({
        selector: "my-app",
        templateUrl: "./app.component.html",
        styleUrls: ["./app.component.css"]
        })
        export class AppComponent {
        name = "Angular";
        _innerText: string;

        ngAfterContentInit() {}

        get childContent(): string {
            return this._innerText;
        }

        set childContent(text) {
            this._innerText = text;
        }

        innerTextFn(innertext: string) {
            this.childContent = innertext;
            console.log('Event: ', innertext);
        }

    }

app.component.html

<hello name="{{ name }}" (innerText)="innerTextFn($event)"></hello>
<app-parent>This is the content text</app-parent>

Here is stackblitz url to check: https://stackblitz.com/edit/angular-bacizp

I hope this may helpful for you and if yes then accept this as correct answer.