Angular 9 ng-content dynamic update

2.5k views Asked by At

how i can update ng-content by updating selector on content elements?

I have code in app.component.html:

<my-slider>
  <img src="../../assets/logo1.png" />
  <img src="../../assets/logo2.png" />
</my-slider>

Here's the code my-slider.component.ts:

@Component({
  selector: 'my-slider',
  template: '<ng-content select=".render" ></ng-content>
             <button (click)="render()"> Show</button>',
  styleUrls: ['./slider.component.css']
})
export class SliderComponent {
  @ContentChildren(ImgComponent) img: QueryList<ImgComponent>;
  constructor() { }
  render() {
    this.img.last.show();
  }
}

And ImgComponent.ts

@Component({
  selector: 'img',
  template: '',
})
export class ImgComponent {
  render: boolean = false;

  constructor(
    private elRef: ElementRef,
    private renderer: Renderer2
  ) { }

  show() {
    this.render = true;
    this.renderer.addClass(this.elRef.nativeElement, 'render');
  }
  hide() {
    this.render = false;
    this.renderer.removeClass(this.elRef.nativeElement, 'render');
  }
}

When I click the button, a class is added to the element, but ng-content never updates. Can i somehow make it work? Or how I can filter components in ng-content?

3

There are 3 answers

0
Alexandr On BEST ANSWER

Thank you, all for your responce. Here is solution i had been looking for:

I had added a structual directive to img instead of making it angular component and now app.component.html looks like:

<app-slider>
  <img *myImg scr="../../assets/logo1.png" />
  <img *myImg scr="../../assets/logo1.png" />
</app-slider>

my-slider.component.ts is not realy changed. I just remove select='.render' anyway here it is:

@Component({
  selector: 'app-slider',
  template: `<ng-content></ng-content>
            <br/>
            <button (click)="render()">Toggle</button>`,
})
export class SliderComponent {
  @ContentChildren(ImgDirective) img: QueryList<ImgDirective>;
  constructor() { }
  ngAfterContentInit() {
    this.img.first.show();
  }
  render() {
    if (!this.img.last.render) {
      this.img.last.show();
    } else {
      this.img.last.hide();
    }
  }
}

And finaly my structual directive ImgDirective:

import { Directive, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[myImg]'
})
export class ImgDirective {
  render: boolean = false;
  constructor(
    private viewContainer: ViewContainerRef,
    private templateRef: TemplateRef<any>,
  ) { }

  ngOnInit() {
    this.viewContainer.clear();
  }

  show() {
    this.render = true;
    this.viewContainer.createEmbeddedView(this.templateRef)
  }

  hide() {
    this.render = false;
    this.viewContainer.clear();
  }
}

Here live example: link

2
Robin De Schepper On

I don't have the answer to your question on ng-content updating, and ng-content is an underdocumented feature of Angular IMHO. But I do have a solution to the problem:

<img 
  *ngFor="let img of images"
  [style.display]="img.render ? undefined : 'none'"
  [src]="img.src"
></img>

You then make a public images: {render: boolean, src: string}[] on your component and toggle the render property of the items.

0
claboran On

You need to access the host element in your component since your proposed solution encapsulates your custom img component into the native one. I would recommend to use a custom attribute directive here. See Stackblitz example