1

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.

1252748
  • 14,597
  • 32
  • 109
  • 229

3 Answers3

2

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.

1252748
  • 14,597
  • 32
  • 109
  • 229
  • If you have an opinion, please leave a comment saying if you think this is okay or not. – 1252748 Dec 12 '19 at 23:50
  • there are a few answers out there re detecting if ng-content is empty, and they're all mostly along these lines. – bryan60 Dec 16 '19 at 16:35
  • @bryan60 can you link me? I thought I had seen them all :/ – 1252748 Dec 16 '19 at 16:39
  • 1
    this is the top answer on the topic: https://stackoverflow.com/questions/35107211/in-angular-2-how-to-check-whether-ng-content-is-empty using a template ref isn't very different from using view child – bryan60 Dec 16 '19 at 17:53
  • I think the best you can improve here is extract this logic into a directive and that's all. Just for the sake of convenience – Sergey Dec 19 '19 at 18:56
1

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.

tutorialfeed
  • 965
  • 3
  • 11
  • 24
0

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

Eliseo
  • 50,109
  • 4
  • 29
  • 67