Asynchronous calls in Angular 4 Pipes

2k views Asked by At

I have a method on a service which fetches time from the server and I plan to use it in a custom pipe. The purpose of the pipe is to compare the the a timestamp to the current time and return it in human readable format.

Service

export class TimeService{
  currentServerTime;

  fetchCurrentTimeStamp(){
     //http request using Observable
     return sendGETRequest(.....).map(data => {
        this.currentServerTime = data;

        return this.currentServerTime;
     })
  }
}

Pipe

export class GetTimeFromNowPipe implements PipeTransform{

   fromNow;
   currentServerTime: number;
   constructor(private timeService: TimeService, private appSettings: AppSettings){}

   transform(value: number){
        var currentTime;
        this.timeService.fetchCurrentTimestamp().subscribe(data => {
            currentTime = data
            if(!value) return "";

            if(value){
               let newDate = moment.unix(value);
               this.fromNow = newDate.from(moment.unix(currentTime), true);
            }
          return this.fromNow;
        });

   }
}

HTML

<ul>
    <li *ngFor = "let model of models">
        <span>{{model.created | getTimeFromNow}}</span>
    </li>
</ul>

I'm stuck with the Async calls. How do I return the value from the pipe only after I fetch the data from the server?

2

There are 2 answers

0
Günter Zöchbauer On BEST ANSWER

A return is missing and subscribe needs to be changed map to allow the async pipe to do the subscription

return this.timeService.fetchCurrentTimestamp().map(data => {

and for async values the async pipe needs to be used

<span>{{model.created | getTimeFromNow | async }}</span>
0
marouane kadiri On

The best way to implement it is to not use the service inside the pipe. You can use custom pipe to implement only what the pipe needs to do. Your pipe needs to be defined like this:

export class GetTimeFromNowPipe implements PipeTransform{

   constructor(private appSettings: AppSettings){}

   transform(value: number, currentTime:number){
            if(!value) return "";
            else{
               let newDate = moment.unix(value);
               return newDate.from(moment.unix(currentTime), true);
            }
   }
}

inside your component you can call your service and store it inside a varible in your Oninit life cycle ( of course you need to inject the service inside the component).

serverTime:number;
ngOnInit(){
  this.timeService.fetchCurrentTimestamp().subscribe(data=>this.serverTime=data);
    }

and then inside the template of your component you can use you're pipe. with either

<ul *ngIf="serverTime">
    <li *ngFor = "let model of models">
        <span>{{model.created | getTimeFromNow:serverTime}}</span>
    </li>
</ul>

Or

<ul>
    <li *ngFor = "let model of models">
        <span>{{model.created | getTimeFromNow: serverTime | async}}</span>
    </li>
</ul>

Clarification & Purposes:

the purpose of this implementation is to facilitate:

-Testing your pipe

-easy debug in case if the serveur have problems

-an independent implementation from the server that can be used in other component with different parameters (reusability).