0

I have researched other similar items here so far without result and obviously need to understand promises better but I am struggling here.

I have an Ionic 4/Angular 8 project with and Azure based back-end. I am trying to get the pages to display images from the Azure storage which requires a key appended to the url.

To avoid having the 'check key, get a new one if expired, append to url' code in every page, I thought I'd make a service function to do it. But though I can get it to work, the calling function doesn't appear to be waiting on the '.then' and so I am getting undefined. (I think). Ideally, I would like the service to just return a string

The service function (with some comments and debug o/p) I have is this:

   // Ideally I'd like to just return a string here ---v
 // v- but the async nature needs async return type ??
 async getStorageURLWithKey(url: string): Promise<string> {
  const nowplus5 =  addMinutes(Date.now(), 5); // 5 mins to give some leeway
  console.log(nowplus5);
  console.log(url);
  console.log(this.azurekey);
  if (!this.azurekey || isBefore( this.azurekey.Expires, nowplus5 )) {
    console.log('Getting new Key');
    const keyObj = await this.getAzureKeyServer().toPromise();
    await this.saveAzureKeyStore(keyObj);
    return url + keyObj.Key; // Return the url with the new Key
  } else {
    console.log('Key is valid' + this.azurekey.Expires);
    const rval = new Promise<string>(function(res) {
      res(url + this.azurekey.Key);
    });
    return rval ; // Key is in time so return it with url
  }
 }

My calling function is this:

getBizImg(): string {
  console.log('GetBizImg');
  if (this.business.Id) {  // We have a Business Object
    this.userService.getStorageURLWithKey(this.business.ImageURL).then((url) => {
      console.log(url);
      return url;
    }, reject => {
      console.log('Rejected:' + reject);
    });
  } else {
    return '';
  }
}

If I call the getBizImg function from say ngOninit, it is getting back undefined before the getBizImg - console.log(url) line.

Here's the ngOninit:

ngOnInit() {
  const imageurl = this.getBizImg();
  console.log(imageurl);
}

Really the call should be coming from the page:

<ion-content padding>
   <ion-img [src]="getBizImg()" ></ion-img>
</ion-content>

But i have commented this out till I can resolve this issue. Otherwise it gets into a seemingly endless loop.

If I turn the getBizImg() into an async function and await the call and return a promise, it works fine -if I modify my ngInit to:

ngOnInit() {
  this.getBizImg().then((wibble) => {
    console.log(wibble);
  });
}

and the getBizImg function to:

async getBizImg(): Promise<string> {
  console.log('GetBizImg ID= ' + this.business.Id);
   if (this.business.Id) {  // We have a Business Object
     const url = await this.userService.getStorageURLWithKey(this.business.ImageURL);
     console.log(url);
     return url;
   } else {
     return '';
   }
 }

But that won't get me the simple string I need for the HTML.

I'm not sure if I am missing something (probably) obvious...

**** Edit update**** I have tried changing to the fully async and including the angular async pipe:

<ion-content padding>
  BIZ IMAGE
<ion-img [src]="getBizImg() | async" ></ion-img>

I also tried to just show the string with:URL is: {{ getBizImg() | async }}

But it gets into a (seemingly endless loop). It is strange though, because it is getting to the this.getAzureKeyServer() function but I am not hitting a breakpoint that I have set in my server code at the GetKey API call.

This set me on a different path to check the page function some more. I took out the calls to the back end to just return a string like this:

<ion-content padding>
  BIZ IMAGE
  URL is: {{ getBizImg() | async }}
</ion-content>

  async getBizImg(): Promise<string> {
    console.log('GetBizImg ID= ' + this.business.Id);
    if (this.business.Id) {  // We have a Business Object
      const url = 'wibble'; // this.business.ImageURL;
      // const url = await this.userService.getStorageURLWithKey(this.business.ImageURL);
      console.log(url);
      return url;
    } else {
      console.log('invalid id');
      return 'invalid id';
    }
  }

... and that just gets into an endless loop too, so I am thinking there is something more fundamental going on... I mean have I made some kind of schoolboy error here?

Brett JB
  • 687
  • 7
  • 24
  • I think this might help me, Im going to investigate.. https://stackoverflow.com/questions/41265716/angular-2-endless-loop-in-async-pipe – Brett JB Aug 28 '19 at 10:23

2 Answers2

1

getBizImg() never returns a URL since you only return it in the callback of the getStorageURLWithKey() promise.

Return that promise (and change the method type to Promise<string> and use the async pipe to display it:

getBizImg(): string {
  console.log('GetBizImg');
  if (this.business.Id) {  // We have a Business Object
    // return here
    return this.userService.getStorageURLWithKey(this.business.ImageURL).then((url) => {
      console.log(url);
      return url;
    }, reject => {
      console.log('Rejected:' + reject);
    });
  } else {
    return '';
  }
}
<ion-content padding>
   <ion-img [src]="getBizImg() | async"></ion-img>
</ion-content>
Thomas
  • 8,426
  • 1
  • 25
  • 49
  • Thank you Thomas, I have put an **Edit Update** in the ext above.. simplified it right down. So I think my problem is to do with the async pipe.. does it need setting up? – Brett JB Aug 28 '19 at 10:19
  • Solved it by stripping back and realising the problem wasn't so much the code but the approach and a mis-understand of angulars change management - see here:https://stackoverflow.com/questions/41265716/angular-2-endless-loop-in-async-pipe, But thank you anyway. – Brett JB Aug 28 '19 at 11:37
0

I think your async getBizImg() is right. you could do it this way

ngOnInit() {
  this.getBizImg().then((wibble) => {
    console.log(wibble);
    // just add here this.urlImage = wibble
  });
}

on your component add a urlImage. and in the html

<ion-content padding>
   <ion-img *ngIf="urlImage" [src]="urlImage" ></ion-img>
</ion-content>

or if you want to call it from the html, I'am not sure but try the asyncPipe https://angular.io/api/common/AsyncPipe

<ion-content padding>
   <ion-img [src]="getBizImg() | async" ></ion-img>
</ion-content>
med morad
  • 497
  • 4
  • 12
  • Thanks med, you were right ... My issue wasn't in my code so much as the approach and a mis-understanding of angular. It became clearer when I removed the calls to the back end (probably should have done that in the first place - but wasn't aware of this issue here: https://stackoverflow.com/questions/41265716/angular-2-endless-loop-in-async-pipe). – Brett JB Aug 28 '19 at 11:35