Search code examples
angularperformanceionic-framework

Performance issues with ionic app with large data


We have an ionic angular app which is basically a contact management app. The app becomes horribly slow as the contacts increases in the app. The taps, scrolls everything becomes slow.

The app uses offline storage and populate the data into a array which is used to render the list of cards. So, network is not an issue.

I have around 1200 contacts where this issues is happening. Please advise how can i debug issues.

The code of the list page looks like below:

 <ng-container *ngIf="core.syncedCards != undefined && core.syncedCards.length > 0">
          <ng-container *ngFor="let card of core.syncedCards">
            <app-contact-card [card]="card"></app-contact-card>
          </ng-container>  
        </ng-container>

The component code is:

  <ion-card>
    <ion-item lines="none">
      <ion-label (click)="viewCard(card)">{{card.firstName}} {{card.lastName}}</ion-label>
      
      <ion-button fill="clear" slot="end" (click)="toggleFavorite(card)">
        <ion-icon slot="icon-only"   color="warning" name="star"></ion-icon>
      </ion-button>
    </ion-item>
    <ion-card-subtitle>{{getJobDept()}}</ion-card-subtitle>
    <ion-card-content>
      <ion-item (click)="viewCard(card)">
          <ion-thumbnail slot="start" class="profile-pic">
            <img src="assets/images/noPerson.jpg">
          </ion-thumbnail>
          <ion-label class="ion-text-wrap">
             <ion-row><ion-col  class="ion-no-padding">{{card.company}}</ion-col></ion-row>
             <ion-row><ion-col  class="ion-no-padding">{{card.city}}, {{card.state}}</ion-col></ion-row>
             
             <ion-row *ngIf="card.tags !== undefined && card.tags.length > 0">
               <ion-col size="auto" *ngFor="let tag of card.tags">
                  <ion-chip [color]="core.getTagColor(tag)">
                    {{tag}}
                  </ion-chip>
               </ion-col>
             </ion-row>
          </ion-label>
        </ion-item>
        <ion-row class="ion-padding" style="padding-bottom:0px" *ngIf="refreshActions() === true"> 
          <ng-container *ngFor="let action of cardActions;let i = index">
            <ion-col size="2" *ngIf="i < 3">
              <ion-icon [name]="getLogoName(action.type)" [color]="getLogoColor(action.type)" size="large" (click)="doCardAction(action.type, card)"></ion-icon>
            </ion-col>
          </ng-container>
          <ion-col size="auto" *ngIf="cardActions.length > 3">
            <ion-icon name="chevron-down-outline" color="secondary" size="large" (click)="expandActions()"></ion-icon>
          </ion-col>
                  
           <ion-col class="ion-text-right" *ngIf="card.status == globalVariables.SYNC_FAILED">CRM Sync failed</ion-col>
           <ion-col class="ion-text-right ion-no-padding">
            <ion-icon name="ellipsis-vertical" size="large" (click)="showCardActions($event, card)" ></ion-icon>
           </ion-col>
        </ion-row>
        <ng-container *ngIf="showMoreActions === true">
            <ion-row class="ion-padding" style="padding-bottom:0px">
              <ng-container *ngFor="let action of cardActions;let i = index">
              <ion-col size="2" *ngIf="i > 2">
                <ion-icon [name]="getLogoName(action.type)" [color]="getLogoColor(action.type)" size="large" (click)="doCardAction(action.type, card)"></ion-icon>
              </ion-col>
            </ng-container>
            </ion-row>
        </ng-container>
       
  </ion-card-content>
  </ion-card>

The code to populate syncedCards is like below:

const sCards = await this.storage.get("syncedCards")
          this.core.syncedCards = []
          console.log("offline synced cards are:", this.core.syncedCards)
          for(const card of sCards){
            this.core.syncedCards.push(this.core.copyContactDataFromJson(card))
          }


copyContactDataFromJson(cardQ: any) {
    var card = new Card(cardQ._tempContactId, cardQ._frontImageUrl, cardQ._backImageUrl, 
 cardQ._status, cardQ._date, cardQ._userId, cardQ._message, cardQ._objectType)
    card.firstName = cardQ._firstName
    card.lastName = cardQ._lastName
    card.jobTitle = cardQ._jobTitle
    card.company = cardQ._company
    card.email = cardQ._email
    card.mobile = cardQ._mobile
    card.workPhone = cardQ._workPhone
    card.website = cardQ._website
    card.street = cardQ._street
    card.city = cardQ._city
    card.state = cardQ._state
    card.country = cardQ._country
    card.postalCode = cardQ._postalCode
    card.favoriteContact = cardQ._favoriteContact
    card.profilePic = cardQ._profilePic
    card.dept = cardQ._dept
    card.fax = cardQ._fax
    card.workPhoneExtn = cardQ._workPhoneExtn
    card.buddyCode = cardQ._buddyCode
      card.buddyCodeLower = card.buddyCode.toLowerCase()
      card.mobileCountryCode = cardQ._mobileCountryCode
      card.mobileAreaCode = cardQ._mobileAreaCode
      card.mobileNumber = cardQ._mobileNumber
        card.mobileRawNumber = cardQ._mobileRawNumber
      card.workPhoneCountryCode = cardQ._workPhoneCountryCode
      card.workPhoneAreaCode = cardQ._workPhoneAreaCode
      card.workPhoneNumber = cardQ._workPhoneNumber
    card.workRawNumber = ''
      card.workRawNumber = cardQ._workRawNumber
      card.favoriteContact = false
      card.partyId = -1
      card.partyNumber = ''
      card.addressNumber = ''
      card.accountPartyNumber = cardQ._accountPartyNumber
      card.accountPartyId = cardQ._accountPartyId
      card.relationshipRecId = cardQ._relationshipRecId
      card.note = cardQ._note
      card.urlType = cardQ._urlType
    card.socialLinks = cardQ._socialLinks
      card.requireApproval = false

      card.tags = cardQ._tags
      card.phones = cardQ._phones

    return card
  }

Solution

  • In my opinion, the problem is that you have so many items loaded. Even if your phone screen doesn't allow visualizing every of them, they slow down your app.

    You have two solutions:

    • If the contacts are not going to keep increasing: I recommend you to use cdk-virtual-scroll-viewport (documentation) to lazy load the elements. It only renders the items that fit on-screen.

    Example:

      <cdk-virtual-scroll-viewport [itemSize]="userItemHeight" minBufferPx="400" maxBufferPx="800" #usersVirtualScroll>
        <ion-list>
          <app-favourites-reorder [favouriteUsers]="allUsersList.favourites" (movedList)="saveReorderFavourites($event)" *ngIf="reorderFavouriteUsers"></app-favourites-reorder>
    
          <div *cdkVirtualFor="let user of users">
            <app-user-item [user]="user">
            </app-user-item>
          </div>
        </ion-list>
    </cdk-virtual-scroll-viewport>
    
    • If the contacts are going to keep increasing: You can use ion-infinite-scroll (documentation) to get contacts from database little by little instead of everything at once. Now you have 1,200 contacts, but think about when you have 1,000,000.

    Example:

    <ion-content>
      <ion-list>
        <ion-item *ngFor="let item of items; let index">
          <ion-avatar slot="start">
            <img [src]="'https://picsum.photos/80/80?random=' + index" alt="avatar" />
          </ion-avatar>
          <ion-label>{{ item }}</ion-label>
        </ion-item>
      </ion-list>
      <ion-infinite-scroll (ionInfinite)="onIonInfinite($event)">
        <ion-infinite-scroll-content></ion-infinite-scroll-content>
      </ion-infinite-scroll>
    </ion-content>