Angular 9: How to access the result of async pipe in HTML from canDeactivate function in component code?

Viewed 521

In my Angular 9 project, I have the template code like to display the object from a service. and pass the object to the component code upon user interaction on the page.

<div *ngIf="report$ | async as report">
  ...
  <div>{{report.Title}}</div>
  <button (click)="DoSomething(report)" >Do something</button>
  ...
</div>

However, how can I access the report object from the async pipe from the component code which is not triggered from the page. For example, in the CanDeactive function when the user navigate away from the current page.

export class ReportComponent implements OnInit {
  report$: Observable<Report>;

  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private location: Location,
    private reportService: ReportService
  ) { }

  ngOnInit() {
    this.report$ = this.route.paramMap.pipe(
      switchMap((params: ParamMap) => this.reportService.getReport(Number(params.get('id')))
      )
    );
  }

DoSomething(report: Report) {
...
}

canDeactivate(): Observable<boolean> | boolean {
    if isReportChanged("How to access the report object from here?")
      return false;

    return true;
}

}

2 Answers

this can be achieved as below (for reference)


TS File

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

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ],
})
export class AppComponent implements OnInit {


  reports :any[] = [
    {Title:"Report 1"},
    {Title:"Report 2"},
    {Title:"Report 3"},
    {Title:"Report 4"},
    {Title:"Report 5"}
    ]

ngOnInit(){
  this.reports.forEach(s=>Object.assign(s,{IsTouched:false}));
  //initialize a new field to mark as touched.
}

  DoSomething(report) {
    this.reports.find(s=>s.Title == report.Title).IsTouched = true;
  }

  OnDeactive()
  {
    console.log(JSON.stringify(this.reports.filter(s=>s.IsTouched)))
  }
}

HTML

<div *ngFor="let report of reports">
  <div>{{report.Title}} - {{report.IsTouched}}</div>
  <button (click)="DoSomething(report)" >Do something</button>
</div>

<br>
<button (click)="OnDeactive()">Back Button Simulate</button>

Here you can call OnDeactive() in back button event handler of your component.

I would suggest something like this in your HTML:

<ng-container *ngIf="report$ | async as report">
  {{ setReportData(report) }}
</ng-container>

This should be calling a function to run on the stream of data

And then in your TS define that function and what to do with the data it receives. If you do like the example, then a backend report variable is set, which can then be used in other function calls:

report: Report;
...

setReportData(report: Report): void{
  if(this.report === null || this.report === undefined) {
    this.report = report
  }
}

Related