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

Exception when calling canGoBack within back button listener #1887

Open NickIliev opened 5 years ago

NickIliev commented 5 years ago

@bastianjoel commented on Thu Jul 04 2019

Environment

Describe the bug Calling canGoBack() of RouterExtensions causes the following exception when using inside back button listener.

An uncaught Exception occurred on "main" thread.
com.tns.NativeScriptException: 
Calling js method onBackPressed failed

TypeError: Cannot read property 'states' of null
File: "file:///data/data/org.nativescript.preview/files/app/tns_modules/nativescript-angular/router/ns-location-strategy.js, line: 236, column: 18

StackTrace: 
Frame: function:'NSLocationStrategy.canGoBack', file:'file:///data/data/org.nativescript.preview/files/app/tns_modules/nativescript-angular/router/ns-location-strategy.js', line: 236, column: 19
Frame: function:'RouterExtensions.canGoBack', file:'file:///data/data/org.nativescript.preview/files/app/tns_modules/nativescript-angular/router/router-extensions.js', line: 51, column: 41
Frame: function:'', file:'file:///data/data/org.nativescript.preview/files/app/app.component.js', line: 26, column: 34
Frame: function:'Observable.notify', file:'file:///data/data/org.nativescript.preview/files/app/tns_modules/tns-core-modules/data/observable/observable.js', line: 110, column: 15
Frame: function:'ActivityCallbacksImplementation.onBackPressed', file:'file:///data/data/org.nativescript.preview/files/app/tns_modules/tns-core-modules/ui/frame/frame.js', line: 888, column: 25
Frame: function:'NativeScriptActivity.onBackPressed', file:'file:///data/data/org.nativescript.preview/files/app/tns_modules/tns-core-modules/ui/frame/activity.js', line: 41, column: 21

at com.tns.Runtime.callJSMethodNative(Native Method)
at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:1203)
at com.tns.Runtime.callJSMethodImpl(Runtime.java:1083)
at com.tns.Runtime.callJSMethod(Runtime.java:1070)
at com.tns.Runtime.callJSMethod(Runtime.java:1050)
at com.tns.Runtime.callJSMethod(Runtime.java:1042)
at com.tns.NativeScriptActivity.onBackPressed(NativeScriptActivity.java:59)
at android.app.Activity.onKeyUp(Activity.java:3087)
at android.view.KeyEvent.dispatch(KeyEvent.java:2722)
at android.app.Activity.dispatchKeyEvent(Activity.java:3380)
at android.support.v4.app.SupportActivity.superDispatchKeyEvent(ComponentActivity.java:108)
at android.support.v4.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:84)
at android.support.v4.app.SupportActivity.dispatchKeyEvent(ComponentActivity.java:126)
at android.support.v7.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:535)
at android.support.v7.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59)
at android.support.v7.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:2533)
at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:358)
at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:5347)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5215)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4626)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4679)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4645)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4653)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4626)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4679)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4645)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4801)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4653)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4858)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4626)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4679)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4645)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4653)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4626)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4679)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4645)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4834)
at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:4995)
at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:2602)
at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:2112)
at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2103)
at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:2579)
at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:141)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:326)
at android.os.Looper.loop(Looper.java:160)
at android.app.ActivityThread.main(ActivityThread.java:6854)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:860)

To Reproduce General:

  1. Add a android back button listener and define property tries

    application.android.on(application.AndroidApplication.activityBackPressedEvent, (args: any) => {
    if (!this.router.canGoBack()) {
        args.cancel = (this.tries++ > 0) ? false : true;
    
        setTimeout(() => {
            this.tries = 0;
        }, 2000);
    } else {
        args.cancel = false;
    }
    });
  2. Close app by using the back button twice
  3. Reopen app
  4. Tap the back button again

Expected behavior

Sample project https://play.nativescript.org/?template=play-ng&id=AO3iYR&v=343

Additional context

mayureshjadhav commented 5 years ago

I am getting this issue. Anyone has a solution?

DariosKrimsKrams commented 5 years ago

Hello, have the same problem when triggering the back-button on specific pages. (dont understand the original example with triggering twice and reopen. For me, just the "normal" workflow is breaking it.

application.android.on(application.AndroidApplication.activityBackPressedEvent, (args: any) => {
    this.ngZone.run(() => {
        args.cancel = true;
        if (this.router.canGoBack()) {
            this.router.back();
        }
    }
});
ivanff commented 4 years ago

I also have this problem!

farismohammed commented 4 years ago

facing the same problem.

001123 commented 4 years ago

I have same issue on IOS :'(

huukuntp1 commented 4 years ago

I’ve same this issue. let me know if this is resolve. thanks everyone

edusperoni commented 4 years ago

When you use the back button twice you're not completely exiting the app, but the UI is destroyed (including angular). What's happening is:

  1. You call android.on(function_on_instance1)
  2. You press back button to exit
  3. it calls function_on_instance1
  4. it exits and destroys the angular instance
  5. open app, new angular instance
  6. You call android.on(function_on_instance2)
  7. You press back button to exit
  8. it calls function_on_instance1 then function_on_instance2

Solution:

ngOnInit() {
application.android.on(application.AndroidApplication.activityBackPressedEvent, handleBackButton, this);
}
ngOnDestroy() {
application.android.off(application.AndroidApplication.activityBackPressedEvent, handleBackButton, this);
}
handleBackButton(args: any) {
    this.ngZone.run(() => {
        args.cancel = true;
        if (this.router.canGoBack()) {
            this.router.back();
        }
    }
}
flgraveline commented 4 years ago

When you use the back button twice you're not completely exiting the app, but the UI is destroyed (including angular). What's happening is:

  1. You call android.on(function_on_instance1)
  2. You press back button to exit
  3. it calls function_on_instance1
  4. it exits and destroys the angular instance
  5. open app, new angular instance
  6. You call android.on(function_on_instance2)
  7. You press back button to exit
  8. it calls function_on_instance1 then function_on_instance2

Solution:

ngOnInit() {
application.android.on(application.AndroidApplication.activityBackPressedEvent, handleBackButton, this);
}
ngOnDestroy() {
application.android.off(application.AndroidApplication.activityBackPressedEvent, handleBackButton, this);
}
handleBackButton(args: any) {
    this.ngZone.run(() => {
        args.cancel = true;
        if (this.router.canGoBack()) {
            this.router.back();
        }
    }
}

Thanks for this @edusperoni !