Behaviour change with *ngFor


#1

Hi everybody,

In this post, https://forum.ionicframework.com/t/problem-after-lazy-loader/122833, my problem was resolve.

But now, my code has changed, and I loop with $ngFor to display some cards.

This is my home.html :

<ion-content class="cards-bg">
  <ion-list>
    <ion-item *ngFor="let item of (items | async)?.slice().reverse()" text-wrap>
      <ion-card *ngIf="item.type === 1" tappable (click)="navigate(item.id)" class="cards-bg">
        <img src="assets/imgs/chpt2017.jpeg"/>
        <ion-card-content>
            <ion-card-title>
                {{item.titre}}
            </ion-card-title>
            <p>
                {{item.titre2}}
            </p>
          </ion-card-content>
      </ion-card>

     <ion-card *ngIf="item.type === 2" class="cards-bg">
        <ion-card-content>
            <ion-card-title>
                {{item.titre}}
            </ion-card-title>
        </ion-card-content>
        <div class='applet'>
          [Event "Nationale Clubcompetitie KNDB 2013/2014"]
           [Site ""]
           [White "Quentin"]
           [Black "Mélanie"]
           [Result "0-2"]
           [GameType "20"]
           [Round "8"]
           [Date "2014.01.11"]
           [WhiteTime ""]
           [BlackTime ""]
           [WhiteUrl "http://toernooibase.kndb.nl/opvraag/liddetailp.php?SpId=1069"]
           [BlackUrl "http://toernooibase.kndb.nl/opvraag/liddetailp.php?SpId=3073"]
           [WhitePhotoUrl "http://toernooibase.kndb.nl/Afbeeldingen/Spelers/1069.jpg"]
           [BlackPhotoUrl "http://toernooibase.kndb.nl/Afbeeldingen/Spelers/3073.jpg"]
           [WhiteFlagUrl "http://toernooibase.kndb.nl/Clublogos/CEMA - De Vaste Zet Geleen.gif"]
           [BlackFlagUrl "http://toernooibase.kndb.nl/Clublogos/WSDV Wageningen.gif"]
           [WhiteRating "1026"]
           [BlackRating "3208"] 
           1. 34-30 20-25 2. 31-27 25x34 3. 39x30 17-21 4. 44-39 21-26 5. 50-44 11-17 6. 30-25 19-24 7. 27-21 16x27 8. 32x21 14-20 9. 25x14 10x19 10. 21-16 5-10 11. 37-31 26x37 12. 41x32 6-11 13. 40-34 10-14 14. 44-40 18-23 15. 34-29 23x34 16. 40x20 15x24 17. 49-44 12-18 18. 47-41 8-12 19. 41-37 4-10 20. 46-41 10-15 21. 44-40 18-23 22. 32-28 23x32 23. 37x28 12-18 24. 39-34 7-12 25. 16x7 2x11 26. 41-37 14-20 27. 37-32 1-7 28. 42-37 9-14 29. 37-31 3-9 30. 34-29 20-25 31. 29x20 15x24 32. 43-39 11-16 33. 31-26 18-22 34. 32-27 22x31 35. 36x27 24-29 36. 33x24 19x30 37. 35x24 16-21 38. 27x16 7-11 39. 16x18 13x35 40. 24-20 25-30 41. 20-15 30-34 42. 38-33 9-13 43. 48-42 13-19 44. 42-37 19-24 45. 33-28 35-40 46. 26-21 17x26 47. 28-22 40-44
          </div>
      </ion-card>
    </ion-item>
  </ion-list>
</ion-content>

If I put my <div class='applet'> outside of <ion-item *ngFor="let item of (items | async)?.slice().reverse()" text-wrap>, the behaviour is good.

my home.ts :

declare var divs2applet;

@IonicPage()
@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  itemsRef: AngularFireList<any>;
   items: Observable<any[]>;

  constructor(public navCtrl: NavController, private db: AngularFireDatabase) {
    this.itemsRef = db.list('article');
       this.items = this.itemsRef.valueChanges();
       this.items.subscribe(res=> console.log(res));
  }

  ngOnInit() {
  	divs2applet();
  }

I don’t understand why the javascript function divs2applet() is not call to display correctly.

Thanks a lot.
Husko


#2

My guess would be because divs2applet will be called before the data is displayed. You’ll have to call it after you get your data.

I’d also recommend moving the slice().reverse() calls into your controller so that your template stays lighter.


#3

Thanks for your answer.
My question is maybe stupid, but how to call it after get the data ?

Thanks for your recommendation.


#4

I’m using Firestore but the approach is the same.

Provide an interface:

export interface Venue {
  id?: string;
  name?: string;
  address?: string;
  logo?: string;
  ...
}

Put your data access in a service.ts and have it return an Observable:

@Injectable()
export class VenuesService {

  ...

  public listLocations(currentPosition: Location): Observable<Venue[]>  {

    ...

    this.venues = this.afs.collection<Venue>('venues', ref => ref.where('location', '>', lesserGeoPoint).
      where('location', '<', greaterGeoPoint));

    return this.venues.snapshotChanges().map(actions => {

      return actions.map(a => {
        const data = a.payload.doc.data() as Venue;
        const id = a.payload.doc.id;
        return { id, ...data };
      });

    });
  }
}

Subscribe to the data in your page.ts:

  public ionViewDidLoad() {

      this.venuesService.listLocations(this.currentPosition).subscribe(data => {
        data.sort((a, b) => {
          return a.name < b.name ? -1 : 1;
        });
        this.items = data;
      });
  }

If you need to load data asynchronously, then set a flag when the data is ready:

  public ngOnInit() {

    this.mapsApiLoaderService.load().then(res => {
      this.isMapApiReady = true;
    });
  }

Your use the flag in your page’s template:

      <ion-label>
        {{item.name}}
        <p>{{item.address}}</p>
        <p *ngIf='isMapApiReady'><strong>{{getDistanceFromLatLng(item)}}</strong></p>
      </ion-label>

Also, try and use templates where possible, for example:


<ion-content>

  <ng-container *ngIf='!items; then skeleton'> </ng-container>

  <ion-list>
    <ion-item *ngFor="let item of items">
      
    ...
    
    </ion-item>
  </ion-list>


  <ng-template #skeleton>

    <ion-list class='fakeItem'>

     ...

    </ion-list>

  </ng-template>

</ion-content>



#5

Hi robinyo,

Thanks for taking time to respond to me.
But I really have trouble realizing what you advise me to do.
Can this really solve the fact that the javascript call is done differently with or without * ngFor?