NativeScript / nativescript-angular

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

Routing breaks when using Frame.goBack(to: BackstackEntry) #1367

Open tsonevn opened 6 years ago

tsonevn commented 6 years ago

From @abinici on March 21, 2017 20:39

Tell us about the problem

Lets say we have an app that consists of 4 pages, and lets name these pages page1, page2, page3 and page4. And lets say that we have navigated from page1 to page4, visiting page2 and page3 along the way.

Now, I wish to go back to page2 using Frame.goBack(to: BackstackEntry) by looking up page2 this way frameModule.topmost().backStack[1]. Then if I try to navigate to page3 nothing will happen. Navigating back to page1 works, but navigating forward still doesn't work.

If, instead of navigating to page2, I navigate to page1 or page3, routing will not break.

Which platform(s) does your issue occur on?

Both

Please provide the following version numbers that your issue occurs with:

Please tell us how to recreate the issue in as much detail as possible.

I have created a sample app that reproduces this issue: https://github.com/abinici/ns-goback-issue

Copied from original issue: NativeScript/NativeScript#3848

tsonevn commented 6 years ago

Hi @abinici, Thank you for your interest in NativeScript.

This is something expected when using FrameModules in this way in NativeScirpt Angular 2 project.

Using frameModule for navigation is a concept used in NativeScirpt core project. Regarding this module, the purpose of goBack() method is to give you simple way to navigate to the previous page. In case you would like to make navigation in a pure NativeScirpt project, you should use navigate(<page_name>) method, where you should specify the page name.

In your case, when you are using NativeScript Angular 2 template, there is another way to making navigation. First of all to in this type of project you should use angular Router module or RouterExtensions, which extends the angular one. For example:

Navigate to another page.

import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import {RouterExtensions} from "nativescript-angular/router"

import frameModule = require("ui/frame");

@Component({
    moduleId: module.id,
    selector: "page4",
    templateUrl: "./page4.component.html",
})
export class Page4Component implements OnInit {
    constructor(
        private route: ActivatedRoute,
        private router: Router, 
        private routerExt:RouterExtensions
    ) {}

    ngOnInit(): void {

    }

    goback() {
        this.router.navigate(["page2"]);
    }
}

Navigate to the previous page.

import { Component, OnInit } from "@angular/core";
import { ActivatedRoute, Router } from "@angular/router";
import {RouterExtensions} from "nativescript-angular/router"

import frameModule = require("ui/frame");

@Component({
    moduleId: module.id,
    selector: "page4",
    templateUrl: "./page4.component.html",
})
export class Page4Component implements OnInit {
    constructor(
        private route: ActivatedRoute,
        private router: Router, 
        private routerExt:RouterExtensions
    ) {}

    ngOnInit(): void {

    }

    goback() {

        this.routerExt.back();
    }
}

For further info, you could also review the documentation.

tsonevn commented 6 years ago

From @abinici on March 23, 2017 9:55

Hi @tsonevn,

I understand that the routing in Navigation Core and Angular is different. But Nativescript Angular is missing the functionality I needed. Thats why I began using Framemodule, because it had the method goBack(to: BackstackEntry), which I was hoping could solve my problem.

And to elaborate this, I dont want to go to previous page. I wish to go 2 or more pages back in history. So, when I navigate from page4 to page2, I want the pages page4 and page3 removed from the stack, in one single action. How can this be accomplished with Nativescript Angular?

I am guessing that the only way resolve this with Nativescript Angular is by calling routerExt.back() multiple times, but this is not optimal.

I think methods like these would help a lot: routerExt.goBack(url: string) or routerExt.goBack(steps: int)

tsonevn commented 6 years ago

Hi @abinici, You could still use NativeScirpt RouterExtensions module and its navigate method in combination with clearHistory option, which will allow you to disable saving the page to the back stack history. For example:

login() {
    // app logic here ...
    this.routerExtensions.navigate(["/main"], { clearHistory: true });
}

For more info review the documentation.

Hope this helps.

tsonevn commented 6 years ago

From @abinici on March 28, 2017 13:34

Hi @tsonevn,

I tried your suggestion, when navigating from page2 to page3, but then my back navigation button disappears on page3, which prevents the user from navigating back to page2.

What I am trying to accomplish, is that the pages that the user navigates to, such as page2, page3 and page4, should all be put on the stack so that user is not prevented from navigating back from page4 to page3 and page2.

But when hitting a button on page4, I would like the user to be taken back to page2 without having to visit page3.

The function I am looking for is something like popToRoot in Ionic2: http://ionicframework.com/docs/v2/api/navigation/NavController/#popToRoot

You use setRoot() to set a root for the current navigation stack. And when calling popToRoot() you go back multiple levels in your page hierarchy.

Currently, I am forced to use Frame.goBack(to: BackstackEntry) and then I have to call pop() x times in this.routerExtensions.locationStrategy, to keep things in sync (I have added a pop-method to NSLocationStrategy). Obviously, this is an ugly hack, but I dont know the implementation well enough to make things the pretty way.

Maybe you could give me some directions so that I or someone else could implement this functionality in a PR, with something like back(steps: int) or back(url: string)?

tsonevn commented 6 years ago

From @abinici on March 28, 2017 14:15

Maybe this issue should be moved to https://github.com/NativeScript/nativescript-angular?

tsonevn commented 6 years ago

Hi @abinici, Indeed this is a valid feature - an ability to navigates back to the specific page. While building the router navigation in NativeScript Angular 2 project, we have not thought about supporting this feature. Regarding that, I will change the label of the issue to Feature request and we will research, what is the best way to implement this functionality for NativeScript Angular 2.

In the meantime, you could call goBack() method to navigate back to the needed page as you have suggested in your previous comment.

You could also keep track on the issue for further.

tsonevn commented 6 years ago

From @phattranky on April 24, 2017 8:51

Need same feature like @abinici :)

tsonevn commented 6 years ago

From @jogboms on July 12, 2017 21:32

This feature would make so much sense if added. I had to use a workaround when I needed a similar feature. Using the navigationFrom and checking for BackButton navigation to route to the desired page. Angular's implementation of skipLocationChange would have simply done the much-needed trick.

tsonevn commented 6 years ago

From @cindy-m on January 29, 2018 13:21

Is there any indication when this feature will be available? We would also really like to use it instead of using tricks with the clear history and self made back buttons or other hacks to get it to work as intended

tsonevn commented 6 years ago

From @bhavincb on April 5, 2018 7:1

+1

tsonevn commented 6 years ago

From @ThunderAnimal on June 4, 2018 15:54

+1

tsonevn commented 6 years ago

From @ThunderAnimal on June 4, 2018 16:19

@abinici how is you pop method looks like? I want to solve the issue with the same workaround. But actually I have no Idea how to solve it, first i was thinking to just pop the state array, but it is not working pretty well :D

One solution to extend the NSLocationSrategy without change the file itself could be that:

(<any>NSLocationStrategy.prototype).pop = function() {
            // YOUR CODE TO POP 
        };

(<any>this._router.locationStrategy).pop();
FBorgonjen commented 6 years ago

For our app we depended on back navigation to a specific page in history without editing any of the core classes. It took a while to figure out the exact choreography of all events that are triggered, but this is the solution we came up with:

public navigateBackTo(depth: number): void {
  let largeBackStack: boolean = false;
  const arrayLength: number = this.routerExtensions.frame.backStack.length;
  this.routerExtensions.frame.backStack.map((backstackEntry: BackstackEntry, index: number) => {
    if (index > depth && backstackEntry.resolvedPage) {
      const page: any = backstackEntry.resolvedPage;
      setTimeout(() => {
        if (page.isLoaded) {
          page.onUnloaded();
        }
        if (index + 1 === arrayLength && !largeBackStack) {
          page.onNavigatedFrom(true);
        } else {
          largeBackStack = true;
          page.onNavigatedFrom(false);
        }
      }, index - depth * 100);
    } else if (index === depth) {
      this.routerExtensions.frame.goBack(backstackEntry);
    }
  });
}

We are still updating the code to move the check for largeBackStack outside the lambda method, but in the meantime this is a solution that works like a charm for us.

Hope this works for you guys as well

ThunderAnimal commented 6 years ago

@FBorgonjen thanks for your code sharing.

Meanwhile I just solved my problem by re-order my navigation structure and using <router-outlet> with routerExtensions.backToPreviousPage() or just routerExtensions.back().

The Navigation is now more straight forward and consistens for the users. But I saved your code, for one day when i really need to go back more the one page. So thanks!

Tukkan commented 6 years ago

@ThunderAnimal it's more straightforward but haven't you lost animation when routing and the ability to swipe back?

ThunderAnimal commented 6 years ago

@paweldziura yeah that's right, the native page animation disappeared. But with Angular you can just define own animations for router navigation. And so it looks - in my specific case - much better, because now only some content of the page will replaced with a nice animation and not the hole page.

FBorgonjen commented 5 years ago

Is there any estimation on when this feature will be implemented? Our solution as proposed above is breaking when navigating back more than 3 pages.

relez commented 5 years ago

I am looking forward to use this feature, any updated? Thanks!

Johanneke commented 5 years ago

I'm also waiting on this feature to be implemented. I'm currently using one of the hacks described above, but I would love to throw away the difficult-looking code and replace it with a simple goBackTo or something like it.

AliKalkandelen commented 5 years ago

I am trying to this exact thing for our application. I am trying to create a breadcrumbs that the user will be able to travel to any page they have previously been on. I am running into the same problem where if i travel back to a page i wont be able to navigate forward from that point on anymore. I'm going to try out @FBorgonjen's code and see if it helps me.

FBorgonjen commented 5 years ago

@AliKalkandelen, as said on September 3rd the proposed method is not always working when navigating back more than 3 pages. But if that is not your case, it works :) Good luck implementing!

@tsonevn, is there any news on this issue. Will it be fixed? I am actually quite surprised that Nativescript has reached version 5.0 (and NS-Angular 6+) and there are still basic navigation flows missing in the code. Especially since this feature is requested on March 21, 2017 20:39

tsonevn commented 5 years ago

Hi @FBorgonjen, This feature request is still under review. I can not commit to the exact time when this functionality will be available. I would suggest keeping track on the issue for more info.

FBorgonjen commented 5 years ago

Hi @tsonevn, Thank you for your reply. I've created a NativeScript Playground example showing the same problem with this back navigation issue for iOS. For me this is a perfect example of a basic navigation flow which I would have expected to be in Nativescript-Angular https://play.nativescript.org/?template=play-ng&id=1b2EFM&v=3 Usecase:

mat1th commented 5 years ago

@tsonevn I'm currently working on an app that has many pages. But this bug hinders me in the development of the app. So I'm hoping you can give me some a time when this will be implemented. You have mentioned multiple times that the feature request is under review, but now it is already 3/4 year ago since your last mention on this. (And 2 years ago since the first time someone requested this feature.)

I'll think with implementing this feature will serve the community. The first request is already from 2017, https://github.com/NativeScript/NativeScript/issues/3848. And there are more people wanting this feature see for example https://github.com/NativeScript/nativescript-angular/issues/1167.

Thank you for your effort in the project!

james-wynne-dev commented 4 years ago

If I log: this.routerExtensions.locationStrategy.toString() then I get: primary.2.[PAGE].[BASE] "the-component-name" primary.1.[PAGE].[BASE] "home" primary.0.[PAGE].[BASE] "information"

So the name of the components is stored in locationStrategy, but I cannot work out how to access the names properly; I would assume they are held in some kind of list. Please, how do I do this?

edusperoni commented 4 years ago

@room101b NsLocationStrategy has an array of outlets (locationStrategy.outlets, private. Your example only has primary outlet), which has an array (used as a stack) of LocationState (2,1,0), which has isPageNavigation ([PAGE]) and modalNavigationDepth([BASE]), and segmentGroup (the string you see https://angular.io/api/router/UrlSegmentGroup).

Each navigation is pushed into the current outlet and then popped out on back. If you want to keep track of the page you're in, I'd recommend listening to angular navigation events.

If anyone is willing to contribute to this issue, this would be a good starting point.

Bear in mind that NSLocationStrategy has a function back(outlet?: Outlet, frame?: Frame): void in which it will try to pop states until it reaches the desired frame (you may need to work with _beginBackPageNavigation and _finishBackPageNavigation).

prabudevarrajan commented 4 years ago

Any update or workaround on this feature?