NativeScript / NativeScript/NativeScript

Dismiss Modal in Nativescript Angular Can Take Multiple Seconds to Respond

Open
#8,419 1 comment 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

os: ios
Dominant language
TypeScript
Stars
25.7k
Forks
1.7k
Avg merge
1d 5h
Merged PRs (30d)
35

Description

Environment

  • CLI: 6.4.0
  • Cross-platform modules: 6.4.1
  • Android Runtime: 6.4.1
  • iOS Runtime: 6.4.0
  • Plugin(s):
    Nativescript Angular

Describe the bug
Apps with large numbers of nested angular components in modal views begin to take exponentially longer to close.

This seems to only affect modals presented in non-fullscreen mode. Modals presented in fullscreen seem to be unaffected.

To Reproduce
Make normal use of in Angular. The more items you nest, the larger the delay will be, exponentially.

Currently only tested for iOS.

Additional context
Recently I went back through our app and increased the number of angular components used to better clean up the code base and better make use of Angular components and templating.

I noticed that some modals took an exceptionally long time to close, and began following the stack trace to see what was going on.

I followed it to a statement that calls _dialogClosed() in view-base.ts, and notice that the stack took several seconds to return from this function.

I modified the function to see roughly how many times _dialogClose() would be called:

...
    let ENTRY = undefined;
    let TOTAL_COUNT = 0;

    ViewBase.prototype._dialogClosed = function () {
        if (!ENTRY) {
            ENTRY = this;
            console.log('Beginning _dialogClosed call.');
            TOTAL_COUNT = 0;
        }

        eachDescendant(this, function (child) {
            child._dialogClosed();
            TOTAL_COUNT += 1;
            return true;
        });

        if (ENTRY === this) {
            console.log('Finished notifying children of close. Total count: ' + TOTAL_COUNT);
            ENTRY = undefined;
        }
    };
...

I opened my modal, then closed it and got the following output:

CONSOLE LOG file:///node_modules/@nativescript/core/ui/core/view-base/view-base.js:695:0: Beginning _dialogClosed call.
CONSOLE LOG file:///node_modules/@nativescript/core/ui/core/view-base/view-base.js:706:0: Finished notifying children of close. Total count: 20611107

So in my case this function was called 20611107 times, and causes a delay of about 14 seconds before the modal responds.

Another modal with essentially only an image and label on it encounter a similar issue:

Simulator Screen Shot - iPhone 11 - 2020-03-07 at 21 12 15

CONSOLE LOG file:///node_modules/@nativescript/core/ui/core/view-base/view-base.js:695:0: Beginning _dialogClosed call.
CONSOLE LOG file:///node_modules/@nativescript/core/ui/core/view-base/view-base.js:706:0: Finished notifying children of close. Total count: 872483

This modal sees about a second's worth of delay when closing.

Expected behavior
There should not be a massive performance hit and delay when closing modals with nested Angular components. Rendering these items is essentially instant, and closing the modal should be too.

(Partial) Work Around
You can get around the issue by having Angular delete most of the components before dismissing the modal.

I updated one view to use *ngIf and destroy all content under it when about to close the modal.

Component:

<StackLayout *ngIf="!closing">
      <ng-content></ng-content>
</StackLayout>

Code:

closeModal(): void {
    if (!this.closing) {
     this.closing = true;

      this._ngZone.run(() => {
       // Call in ngZone to ensure Angular is notified of the closing change.

        setTimeout(() => {
         // Delay to allow the *ngIf to update and destroy children.
          this._dismissModal();
        }, 10);
      });
    }
}

This causes the second example modal pictured above to output the following:

CONSOLE LOG file:///node_modules/@nativescript/core/ui/core/view-base/view-base.js:695:0: Beginning _dialogClosed call.
CONSOLE LOG file:///node_modules/@nativescript/core/ui/core/view-base/view-base.js:706:0: Finished notifying children of close. Total count: 43

So by destroying all components before calling dismiss it doesn't have to traverse the chain.

The issue is though if that dismissing via a swipe down gesture doesn't hit this code, so the view will get dismissed but the app will still hang while it called _dialogClosed() recursively.

I've got a workaround for that too, but that's beyond the scope of this ticket and a bit more complex.

Sample project
I will add a sample project when I get a chance, there isn't anything special though. Essentially the second modal component's elements looks like:

<StackLayout>
   <PopupPageView>
     ... // ng-content
      <LoadingView>
         ... // ng-content
         <ExampleModalContent>
            <StylingContentComponent>
               ... // ng-content
                  <Image>
                  <Label>

Only PopupPageView, LoadingView, and StylingContentComponent has an ng-content, along with a 1-2 StackLayouts within.

The ExampleModalContent represents that pictured modal.

The first modal example _dialogClosed calls had 1-2 more nested sections within it.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in nativescript-core/ui/core/view-base/view-base.ts at _dialogClosed and trace its eachDescendant traversal. Reproduce the issue with nested Angular ng-content in a non-fullscreen modal on iOS, then verify that dismissing it no longer causes millions of calls or multi-second delays, including when dismissed by swipe.

Written by the indexing model from the issue text.

Assessment

Tech stack
angular, typescript
Domain
mobile-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.