Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

angular2 detect changes in ng-content

Is there a way to detect changes in ng-content?

@Component({
    selector: 'example',
    template: `<ng-content></ng-content>`
})
export class Example {}

@Component({
    selector: 'test',
    template: `
        <example>
            <div *ngFor="let el of elements">{{el}}</div>
        </example>`
})
export class Test {
    elements = [1, 2, 3];

    ngOnInit() {
        setInterval(() => this.elements[0] += 10, 3000);
    }
}

I would like to get some information in Example class when my ng-content will change.

Here is plunker

like image 269
Stwosch Avatar asked Nov 15 '17 11:11

Stwosch


People also ask

How do you identify changes in Angular components?

To run the change detector manually: Inject ChangeDetectorRef service in the component. Use markForCheck in the subscription method to instruct Angular to check the component the next time change detectors run. On the ngOnDestroy() life cycle hook, unsubscribe from the observable.

What triggers change detection in Angular?

By default, angular will run the change detector every time @Input() data is changed or modified. But with OnPush strategy, the change detector is only triggered if the data passed on @Input() has a new reference.

What is Ngcontent?

The ng-content is used when we want to insert the content dynamically inside the component that helps to increase component reusability. Using ng-content we can pass content inside the component selector and when angular parses that content that appears at the place of ng-content.


2 Answers

The easiest solution is to use the cdkObserveContent directive. First, you must add to the imports of the module that owns the component ObserversModule

import {ObserversModule} from '@angular/cdk/observers';

@NgModule({
  imports: [
    /* ...other modules you use... */
    ObserversModule
  ],

/* ... */
})
export class MyModule { }

Then in your component's template:

<div (cdkObserveContent)="onContentChange($event)">
  <ng-content></ng-content>
</div>

The event will trigger each time the content inside changes somehow and the value passed to the function is an array with all the details about what changed. The value of the data that has changed can be found in target.data.

In your component.ts:

onContentChange(changes: MutationRecord[]) {
   // logs everything that changed
   changes.forEach(change => console.log(change.target.data));
}

Also, you can use as a service that gives you an Observable<MutationRecord[]>

like image 109
Dylanbob211 Avatar answered Sep 17 '22 12:09

Dylanbob211


My solution for a project is to monitor the content's innerHTML. This example is from a tooltip that shows template content. I don't really like this solution, and would be happy to have a different/better one offered. This will emit whenever there are changes.

/// tooltip.component.html
<div class="tooltip" [class.hidden]="hidden">
    <div #content>
        <ng-content></ng-content>
    </div>
</div>

 

///tooltip.component.js
import { AfterContentChecked, AfterContentInit, Component, ElementRef, EventEmitter, Output, ViewChild } from "@angular/core";

@Component({
    selector: "example",
    templateUrl: "./tooltip.component.html",
    styleUrls: ["./tooltip.component.scss"]
})
export class TooltipComponent implements AfterContentInit, AfterContentChecked {    
    @ViewChild("content") contentWrapper: ElementRef;
    content = "";
    @Output() public readonly contentChanged = new EventEmitter<string>();

    ngAfterContentInit(): void {
        this.content = this.contentWrapper.nativeElement.innerHTML;
        this.contentChanged.emit(this.content);
    }

    ngAfterContentChecked(): void {
        const c = this.contentWrapper.nativeElement.innerHTML;
        if (c !== this.content) {
            this.content = c;
            this.contentChanged.emit(this.content);
        }
    }
}
like image 21
Will Shaver Avatar answered Sep 17 '22 12:09

Will Shaver