1

I have Angular Element Component (with Angular 8) which has 2 props. When I try to push something in the array the Shadow-Root doesn't re-render it(the counter yes instead). How I can force the rendering when i push an object in the component?

Thanks

This is the Component class

export class CounterComponent implements OnInit {

  @Input()
  counter: number;

  @Input()
  images: Array<any>;

  constructor(private cdr: ChangeDetectorRef) { }

  ngOnInit() {
    this.counter = 0;
    this.images = [];
  }

  add() {
    this.counter++;
  }

  reduce() {
    this.counter--;
  }

  @Input()
  addImage() {
    this.images.push({ ciccio: 'piccio'}); //no rerender
    this.cdr.detectChanges(); // Error cdr is null
  }
}

AppModule

import { BrowserModule, platformBrowser } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';

import { CounterComponent } from './counter/counter.component';
import { createCustomElement } from '@angular/elements';

@NgModule({
  declarations: [
    CounterComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  entryComponents: [CounterComponent]
})
export class AppModule {

  constructor(private injector: Injector) {

  }

  ngDoBootstrap( ) {
    const el = createCustomElement(CounterComponent, {injector: this.injector});
    customElements.define('count-component', el);
  }
 }

This is the component template

<div style="display: flex;justify-content: center;flex-direction: column">
    <h1>Counter component</h1>
    <h4>Count ---> {{counter}}</h4>
    <button (click)="add()">Increase</button>
    <button (click)="reduce()">Decrease</button>
</div>

<h3>
Images {{images.length}}
</h3>
Bonfry
  • 163
  • 2
  • 12

3 Answers3

1

According to "Angular Elements Overview" in Angular's documentation:

We are working on custom elements that can be used by web apps built on other frameworks. A minimal, self-contained version of the Angular framework will be injected as a service to support the component's change-detection and data-binding functionality. For more about the direction of development, check out this video presentation.

As said in 8:28 in the video presentation attached above, dependency injection works for custom elements in Angular Elements. Therefore, inject ChangeDetectorRef into your custom element and call detectChanges() when mutating your images array, like so:

export class CounterComponent /* ... */ {
    constructor(private cdr: ChangeDetectorRef) { }   
    /* ... */
    addImage() {
        this.images.push({ ciccio: 'piccio'});
        this.cdr.detectChanges(); // change detection will detect the change in `images` and render
    }
}
Amit Beckenstein
  • 1,220
  • 12
  • 20
  • i reiceve this error --> Cannot read property 'detectChanges' of undefined – Bonfry Sep 12 '19 at 19:57
  • @Bonfry Sounds like `ChangeDetectorRef` wasn't *actually* injected. Did you [pass an `Injector` instance as an option when you called `createCustomElement()`](https://youtu.be/Z1gLFPLVJjY?t=558) to create your custom `CounterComponent` element? – Amit Beckenstein Sep 12 '19 at 20:04
0
  @Input()
  addImage() {
     this.ngZone.run(() => {
        this.images.push({ ciccio: 'piccio'}); //no rerender
     });
  }

Can you try injecting NgZone from @angular/core and run inside it?

Siddharth Pal
  • 1,408
  • 11
  • 23
0

Try this, which will actually change the value of the images property on your component, allowing it to see the change:

  @Input()
  addImage() {
    this.images = [...this.images, { ciccio: 'piccio'}]; // should render
  }

Plunk: here

Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113