NativeScript / nativescript-angular

Integrating NativeScript with Angular
http://docs.nativescript.org/angular/tutorial/ng-chapter-0
Apache License 2.0
1.21k stars 241 forks source link

Poor performance in data binding using ngFor (nested) even when using <template ngIf> #500

Closed rrcoolp closed 6 years ago

rrcoolp commented 8 years ago

Hi guys,

I have been playing around with NG for a while and although its a fantastic framework - the only major hold back I am finding in developing a smooth and fast app is the performance of DATA BINDING of items more than 20 items in array....

I have built an example MENU for shopping by departments - however the performance of the angular binding and rendering complete is extremely slow....

Here is the example of my GRID (apologies if things seem quite messy) - as I have stripped down my app to build a small demo application posted here:

https://github.com/rrcoolp/menu-test

RUN command: tns run android

Click on Accessories (see slow performance in binding takes a second or so).....Try clicking other expandable submenus etc...

Let me know if you feel anything is unclear.

adisoftbn commented 8 years ago

I also met this issue, and I finally moved to listview component which works super fast.

rrcoolp commented 8 years ago

Hi @adisoftbn,

Thanks for sharing. Are you able to post a short example that you have used - something that would replace the need of NGFOR perhaps?

I am not sure if you looked at my example APP - which has upto 3 levels of sub-categories - so am intrigued to learn how you would achieve this in LISTVIEW...

Yet - would you agree that NG should be just as quick though - if it were a choice of the future?

Thanks again.

adisoftbn commented 8 years ago

Hey @rrcoolp

I use listview for rendering one 2 levels array. First time i made something like you, NGFOR and inside of NGFOR Item another NGFOR.

I have a samsung s7 for testing. And it was extremely laggy. Actually you couldn't use it. So what i made was to transform the two level array in an one level array, but marking every object from it as "item" or "sub item". And i made a common listview template for both.

_I know is not a good idea, but I had to find a quick hack to temporary solve the issue._

The problem with listview is that it doesn't automatically expand in height so you cannot add an listview into a root listview item. I have tried doing a 2 level listview architecture, but the child listview was very small and had scrolling.

I don't have a full example to share yet because i'm working on a closed source project, but i can share for you the following _hacked snippet_ :D

  <StackLayout row="1" class="global-root-small">
    <ListView [items]="activityTypeItemService.items" class="items-multi-select" verticalAlignment="center" (itemLoading)="listViewItemLoading($event)" separatorColor="transparent">
      <template let-item="item">
        <StackLayout [padding]="item.isSubItem ? '0 5 0 40' : '0 5 0 5'">
          <GridLayout columns="30, *, 30" rows="auto, *" (tap)="selectActivityType(item)" [class.global-selected-row]="item.isSubItem ? selectedSubActivityId==item.id && selectedActivityId==item.parentId: selectedActivityId==item.id">
            <Image col="0" src="~/Images/add-activity/{{item.isSubItem ? 'subtypes/'+item.icon : 'types/'+item.icon}}-white.png" class="global-friend-small-image"></Image>
            <Label col="1" [text]="item.title" class="h2-text-style global-friend-name" verticalAlignment="center" textWrap="false" [margin]="item.isSubItem ? '0 0 0 10' : '0'"></Label>
            <Image col="2" [visibility]="item.hasChilds ? 'visible' : 'collapse'" [src]="!item.isSubItem && selectedActivityId == item.id ? 'res://general_arrow_down_white' : 'res://general_arrow_left_white'" class="global-friend-dropdown-icon"></Image>
          </GridLayout>
        </StackLayout>
      </template>
    </ListView>
  </StackLayout>
rrcoolp commented 8 years ago

Hi @adisoftbn,

Thanks again for you help.

I really do feel that the NGFOR (nested) like our approach - should work very well and quick....Which is disappointing - however like you I am in a little hurry to complete this module for client - so will test your approach and see if it makes much difference to the performance...

Let's hope someone from {N} picks this up and share's some advice about this nested NGFORs business soon.

Thanks again.

adisoftbn commented 8 years ago

@rrcoolp

By the way. Try also with angular 2.1. I see some speed improvements in this version of angular. But remove the _@angular/platform-server_ plugin before. Otherwise will fail to run.

    "@angular/common": "2.1.0",
    "@angular/compiler": "2.1.0",
    "@angular/core": "2.1.0",
    "@angular/forms": "2.1.0",
    "@angular/http": "2.1.0",
    "@angular/platform-browser": "2.1.0",
    "@angular/platform-browser-dynamic": "2.1.0",
    "@angular/router": "3.1.0",
    "nativescript-angular": "1.1.0",

Remove platform folder, before building with angular 2.1

dacrystal commented 7 years ago

Can you check if enabling production mode enableProdMode() has any impact on your issue ?

Angular Doc: https://angular.io/docs/ts/latest/api/core/index/enableProdMode-function.html

rrcoolp commented 7 years ago

Hi @dacrystal,

enableProdMode() is already called in main.ts - as you can see in this demo application:

https://github.com/rrcoolp/menu-test

Thanks

SBD580 commented 7 years ago

+1

valentinstoychev commented 7 years ago

Hey all please check this article for performance tips around using arrays and lists in NativeScript and Angular - https://medium.com/@alexander.vakrilov/faster-nativescript-listview-with-multiple-item-templates-8f903a32e48f#.785zfnyms

x00 commented 7 years ago

@valentinstoychev that article is great, but does it also apply to gridview? I don't really want list. Also the cell can have a lot of variations. If you would have 5 boolean properties that would be 32 templates....?

If take all the variations an out an just loop a label it still takes just a long. For me it take milliseconds to load the data 3 second to render it. If the next time I render if there is no change it is faster but as soon as I change 3 whole seconds again.

I'm also not understanding why it takes so long to draw, give a limited data. This is not really to do with recycling I wouldn't have thought.

x00 commented 7 years ago

I mean if my template was generated by producing a string of markup using old school concatiantion you wouldn't expect this lag in rendering it. This is specifically an issue of the ngFor loop, within the template.

I wonder if <Repeater> performs better then <template *ngFor..nested. I will give it a try.

x00 commented 7 years ago

I ran an android test with a multi dimensional array using nested *ngFor put the mm:ss:sss time at the start finish and for each item. There was no time difference but is still took seconds to (re)render (this assumes some graphical change and variable styling ).

This implies that it is the applying of the styles/rendering that is taking the most time. Maybe to do with how {NS} is handling some views.

This is clued in by the fact that the times for inside the template time sometimes were milliseconds before, item outside, which makes sense. This is processed first and as I understand {NS} has "virtual" views.

However the actual visual change is taking forever. Same thing for <Repeater> although I tested within an angular template.

x00 commented 7 years ago

I also applied enableProdMode as I had no choice due working with Dates and times it would throw up errors about how the value had changed.

edit I think the performance issue is linked to the level of styling involved and the number of items and sub items. The less complicated the styling the e.g variable classes within each level of the *ngFors the more overhead it is taking. There is obliviously a bottleneck here.

However it is still disappointing becuase if this was a hybrid app, but not requesting from server you would not expect such a lag to render.

x00 commented 7 years ago

ok it is obvious fro test that the innermost *ngFor are processed first as these are the earliest timestamps before their parent *ngFor and before outside of the templates

NickIliev commented 6 years ago

@x00 consider using ListView instead of ngFor for your mobile application. While in Web development the performance is not a big issue in the mobile world you can easily hit OutOfMemory exception - good explanation why to prefer ListView and how to use structural directives can be found here.

Closing due to inactivity.

x00 commented 6 years ago

Listview wasn't suitable for this particular application. Instead I re-factored that data, and used a fixed number cell wraplayout, collapsing cells where needed and re-rendering cells individually. This is kind of what ListView does with its recycling, however in this case the performance was more specifically catered to the the application.

GridView is kind of the equivalent to ListView but for grid, however I went with a wraplayout where the cells are driven by optimised data lookupos. This is because the data was in a know range of lengths, and not arbitrary length.

whatwhywhenandwho commented 4 years ago

@NickIliev Thank you very much for this suggestion. Made my app super fast. Now lists truly feel native-like 🚀