3

So I have this easy component:

import {
  Component,
  ViewEncapsulation,
  OnInit,
} from '@angular/core';

import { HttpClient } from '@angular/common/http';
import { BehaviorSubject } from 'rxjs';

@Component({
  selector: 'custom-element',
  template: '<div>{{ chuckText$ | async }}</div>',
  styleUrls: ['./button.component.css'],
  encapsulation: ViewEncapsulation.Native
})

export class ButtonComponent implements OnInit {

  public chuckText$ = new BehaviorSubject<string>('');

  public constructor(
    private http: HttpClient
  ) {
  this.chuckText$.next('Default Text');
}

  ngOnInit() {
    // nothing too fancy, just for testing purpose
    this.http.get('https://api.chucknorris.io/jokes/random')
      .subscribe((data: any) => {
        console.log(data.value);
        this.chuckText$.next(data.value);
      });
  }
}

After building my component and adding it in a simple angularJS app, (I need to add a custom element to an old app) I get my element with the default text and the http.get result in the console, but my component has not been updated, as shown in the picture.

Like this

I'm clearly missing something that is just not obvious to me. Any idea on why the content does not update? As a reminder this feature is released but in an experimental state, maybe it's just that.

For reference this is my app.module.ts:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, Injector } from '@angular/core';
import { createCustomElement } from '@angular/elements';
import { HttpClientModule } from '@angular/common/http';
import { ButtonComponent } from './button/button.component';

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

  constructor(private injector: Injector) {
    const customElement = createCustomElement(ButtonComponent, { injector });
    customElements.define('custom-button', customElement);
  }

  ngDoBootstrap() { }
}

1 Answers1

8

I got the same problem with some of mine custome elements. Somehowe the change detection acts differently in a custome element than in a regular angular application and doesn't update the view accordingly.

Solution 1

If you are updating the state of your component invoke the changeDetection manually.

Inject ChangeDetectorRef in your component's constructor and call markForCheck() or detectChanges() everytime you like to update your view.

Be aware of detectChanges(). It will start the changeDetection process and can have performance issues when you are not use it wisely.

Docs: ChangeDetectionRef

Solution 2

Inject NgZone in the component and call NgZone.run().

For example:

this.ngZone.run(() => this.chuckText$.next(data.value))

Docs: NgZone

SplitterAlex
  • 2,755
  • 2
  • 20
  • 23
  • Do we have an explanation for that ? I use Angular materials components and they don't animate properly due to problem in change detection on focus for example. Changedetection work but the UI is not smooth – X-Blaster Aug 24 '18 at 13:54
  • Solution 2 worked better in my case as solution 1 breaks other stuff like Material animations. – Paul Selle Feb 14 '22 at 15:09