Search code examples
htmlangulartypescriptpromiseangular-pipe

Trigger Angular 2 pipe after page refresh with data from promise


In my application I have a table which is filled by an array and where I use a filter on it. In a OnInit, a promise is called to get the data. Now the problem is that after a refresh, the value of _projects (and the input parameter of transform) is undefined and nothing is shown in the table. When I click a dropdown the transform function is triggered and the table is filled. Is there a way to trigger the pipe when _projects is defined in the promise? I also tried with pure/impure pipes.

this._projectService.getProjectsPromise().done(
    function (jsdo, success, request){
        that._projects = request.response[HorizonData.dsProjects][HorizonData.dsProjects][HorizonData.ttProjects];
        for (let project of that._projects) {
            project.WhenWhoCreatedFull = project.WhenWhoCreated
            var whenwho = project.WhenWhoCreated.split(" ");
            project.WhenWhoCreated = whenwho[0] + " " + whenwho[2];
        }
    }                
)

In my component.html

<tr title="{{ project.INFO }}" *ngFor="let project of _projects | projectFilter: filtered">

Angular Pipe:

transform(value: IProject[], filterBy: IProject): IProject[] {
    return filterBy && value ? value.filter((project: IProject) =>
        (project.projectnr.toLowerCase().indexOf(filterBy.projectnr.toLowerCase()) !== -1)
        && (project.projectname.toLowerCase().indexOf(filterBy.projectname.toLowerCase()) !== -1)
        && (project.clientnr.toLowerCase().indexOf(filterBy.clientnr.toLowerCase()) !== -1)
        && (project.clientname.toLowerCase().indexOf(filterBy.clientname.toLowerCase()) !== -1)
        && (filterBy.statusdesc.indexOf(project.statusdesc) !== -1)) : value;
}

Solution

  • Yes! There are several ways of doing this- depending on your usecase.

    1. After setting the new value of _.projects, you can make a manual call to Angular to trigger change detection, as outlined in this previous answer. While this is certainly the easiest to implement, it requires a lot more computation on your browser's part, and will likely lead to a slower app.

    2. You can refactor your code so that _.projects is itself a promise and then use the Angular async pipe to handle updates. This is also relatively easy, and has the benefit of being less resource intensive on Angular's end:

    this._projects = this._projectService.getProjectsPromise().then(
      function(jsdo, success, request) {
        var projects = request.response[HorizonData.dsProjects][HorizonData.dsProjects][HorizonData.ttProjects];
        for (let project of projects) {
          project.WhenWhoCreatedFull = project.WhenWhoCreated
          var whenwho = project.WhenWhoCreated.split(" ");
          project.WhenWhoCreated = whenwho[0] + " " + whenwho[2];
        }
        
        return projects;
      }
    )
    <tr title="{{ project.INFO }}" *ngFor="let project of (_projects | async) | projectFilter: filtered">

    1. Promises, on the whole, are not the best for handling reactive data, as after they have resolved, no more data can be updated. While this would require significantly more refactoring than just this chunk of code, I would look into RxJS- it is a library for creating Observables, which are reactive data flows. This is a lot better for your code as a whole, so if you have the option to refactor to make use of RxJS, I would.