adityav
adityav

Reputation: 201

Asynchronous calls in Angular 4 Pipes

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?

Upvotes: 3

Views: 2022

Answers (2)

marouane kadiri
marouane kadiri

Reputation: 316

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).

Upvotes: 2

G&#252;nter Z&#246;chbauer
G&#252;nter Z&#246;chbauer

Reputation: 658047

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>

Upvotes: 6

Related Questions