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

Nested page-router-outlet loses parent component #969

Open tsonevn opened 7 years ago

tsonevn commented 7 years ago

From @Burgov on August 30, 2017 19:50

Please, provide the details below:

Did you verify this is a real problem by searching Stack Overflow and the other open issues in this repo?

yes

Tell us about the problem

Nested page-router-outlet loses parent component

Which platform(s) does your issue occur on?

Android (haven't tested on iOS)

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 the following route config:

routes = [
    {
        path: "",
        component: BaseComponent,
        canActivate: [ AuthGuard, AccountGuard, SiteGuard ],
        children: [
            { path: "", component: HomeComponent },
            { path: "scan", component: ScanComponent },
        ]
    }
]

In AppComponent (root) template I have <page-router-outlet></page-router-outlet>.

In BaseComponent I enable the RadSideDrawer, so that it's visible in all child components.

This works fine, as long as I include <router-outlet></router-outlet> in the BaseComponent template. However, if I do that, I don't receive any page events, and thus can not execute any actions when a page is loaded.

If I put <page-router-outlet></page-router-outlet> in the BaseComponent template, that part works, the events are emitted, but as soon as I leave the first page, the RadSideDrawer is gone!

Is there code involved? If so, please share the minimal amount of code needed to recreate the problem.

See above

Copied from original issue: NativeScript/NativeScript#4779

giorgiopiatti commented 7 years ago

It's a real problem, because using only as solution we can't managing goBack methods or all the nice Router's features of nativescript.

vakrilov commented 7 years ago

The <page-router-outlet> is not intended to be nested. The reason for that is that page navigation is global and only one - thus nesting will lead to different outlets dealing with the same page navigation stack.

Your initial approach (nesting <router-outlet> inside components loaded trough <page-router-outlet>) is the correct one. You will also have the benefit of having only one side-drawer component (instead of having to include it in many pages).

As far as I understand the problem in this approach is not receiving page events (after all all components loaded inside the <router-outlet> are shown on the same page). You can try using the angular lifecycle hooks (ex. ngOnInit()), the activate and deactivate events of the router-outlet or the events of the Router to execute app logic.

dvdbrk commented 7 years ago

@vakrilov I have faced the same issue, and your answer does clarify this a bit.

Correct me if I am wrong, but it means that all the wonderful things that come out of the box when using page-router-outlet (back button, remembering scroll position on the previous screen, screen transitions, component caching) - we actually can't use in any fairly complicated app?

However, the setup below does work for the initially, first only, loaded component. All shared components are present on first lazy loaded route no matter how deep in app it is. BUT as soon as you navigate to any other route, wanting to replace content inside the page-router-outlet - that does not work, the entire screen is replaced, shared-components below are lost. If you navigate back to the same route (first route) the shared components are shown again.

app.components.html

<GridLayout rows="*, *">

  <StackLayout row="0">
    <page-router-outlet></page-router-outlet>
  </StackLayout>

  <StackLayout row="1">
    <shared-component-1></shared-component-1>
    <shared-component-2></shared-component-2>
    <shared-component-3></shared-component-3>
    <shared-component-4></shared-component-4>
    <shared-component-5></shared-component-5>
  </StackLayout>

</GridLayout>

Is this all expected behaviour?

If so, a word of warning for others, do not even try to put all those shared components on each separate screen / component, as that will slow down navigation to more than 3, 4 seconds per screen (apart from going back)... Rather unusable.

mxth commented 7 years ago

@vakrilov the problem is not only about page events. If you navigate between routes inside side-drawer (routes plugged in router-outlet), you cannot use the back button.

Like you said:

after all all components loaded inside the <router-outlet> are shown on the same page

What I want here is routes iniside side-drawer content would behave as different pages.

Burgov commented 7 years ago

I ended up not using nested routes. Instead, each of my pages is wrapped within a component that renders the action bar and handles the side drawer. The back button works perfectly (including the caching of pages).

aks1994 commented 6 years ago

Is there any solution to this? I am hoping to have tabs within my application which remain at the bottom of the page and then have separate navigation stacks within each tab. I would ideally like to have page navigation within the tabs so that we can use the left-edge to right swipe gesture on iOS or back button on Android to move between pages within a tab. One way is to put the tabs on each page but then the tabs will also move and get replaced during each navigation. Several apps like Facebook, linkedin etc allow only partial navigation of the page (i.e. some components that remain permanently on screen) so was wondering if this was possible with nativescript

hackerunet commented 6 years ago

I have a repository to try to find a solution to this but as far as I get more and more troubles I find, once is i cannot navigate back to the main parent router outlet components, maybe adding routes to the main component and adding an outlet inside its routes declaration will do the work, any thoughts or help will be much appreciated. https://bitbucket.org/hackerunet/radsidedrawer/src/master/

hackerunet commented 6 years ago

I finally solved the problem, nested page router outlets, parent / child navigation and main menu over the action bar. https://bitbucket.org/hackerunet/radsidedrawer/src/master/

Burgov commented 6 years ago

@hackerunet that was our solution in the end as well. Rendering the action bar and side drawer in each and every page as I described on Dec 1st proved to be very inefficient, and with the introduction of NS 4.0 no longer necessary.

People discourage nested page-router-outlets, but it seems to be the best way forward.

Nesting a router-outlet inside a page-router-outlet kills all the native navigation support.

hackerunet commented 6 years ago

interesting, I'm still getting through this and struggling a lot because I have one application that is not going to be updated and will remain using angular 5 core and nativescript core 4.2 (affortunately this last was updated) but the page-router-outlet is not working properly neither with another nested page-router-outlet nor with a page-router-outlet as parent and a router-outlet as child, so, I'll be trying to figure it out but seems to me that I'm not going to get anywhere.

adver89 commented 3 years ago

@hackerunet Do you still have these sources? Link returns 404