[Ionic3] Find a way to call a function from HTML or different methods?

Hello Ionic friends !

I m facing a problem for few days that I can’t solve.

I have an array that contains object, and each object contains a photo (it will be an array of photos).
My array contains object and im displaying each object with my *NgFor

like this :

<ion-item-sliding *ngFor="let k of filtered">

            <ion-item>
              <button class="btn" (click)="navigate(k)">

                <img  src="{{k.photo}}" style="z-index:0;"  >

I can get the photo for 1 item : so for exemple when I click on navigate(k) I find the photo because I am referring to one item:

  1. I am using a method doing this:
  loadImage(key) {

    firebase.storage().ref("/pictures/").child(key).getDownloadURL()
      .then( (url => {
        return url;
      }))

So this is the problem because I am not handling only one item but an array of items…
I have been looking for method that could be called from the HTML… but there is none : only “(click)” but I want them loaded without clicking…

So after trying this way, I decided to find another way…

  1. Why not when I create my item : load into my field ‘photo’ of my object the url to retrieve it like this I would just have to display the image by doing src="{{k.photo}}"…

So when I am creating the instance of my item I tried to do something like this:

this.s.addItem(this.item).then((res: any) => {
        this.item.seller = this.userservice.afireauth.auth.currentUser.displayName;

        this.createPost(this.Picture, this.item.uid); 
      })```

and then the method addItem : 

addItem(s : sCreds) {

s.uidOfSeller = this.userservice.afireauth.auth.currentUser.uid;
s.seller = this.userservice.afireauth.auth.currentUser.displayName;
s.date = new Date().getTime();
  s.uid = firebase.database().ref('/s-list/').push(s).key;


firebase.storage().ref("/picturesOfCustom/").child(s.uid).getDownloadURL() // --> suppose to create a url and put it in s.photo
  .then( (url => {
    s.photo = url; 
  }))

  return this.sneakRef.set(s.uid, s);

}```

But my s.photo is not hydrated and does not contain anything (inside the field : s.photo)

I might not doing it in the proper way?

  1. I also tried a method that does not work as well:
    When I retrieve my items inside my array : I decided to do something like this :
this.firedataS.orderByChild('uid').once( 'value', (snapshot) => {

        let userdata = snapshot.val();
        let temparr = [];

        for (var key in userdata) {

          if (arrayUidC.indexOf(userdata[key].uidOfSeller) != -1)  {

    firebase.storage().ref("/pictures/").child(s.uid).getDownloadURL() // --> suppose to create a url and put it in s.photo
      .then( (url => {
        userdata[key].photo = url; 
      }))

            temparr.push(userdata[key]);
            resolve(temparr);
          }

        }

It does put the url inside an item, but only one ! (generally the last), I also tried to move the “temparr.push(userdata[key])” and the “resolve(temparr);” at different place (after : the ‘}’).

So I don’t know if at least one of my three methods is correct, I would really appreciate any help / guidance :slight_smile:

Thanks Ionic friends! :star_struck:

You do not correctly implement the async aspect of the firebase function

E.g.

 firebase.storage().ref("/pictures/").child(s.uid).getDownloadURL() // --> suppose to create a url and put it in s.photo
      .then( (url => {
        userdata[key].photo = url; 
      }))

            temparr.push(userdata[key]);

Will not work as temparr will be populated with nothing as the firebase function will not resolve directly (use async await for that). Besides many other things one could change in the could, the minimal you could do is put that statement in the then:

 firebase.storage().ref("/pictures/").child(s.uid).getDownloadURL() // --> suppose to create a url and put it in s.photo
      .then( (url => {
        userdata[key].photo = url; 

            temparr.push(userdata[key]);
      }))


Alright! Thanks for your answer :slight_smile:

However I don’t get what you said about using ‘async wait’ and where to put it…

By the way I modified a bit my code and it does not exactly what I want:

 getAllSOfMyFeedS(array) {
    var promise = new Promise((resolve, reject) => {
      this.firedataS.orderByChild('uid').once('value', (snapshot) => {
        let userdata = snapshot.val();
        let temparr = [];
        for (var key in userdata) {

          firebase.storage().ref("/pictures/").child(userdata[key].uid).getDownloadURL()
            .then( (url => {
              userdata[key].photo = url;

              console.log("1: ");
              console.log(userdata[key]);

            }))
            temparr.push(userdata[key]);

          console.log("2: ");
          console.log(userdata[key]);

        }

        resolve(temparr);
        console.log("3: ");
        console.log(userdata[key]);

      }).catch((err) => {
        reject(err);
      })
    })
    return promise;
  }

Now it does add the picture but only to the second one!
Here is a screenshot of my web console when I am running my code :

I don’t get why it does not add it each time it goes into my loop.

Thanks :slight_smile:

Becuase the push is outside the Then. Put the push statement next to the console.log which succeeds in your view…

Putting the temparr.push(userdata[key]);
inside the then results with 2 same items displayed in my view
(the Adidas one)

I would suggest starting over completely, and while the following rules may seem arbitrary and weird at first, I developed them as I was learning Rx, and they helped me avoid many pitfalls, including the one you fell into here:

  1. declare types for every function parameter and return type
  2. no any in following rule #1
  3. do not manually instantiate futures (Observables or Promises) - no new anywhere in your code
  4. any time you are going to call a function that returns a future, decide which of these three types your function is. yours here is a class C, so:
  5. the very first word of any class C future function should be return

Tangentially, never use var. Always use let instead.

The main thing that rule #5 does is guide you towards thinking in terms of an assembly line, which is how idiomatic reactive code looks. You are trying to write in an imperative style, and while learning to let go of that instinct is hard (at least it was very hard for me), the effort is absolutely worth it.


What are the building blocks of our assembly line?

  • a function that returns a Promise of a list of raw thingies
  • a way to get a unique uid out of a raw thingy
  • a function that takes a uid and returns a Promise of a url
  • a way to cook a thingy by filling in its photo property with a url

What do we want?

  • a single Promise of a list of cooked thingies

I don’t speak Firebase, so somebody else is going to have to cover the nuts and bolts of that syntax, but I would write the skeleton of the method like so:

interface Thingy {
  uid: string;
  // other stuff
  photo?: string;
}

interface CookedThingy extends Thingy {
  photo: string;
}

getAllCookedThingies(): Promise<CookedThingy[]> {
  return this.firebase.allRawThingies()
    .then((raws: Thingy[]) => {
      let cooked$s = raws.map(raw => {
        return this.firebase.uidToUrl(raw.uid)
          .then((url) => {
            raw.photo = url;
            return raw as CookedThingy;
          });
      });
      return Promise.all(cooked$s);
  });
}
1 Like

Thanks for your message !
It is really appreciated ! :slight_smile:

I note what you are telling me :wink:

The problem is just an algorithmic problem, I can’t see where my logic is not right in the last code I wrote…
If anyone could see an algorithmic issue it would be really appreciated ! :slight_smile: