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>
6
  • have created a demo( stackblitz.com/edit/angular-vrcsj4 ) based on your code. The ui seems to be rendering the changed array length on addition of new alements to array. Can you please explain a bit more about the issue that you are facing. Commented Sep 12, 2019 at 19:00
  • @SiddhantPatra your demo is irrelevant, he's using Angular Elements (@angular/elements) which works a little differently than "normal" Angular. Commented Sep 12, 2019 at 19:11
  • @Input() set counter(val) { console.log(val) } Commented Sep 12, 2019 at 19:15
  • add your code to stackblitz , so that everybody can understand Commented Sep 12, 2019 at 19:18
  • next.plnkr.co/edit/nt5YkmCpOgm2UJcR Commented Sep 12, 2019 at 19:34

3 Answers 3

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
    }
}
Sign up to request clarification or add additional context in comments.

2 Comments

i reiceve this error --> Cannot read property 'detectChanges' of undefined
@Bonfry Sounds like ChangeDetectorRef wasn't actually injected. Did you pass an Injector instance as an option when you called createCustomElement() to create your custom CounterComponent element?
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

1 Comment

This works due to Angular detecting the change of the images reference. Immutability definitely works best for change detection.
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?

2 Comments

angular elements doesn't inject ngZone in the componet
sorry, I didn't get. Are you saying we can't inject ngZone in component?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.