The matTooltip in following component is redering correctly. The overlay and small bubble for the tooltip is rendered, but the text is missing (although in the html when inspecting in the browser) and it isn't positioned correctly.
Interesting is, that the tooltip works when I remove the detectChanges() call, or it works outside the *ngFor even with detectChanges();
@Component({
selector: 'mur-app-titlebar',
templateUrl: './app-titlebar.component.html',
styleUrls: ['./app-titlebar.component.scss']
})
export class AppTitlebarComponent implements OnInit, OnDestroy {
public appbarItems: IMenuItem[];
private destroy$ = new Subject();
constructor(
private appBarService: AppBarService, // my custom service
private changeDetector: ChangeDetectorRef,
) {
}
public ngOnInit() {
this.appBarService.getAppbarItems().pipe( //observable comes from outside of angular
takeUntil(this.destroy$)
).subscribe(value => {
this.appbarItems = value || [];
// change detection is not triggered automatically when the value is emmited
this.changeDetector.detectChanges();
});
}
public ngOnDestroy() {
this.destroy$.next();
}
}
<ng-container *ngFor="let item of appbarItems">
<button mat-button
(click)="item.onclick && item.onclick()"
[disabled]="item.disabled"
[matTooltip]="item.tooltip"
[style.color]="item.color">
<mat-icon *ngIf="item.icon"
[class.mr-3]="item.label">
{{item.icon}}
</mat-icon>
<span>{{item.label}}</span>
</button>
</ng-container>
I have verified, that appbarItems is set only once and is not changing
Usually you don't need to call cdRef.detectChanges()
in callback of async operations in Angular.
But if you do it it means that you're trying to solve some problem with view updates. There can be several reasons why component view is not updated after async code:
your component is hidden for checking under OnPush change detection strategy
callback is executed outside of Angular zone.
Looks like you faced the second situation. Calling cdRef.detectChanges outside of Angular zone get bring you a situation when some of Angular handlers will be registered outside of Angular zone. As a result view won't be updated as a result of those handlers and you will have call detectChanges in other places or again use zone.run.
Here's an example of such cases https://ng-run.com/edit/XxjFjMXykUqRUC0irjXD?open=app%2Fapp.component.ts
Your solution might be returning code execution to Angular zone by using ngZone.run
method:
import { NgZone } from '@angular/core';
constructor(private ngZone: NgZone) {}
.subscribe(value => {
this.ngZone.run(() => this.appbarItems = value || []);