WJosh Vetter
WJosh Vetter

Reputation: 37

Nativescript Angular Routing from 1 Child module to another in a Tabs setup

I am using a <page-router-outlet></page-router-outlet> and <BottomNavigation></BottomNavigation> setup for tabs in my Nativescript Angular project, and am having trouble navigating from 1 child tab route to another child tab route.

So here is the app-routing.module.ts:

const routes: Routes = [
    { path: '', redirectTo: '/auth', pathMatch: 'full' },
    { path: 'auth', component: AuthComponent },
    { path: 'individual-tasks', component: SpecificTasksComponent },
    {
        path: 'tabs',
        component: TabsComponent,
        children: [
            {
                path: 'feed',
                loadChildren: '~/app/pages/feed/feed.module#FeedModule',
                component: NSEmptyOutletComponent,
                outlet: 'feedTab'
            },
            {
                path: 'notification',
                loadChildren: '~/app/pages/notification/notification.module#NotificationModule',
                component: NSEmptyOutletComponent,
                outlet: 'notificationTab'
            },
            {
                path: 'create',
                loadChildren: '~/app/pages/create/create.module#CreateModule',
                component: NSEmptyOutletComponent,
                outlet: 'createTab'
            },
            {
                path: 'profile',
                loadChildren: '~/app/pages/profile/profile.module#ProfileModule',
                component: NSEmptyOutletComponent,
                outlet: 'profileTab'
            }
        ]
    }
];

And I am currently trying to navigate from within the create tab module to the feed tab module. Here is the create-routing.module.ts:

const routes: Routes = [
    { path: '', redirectTo: 'create', pathMatch: 'full' },
    { path: 'create', component: CreateComponent },
    { path: 'create-tasks', component: CreateTasksComponent },
    { path: 'create-preview', component: CreatePreviewComponent }
];

So if I am currently within the create-preview route how can I navigate back to the "tabs/feed" outlet thats in the app-routing.module.ts?

I have been trying this:

        this.router.navigate([
            '../tabs', {
                outlets: { feedTab: ['feed'] }, relativeTo: this.activatedRoute
            }
        ]);

but even though I am explicitly writing that the navigation should be to the feedTab, it still navigates to the starting outlet (profile) instead of the feed outlet. It is like the outlet stated is entirely ignored... Any ideas???

Upvotes: 0

Views: 1214

Answers (3)

RcoderNY
RcoderNY

Reputation: 1764

One of the comments asked to clarify how to use a BehaviorSubject for this purpose, so I'll post this code in case it can help someone in the future. This will show how to use a rxjs BehaviorSubject to listen for tab changes and also provide a way to change the current tab of the BottomNavigation from anywhere within the app using this service.

The purpose of this service is to provide a central place where the BottomNavigation UI element can be accessed throughout the app. It can:

  • Use an ElementRef of BottomNavigation so the nativeElement can be accessed to get or change the current tab of the BottomNavigation element.
  • Provides an rxjs BehaviorSubject observable that allows consumers of this service to subscribe to and then be notified of BottomNavigation selectedIndexChanged events. The newIndex and oldIndex are emitted from this rxjs BehaviorSubject observable on every BottomNavigation selectedIndexChanged event.

NOTE: In the component that has the BottomNavigation element in its template (app.component.ts in this example), it must give this NavigationService the reference to the BottomNavigation it needs in its ngAfterViewInit life cycle hook like: this._navigationService.bottomNavigationRef = this.navRef; (see snippet in app.component.ts below)

// navigation.service.ts

import { ElementRef, Injectable, OnDestroy } from '@angular/core';

import { BottomNavigation, SelectedIndexChangedEventData } from '@nativescript/core';

import { BehaviorSubject, Subscription } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class NavigationService implements OnDestroy {
  private _bottomNavigationRef: ElementRef<BottomNavigation>;
  private _subscription: Subscription;
  private callbackSelIndexChgEv;
  /** rxjs BehaviorSubject observable to track the current tab of the BottomNavigation */
  bottomNavigationTab$: BehaviorSubject<{ newIndex: number; oldIndex: number }>;

  constructor() {
    // Must initialize rxjs BehaviorSubject observable with initial value.
    this.bottomNavigationTab$ = new BehaviorSubject({
      newIndex: -1,
      oldIndex: -1,
    });

    // Logs the current tab per this service.
    this._subscription = this.bottomNavigationTab$.subscribe((value) => {
      console.log(
        `NavigationService -> The BottomNavigation current tab index is now:
            newIndex: "${value.newIndex}"
            oldIndex: "${value.oldIndex}"`
      );
    });
  }

  ngOnDestroy(): void {
    this._subscription.unsubscribe();
    this._bottomNavigationRef.nativeElement.off(
      BottomNavigation.selectedIndexChangedEvent,
      this.callbackSelIndexChgEv
    );
  }

  get bottomNavigationRef(): ElementRef<BottomNavigation> {
    return this._bottomNavigationRef;
  }

  set bottomNavigationRef(bottomNavRef: ElementRef<BottomNavigation>) {
    this._bottomNavigationRef = bottomNavRef;

    this.callbackSelIndexChgEv = (
      $event: SelectedIndexChangedEventData
    ): void => {
      /* Update the current tab of the rxjs BehaviorSubject Observable */
      this.bottomNavigationTab$.next({
        newIndex: $event.newIndex,
        oldIndex: $event.oldIndex,
      });
    };

    this._bottomNavigationRef.nativeElement.on(
      BottomNavigation.selectedIndexChangedEvent,
      this.callbackSelIndexChgEv
    );
  }
}

// app.component.ts (partial file)

// ...
 @ViewChild('bottomNav') navRef: ElementRef<BottomNavigation>;
// ...
ngAfterViewInit(): void {
    // Gives the NavigationService the reference to the BottomNavigation it needs.
    this._navigationService.bottomNavigationRef = this.navRef;
}
// ...
<!-- app.component.html (partial file just to show #bottomNav) -->
<BottomNavigation #bottomNav>
<!-- ... -->
</BottomNavigation>
// another.component.ts

import { Component, OnDestroy, OnInit } from '@angular/core';

import { Subscription } from 'rxjs';

import { NavigationService } from './navigation.service';

@Component({
  selector: 'app-another-component',
  templateUrl: './another.component.html',
  styleUrls: ['./another.component.scss']
})
export class AnotherComponent implements OnDestroy, OnInit {
  private _subscription: Subscription;

  constructor(private _navigationService: NavigationService) {}

  ngOnInit(): void {
    // Example using BehaviorSubject Observable:
    this._subscription = this._navigationService.bottomNavigationTab$.subscribe(
      (selectedTab) => {
        console.log(`This component knows that the BottomNavigation current tab is now: ${selectedTab.newIndex} and the old tab was: ${selectedTab.oldIndex}`);

        if (selectedTab.newIndex === 2) {
          // do something ...
        }
      }
    );
  }

  ngOnDestroy(): void {
    // unsubscribe from BehaviorSubject Observable
    this._subscription.unsubscribe();
  }

  // Example changing the BottomNavigation tab from another component:
  changeTab(tab: number): void {
    this._navigationService.bottomNavigationRef.nativeElement.selectedIndex = tab;
  }

}

rxjs BehaviorSubject documentation: https://www.learnrxjs.io/learn-rxjs/subjects/behaviorsubject

Upvotes: 2

Manoj
Manoj

Reputation: 21908

I don't think the router gives you the ability to switch tabs. You will have to update selectedIndex of BottomNavigation and then navigate to particular tab you like.

To update selectedIndex from child component, use a service with a BehaviorSubject. Listen to the subject from parent component, update the value from child component.

Upvotes: 1

Ganesh
Ganesh

Reputation: 6016

Try this :

You need to remove this.activatedRoute from navigate(), which is assigning current route before the routing path which you have provided now.

 this.router.navigate(['../tabs/feed', {
       outlets: {  primary: ['feed'], feedTab: ['feed'] }
    }
 ])

you can read more about outlets here and follow this blog for more details.

Hope this helps.. :)

Upvotes: 0

Related Questions