Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular5: Need to prevent ngbTooltip from rendering before data is returned from observable

I'm new to angular 2+, and inherited an incomplete project. My goal is to create a tooltip that checks for audit details on a particular field, and displays them. An example might be:

"Updated by Seth, on 8/18/18."

To do this I've created a componentin angular that passes all relevant information back via a service. I can see that the service call is working as expecte by looking in the web debugger. Unfortunately, what is rendering is a tooltip with the following incomplete text:

 "updated by , " 

Here is my component template.

<ng-template #tipContent>
<span *ngIf="loadCompleted">Updated By {{ auditDetail.updatedBy }}, {{ auditDetail.updatedAt | amTimeAgo }}</span>
</ng-template>
<i [ngbTooltip]="tipContent" (mouseenter)="refreshAuditDetail()" class="fas fa-info-circle fa-sm"></i>

Here is the component typescript.

@Component({
 selector: 'audit-tooltip',
 templateUrl: './audit-tooltip.component.html',
 styleUrls: ['./audit-tooltip.component.css']
})
export class AuditTooltipComponent {
 @Input() plan: Plan;
 @Input() fieldName: string;
 auditDetail: AuditDetail;
 loadCompleted: boolean = false;

constructor(private planService: PlanService) { }

refreshAuditDetail() {
 var planId = this.plan.id;
 var fieldName = this.fieldName;
 var fieldValue = this.plan[this.fieldName];
 this.loadCompleted = false;


this.planService.getAuditDetails(planId, fieldName, fieldValue)
  .subscribe((auditDetail: AuditDetail) => {
    console.log(auditDetail);
    this.auditDetail = auditDetail;
  }, () => {}, () => this.loadCompleted = true);
}
}

And, if necessary, here is where I'm using it.

  <h5 class="card-title">{{fieldLabel}}<audit-tooltip [fieldName]="fieldName" [plan]="plan"></audit-tooltip> </h5>

What am I missing?

like image 813
Seth Avatar asked Aug 27 '18 20:08

Seth


2 Answers

Look into using OnInit lifecycle hook which is processed first when a component is loaded.

Documentation For ngOnInit

Thats simple enough, then move that call to the service as seen below into the life cycle hook.

Give the class a public variable ie

public toolTipData: string;

(moved into OnInit)

this.planService.getAuditDetails(planId, fieldName, fieldValue)
   .subscribe((auditDetail: AuditDetail) => {
    this.tooTipData = auditDetail.toolTipInfo // (guessing at name)
    this.auditDetail = auditDetail;

    this.loadCompleted = true);
  },
  (errors) => 
  { 
    console.log(errors) // api error status code.
  }
});

In the HTML set the tool tip value to

[ngbTooltip]="toolTipData"

This should help populate the tool tip appropriately.

like image 176
Dince12 Avatar answered Dec 28 '22 23:12

Dince12


The asynchronous changes aren't showing up because NgbTooltip window uses ChangeDetectionStrategy.OnPush and the only way to trigger change detection is calling open() on a closed tooltip.

In order to access the NgbTooltip API:

Add a template reference to NgbTooltip

    <i #tooltip [ngbTooltip]="tipContent" (mouseenter)="refreshAuditDetail()" class="fas fa-info-circle fa-sm"></i>

Bring the reference into the component

    @ViewChild('tooltip') tooltip: NgbTooltip;

Hacky Solution

Close and re-open tooltip once the data is loaded. This could cause issues if the user already moved off of the tooltip by the time the data is loaded.

     refreshAuditDetail() {
      ...
      this.planService.getAuditDetails(planId, fieldName, fieldValue)
        .subscribe((auditDetail: AuditDetail) => {
          this.auditDetail = auditDetail;
          this.tooltip.close();
          this.tooltip.open();
        }, () => {}, () => this.loadCompleted = true);
      }
    }

Better Solution

Set triggers="manual". Call this.tooltip.open() in refreshAuditDetails .Add a (mouseleave) event which cancels the subscription from refreshAuditDetails and calls this.tooltip.close().

like image 21
Steve B Avatar answered Dec 28 '22 23:12

Steve B