Skip to content
This repository was archived by the owner on May 4, 2022. It is now read-only.

DeepLinker generated URL names with tabbed pages causes double / #153

Open
morrisonbrett opened this issue Nov 3, 2016 · 39 comments
Open
Labels

Comments

@morrisonbrett
Copy link

Starting to use DeepLinker. Ran into this issue. Details simplified for brevity's sake:

I have a tab called 'users'. Here it is:

<ion-tabs class="tabs" [selectedIndex]="selectedTabIndex" tabsHighlight="true" tabsPlacement="top">
  <ion-tab [root]="UsersPageRoot" (ionSelect)="selectTab(0, UsersPageRoot)" tabUrlPath="" tabIcon="desktop"></ion-tab>
</ion-tabs>

I want the URL via DeepLinker to be localhost:8100/#/users

However, the DeepLinker will take that, inspect the tabUrlPath value, and add an extra /

The URL will be: localhost:8100/#//users

I can put a word in the tabUrlPath - let's try the word 'account'.

<ion-tabs class="tabs" [selectedIndex]="selectedTabIndex" tabsHighlight="true" tabsPlacement="top">
  <ion-tab [root]="UsersPageRoot" (ionSelect)="selectTab(0, UsersPageRoot)" tabUrlPath="account" tabIcon="desktop"></ion-tab>
</ion-tabs>

The URL will be: localhost:8100/#/account/users

So the problem is, if I put an empty string in tabUrlPath, the DeepLinker code should factor that in when generating the URL slug. The same problem happens when using the tabTitle attribute. A blank title yields an extra slash.

I hope someone can fix this. I'm pretty sure the code fix will be somewhere in here: https://github.com/driftyco/ionic/blob/504e6e0440032d5813566def22a57f0e410bc2c6/src/navigation/deep-linker.ts

@mhartington
Copy link
Contributor

This is to be expected. Since the tabs act as their own sub nav, with it's own navController instance, it gets it's own url segment. This is by design.

@morrisonbrett
Copy link
Author

Is there are a work around? I consider multiple // forced into a URL a slug a design flaw.

@mhartington
Copy link
Contributor

This is the intended design.

Since components can be loaded anywhere, saying app/user would just load the user component.
But instead, you want to load the whole tab navigation with that.
So deeplinker needs to tell the url that it's not just the user component, but the whole tabs navigation stack.

the multiple blank // is a side-effect of not using tabUrlPath property correctly.

@morrisonbrett
Copy link
Author

morrisonbrett commented Nov 7, 2016

I want full control over the URLs, and what you're saying is that it's by design. But it's actually a "design limitation". If I set tabUrlPath to be "events", I want the output URL to be: http://sitename.com/events, not http://sitename.com//events. Are you saying that it is not possible? Is there a work around?

@mhartington
Copy link
Contributor

Looking the tabs based starter with some simple links setup...

// app.module.ts
  imports: [
    IonicModule.forRoot(MyApp, {}, {
        links: [
          { component: AboutPage, segment: 'about', name: 'About' },
          { component: HomePage, segment: 'home', name: 'Home' },
          { component: ContactPage, segment: 'contact', name: 'Contact' },
        ]

      })
  ],

and a tabs.html

<ion-tabs>
  <ion-tab [root]="tab1Root" tabUrlPath="myTitle" tabTitle="Home" tabIcon="home"></ion-tab>
  <ion-tab [root]="tab2Root" tabTitle="About" tabIcon="information-circle"></ion-tab>
  <ion-tab [root]="tab3Root" tabTitle="Contact" tabIcon="contacts"></ion-tab>
</ion-tabs>

my generated url for the first component, HomePage is http://localhost:8100/#/myTitle/home

@morrisonbrett
Copy link
Author

Right. I'm saying I want it to be just http://localhost:8100/#/home. Is there are work-around? Setting tabUrlPath="" produces 2 //, which I think should be considered a bug. The code that generates the URL should look for empty string, and handle accordingly.

@mhartington
Copy link
Contributor

http://localhost:8100/#/home mean that you are just loading the HomePage component and not the whole tab navigation structure, which is not what you want in this case

Setting tabUrlPath="" produces 2 //, which I think should be considered a bug
No, this is the correct out put from incorrect usage. You're not disabling the tabUrlPath, you're just setting it's value to an empty string.

Having tabUrlPath is needed in order to determine that you are trying to load a full tabs-based navigation stack and all the navController history that comes with it.

@morrisonbrett
Copy link
Author

I need to be able to control the URLs generated, such as /events, /faq, etc. because I'm building a web site, not an app, via the browser platform. Is there a workaround?

@mhartington
Copy link
Contributor

mhartington commented Nov 8, 2016

Don't use tabs then.

If you think about how tabs are a navcontroller with their own nested navcontroller's for each tab, then this makes sense. There is no "work around" for something that is working as intended.

@morrisonbrett
Copy link
Author

I see you closed the issue. I guarantee other users in the future are going to bring it up again, and again, and again. It's going to come up. Because of your refusal to acknowledge this as a bug or serious design limitation, I'm going to have to stop using tabs, which means needless development time and money. Frustrating.

@mlynch mlynch reopened this Nov 8, 2016
@mlynch
Copy link
Contributor

mlynch commented Nov 8, 2016

Hey @morrisonbrett, there are a few things in here that we can definitely address to make it easier to control the URLs you want. Ionic's router tries to serialize the current path to that page, because pages can exist anywhere in an app, it's a major feature of Ionic 2 (think how the App Detail page in the App Store app on iOS can be loaded in all of the tabs, or on its own). However, deeplinks need to be clean and easy for the user, and then your app can decide how to display the best UI to match, regardless of the URL path back to it.

So, we need to add that.

Secondly, the double root slash shouldn't exist, so that's a bug. We will put this on the list of things to look into soon.

@MarkChrisLevy
Copy link

MarkChrisLevy commented Nov 8, 2016

@mlynch @mhartington If I understand @morrisonbrett correctly, the issue is that sometimes there is no need to add url/path change when switching between tabs... Especially if you consider the fact, that on Android (or in any browser), when each tab MUST have an url and you switch tabs (say go from tab A to tab B), then pressing the back button (not the one on app navbar) will switch back tabs, not the page... that in many situations is not acceptable (imagine, that you made tabs switch like this: tab A to tab B, tab B to C, tab C to A, tab A to B.... pressing back button will jump from tab to tab....). So either there should be a possiblity to have no url change for tab switch or ability to control how hardware back button behaves in terms of tab url change.

@morrisonbrett
Copy link
Author

So @mlynch would make sense for you to create new issues, tag them as bugs, and cross-reference them back here?

@xinbenlv
Copy link

xinbenlv commented Nov 15, 2016

+1 on prioritize this bug.
We love Ionic, because it's a chance to code once and fits all. We chose Ionic because we want to provide a consistent experience across platforms. With Ionic, we can reach to all major platforms.

However recently I have seen many times Ionic team chose the prioritization based on treating Web as a second class citizenship. When Ionic1 was there, Nokia and Blackberry were still something, Windows Phone was in the horizon. But in 2016, it wouldn't be too attractive if the only thing Ionic does well is to support two platforms, iOs and Android

When we started to evaluate Ionic, it promotes its Mobile-like User Experience with a Tab-structure. the Tab has been used by a lot of ionic developers. Telling them to "or you should just stop use Tab" doesn't seem very practical, nor very friendly to the ecosystem you worked very hard to build.

There are many solutions to the problem, such as just give developer the control to set sub navigation controller in the specific path location.

We all are developers we understand the schedule, the resource limit etc. But we really hope you prioritize in the way that best fit Ionic's market nitch.

@tleguijt
Copy link

I filed a new issue that is related to this one:

ionic-team/ionic-framework#9183

I think the reason why @morrisonbrett leaves the tabUrlPath empty is the same reason why I left segments empty in my DeepLinker config (as described in this topic: https://forum.ionicframework.com/t/how-do-i-let-tabs-play-well-with-deeplinker/67715).

I do believe this is a bug and not just a documentation issue (I believe the documentation on the DeepLinker still has to be written?).

The example I provided in the other issue is different from the example provided here, but it also concerns the DeepLinker + Tabs combination.

@morrisonbrett
Copy link
Author

@mhartington I took your advice. I ended up abandoning tabs because of this! I rolled my own "tab bar" styling, with active, etc.. Now each "tab" is a unique URL.

@Daskus1
Copy link

Daskus1 commented Dec 29, 2016

Same problem here....
I am building an App and want to deploy it as PWA as well but deeplinking is not usable with tabs.
I would want to use the tabs component but without giving it any route like @morrisonbrett .
I have not found a way to achieve this. When I leave tabUrlPath="" i get a //. It results in terrible user-experience for PWA...
I hope this is going to be fixed

@tleguijt
Copy link

@Daskus1 I've provided a quick (dirty) fix to work around this issue: ionic-team/ionic-framework#9183 (comment)

@morrisonbrett
Copy link
Author

@Daskus1 I ended up abandoning tabs and creating the tab UI equivalent myself.

@Daskus1
Copy link

Daskus1 commented Dec 29, 2016

In my opinion the Ionic Team should think about making this configurable, since a lot of people are having this specific usecase/problem with Tabs

@morrisonbrett
Copy link
Author

On a similar note, can someone please answer this? https://forum.ionicframework.com/t/ionic-2-deeplinker-angular-html5mode-urls-without/74530/

@btsiders
Copy link

I've spent about a week thinking about this for my app. We have around 180 different pages, all originating from 5 main tabs but very much intertwined. TL/DR: Tabs and DeepLinker as-is will work for us. URLs will end up with redundant data, but that's an affordable trade-off to allow us to keep separate tab navigation stacks. That's the real question that each situation will have to answer.

I think that if one's application doesn't need the separate stacks, @morrisonbrett's custom tab object solution is the best answer. To make this easier, the Ionic tab object itself really should support an option to use just one stack / one rootPage like @Daskus1 suggested. This is more of a feature request than a bug.

If, however, your app can get some benefit from the separate stacks, I think the solution is just to give in to the Department of Redundancy Department's DeepLinker URLs. I tried and tried to come up with ways to remove the redundancy in our page URLs and still pick a tab and a page with page params, but it just wouldn't work.

Well, the only way I could find for it to work would be for Ionic to give us a spot to parse the incoming URL ourselves. If my organizations tabUrlPath could be parsed by the dashboard page, I wouldn't need a URL with organizations/organization/12/dashboard and could instead just have organizations/12/dashboard, with :id/dashboard as the dashboard page path. But I also have a department page with a department dashboard and a project page with a dashboard, so all of them can't have a path of :id/dashboard unless they can all use the same component and tell which page type (tab) they are displaying. If we were able to read the current tabUrlPath as a page param on a page, we wouldn't have to say things twice. Maybe that's possible and I just haven't figured out how yet. Again, more of a feature request than a bug.

@morrisonbrett
Copy link
Author

@btsiders agree. Makes a lot of sense. Here's how my code ended up, working well. Posting here if it helps anyone:

Link Config Declaration:

export const deepLinkConfig: DeepLinkConfig = {
  links: [
    { component: OnlineCoursesPage, name: 'OnlineCourses', segment: '' },
    { component: CartPage, name: 'Cart', segment: 'cart' },
    { component: CheckoutPage, name: 'Checkout', segment: 'checkout' },
    { component: ThankYouPage, name: 'ThankYou', segment: 'thanks' },
    { component: AccountPage, name: 'Account', segment: 'account' },
    { component: RegisterPage, name: 'Register', segment: 'register' },
    { component: ForgotPasswordPage, name: 'ForgotPassword', segment: 'account' },
    { component: ForgotUsernamePage, name: 'ForgotUsername', segment: 'account' },
    { component: OnlineCoursesPage, name: 'OnlineCourses', segment: ':statename/:licensetypename/online-course-results' },
    { component: CorrespondencePage, name: 'Correspondence', segment: ':statename/:licensetypename/correspondence' },
    { component: LiveSeminarsPage, name: 'LiveSeminars', segment: ':statename/:licensetypename/search-seminars-list' },
    { component: LiveWebinarsPage, name: 'LiveWebinars', segment: ':statename/:licensetypename/webinar' },
    { component: LicensingInfoPage, name: 'Licensing', segment: ':statename/:licensetypename/licensing-info' }
  ]
};

Imports line indicating I don't want Angular type # in the URLs:

  imports: [
    IonicModule.forRoot(MyApp, { locationStrategy: 'path' }, deepLinkConfig)
  ],

Logic that I put in my platform.ready() to parse the URL the user clicked to enter the site. Properly set some globals, which manage the segment variables in the URL path:

          // Take a look at the query params.  If they exist, override the values from storage
          console.log('Segments', this.nav._linker.segments);
          if (this.nav._linker.segments && this.nav._linker.segments[0]) {
            let split = this.nav._linker.segments[0].id.split('\/');
            console.log('Split', split);

            // See if the first split is a valid state
            if (split[0]) {
              let argStateCode = Globals.stateCodeByStateName(split[0]);
              if (argStateCode.length > 0) {
                Globals.stateCode.next(argStateCode);
                this.storage.set('StateCode', argStateCode);
              }
            }

            // See if the second split is a valid license type
            if (split[1]) {
              let argLicenseType = Globals.licenseTypeByLicenseName(split[1]);
              if (argLicenseType.length > 0) {
                Globals.licenseTypeId.next(argLicenseType);
                this.storage.set('LicenseTypeId', argLicenseType);
              }
            }
          }

@btsiders
Copy link

Interesting. I wouldn't think three pages would work with the same 'account' segment, though.

@mhartington
Copy link
Contributor

Hi. would you mind moving the conversation to the forum.
Thanks.

@damirarh
Copy link

@mhartington I modified a bit your links setup for the tabs based starter:

app.module.ts:

  imports: [
    IonicModule.forRoot(MyApp, {}, {
        links: [
          { component: AboutPage, segment: 'about', name: 'About' },
          { component: HomePage, segment: 'home', name: 'Home' },
          { component: ContactPage, segment: 'contact', name: 'Contact' },
        ]
      })
  ],

tabs.html

<ion-tabs>
  <ion-tab [root]="tab1Root" tabUrlPath="myTitle" tabTitle="Home" tabIcon="home"></ion-tab>
  <ion-tab [root]="tab2Root" tabUrlPath="myOtherTitle" tabTitle="About" tabIcon="information-circle"></ion-tab>
  <ion-tab [root]="tab3Root" tabTitle="Contact" tabIcon="contacts"></ion-tab>
</ion-tabs>

When I try to open http://localhost:8100/#/myOtherTitle/about, the app loads http://localhost:8100/#/myTitle/home. Don't these URLs act as a way to navigate directly to a specific page? Am I missing something?

@DarrenHou1993
Copy link

@damirarh I had a some problem! did you solve it?

@damirarh
Copy link

damirarh commented Feb 5, 2017

@DarrenHou1993 no, not yet.

@5im0n
Copy link

5im0n commented Feb 7, 2017

I have similar problem with my application

links: [
    { component: TabsPage, segment: 'tabs' },
    { component: HomePage, segment: 'home-page' },
    { component: AboutPage, segment: 'about-page', useDefault: true },
    { component: CustomerPage, segment: 'customer-page' },
    { component: CustomerDetailPage, segment: 'customer-detail', defaultHistory: [ContactPage] },
    { component: CustomerDetailDeepPage, segment: 'customer-detail-deep', defaultHistory: [ContactPage, CustomerDetailPage] }
]
<ion-tabs>
  <ion-tab [root]="tab1Root" tabUrlPath="home-tab"  tabTitle="Home" tabsHideOnSubPages="true" tabIcon="home"></ion-tab>
  <ion-tab [root]="tab2Root" tabUrlPath="about-tab"  tabTitle="About" tabsHideOnSubPages="true" tabIcon="information-circle"></ion-tab>
  <ion-tab [root]="tab3Root" tabUrlPath="customer-tab"  tabTitle="Customers" tabsHideOnSubPages="true" tabIcon="contacts"></ion-tab>
</ion-tabs>

My main page is the Tabs Page, with three tab (Home, About, Contact).
When I navigate deeper in the customer detail page and I press F5 to reload the page, the history of the CustomerDetailDeepPage isn't created.
Furthemore I see the Tabs even if I set the option tabsHideOnSubPages.

Maybe I miss something ?

@damirarh
Copy link

damirarh commented Feb 7, 2017

@DarrenHou1993, thanks to @5im0n I finally managed to solve my problem. It didn't work for me because I didn't have TabsPage in my deep link configuration. Also, each ion-tab in single ion-tabs requires a unique tabUrlPath (in my sample above that's true, but it wasn't in my actual app).

Here's a working configuration:

app.module.ts:

  imports: [
    IonicModule.forRoot(MyApp, {}, {
        links: [
          { component: TabsPage, segment: 'tabs', name: 'Tabs' },
          { component: AboutPage, segment: 'about', name: 'About' },
          { component: HomePage, segment: 'home', name: 'Home' },
          { component: ContactPage, segment: 'contact', name: 'Contact' },
        ]
      })
  ],

tabs.html:

<ion-tabs>
  <ion-tab [root]="tab1Root" tabUrlPath="home-tab" tabTitle="Home" tabIcon="home"></ion-tab>
  <ion-tab [root]="tab2Root" tabUrlPath="about-tab" tabTitle="About" tabIcon="information-circle"></ion-tab>
  <ion-tab [root]="tab3Root" tabUrlPath="contact-tab" tabTitle="Contact" tabIcon="contacts"></ion-tab>
</ion-tabs>

While generated URLs aren't as nice any more, navigation now works correctly.

@billygerhard
Copy link

+1 to prioritizing this as well. I was just trying to create a tabbed app with DeepLinkConfig, and I just ran into this issue. I'm glad I found it early on in my development so I don't waste my time on working on an issue that doesn't have a resolution yet.

@rdlabo
Copy link

rdlabo commented Mar 2, 2017

I worry @5im0n 's trouble too.
What can I resolve defaultHistory under tabs??

@5im0n
Copy link

5im0n commented Mar 2, 2017

@rdlabo, I've open an issue for this trouble #10356.

@rdlabo
Copy link

rdlabo commented Mar 2, 2017

@5im0n thanks!! If it can, I will use DeepLinks in PWA!!
(Now Don't support URL routing...)

@lesrpo
Copy link

lesrpo commented Jun 14, 2017

Hello guys!,
I have the same issue with the double '//' and I am working with ionic 3, are you guys sorting out this issue soon? @jgw96 @adamdbradley @ihadeed

Thanks for your support. Really appreciate your work!

@jgfet
Copy link

jgfet commented Aug 21, 2017

@mhartington Can you look at the way Grails handles deep linking perhaps? Try following their lead. Because when I set root to HomePage, why am I getting some sort of ghost URL state from last time I ran the app?. I want to be at http://localhost:8100/ not http://localhost:8100/#//jobs/jobs. Or something akin to that. I can't even overwrite url in browser, it just comes back the same. This is not intuitive. Is there a detailed blog post you can refer me to so I can get my head around your way of thinking? Then perhaps I can find a way to work around these issues.

@ntziolis
Copy link

ntziolis commented Nov 16, 2017

I completely understand why this is by design and I agree, but I do feel there is a way to allow for this without breaking the design paradigm:

It would be sufficient if we were able to either:

  • provide a custom deeplink configuration for each tabs page and its respective NavController (fallback being the parents deeplink config)?
  • or allow to provide a global nested deeplink configuration that allows to specify deeplink configurations that are only valid for the specified url path

This would:

  • allow to specify segments for pages in the case they are loaded from this specific tabs page
    • to remove the duplicate data in url issue
      • /client/client-page
  • allow for sensible links when linking directly to the page (defined via segments or global deeplink config)
    • /client-page
  • not break existing code

In addition it would also be consistent with the idea that the tab control has a separate navigation stack. who is to say that nav stack need to follow the same configuration as its parent while this should be the fallback.

I don't know enough about the life cycle of the NavController works and how it gets its deeplink information. Any comment from ionic side what it would take?

@ntziolis
Copy link

ntziolis commented Nov 16, 2017

Another approach would be:

  • to extend the deep linker config to specify a unique-segment string for each component in addition to the segment info
  • the field would not be required and the segment value would be the fallback
  • but if such a unique segment value is specified
    • the unique segment value would be used as url path when trying to navigate to a pagedirectly
    • the segment value would be used when called from a tabs page
      • I'm is sure there is some way to figure this out in flight, that said i think this is the critical piece that I do not have enough understanding of
  • in addition it should be allowed to disable direct access via an additional field in the config like disable-direct-access
    • right now the default is that they are all accessible directly if you specify segments for them as far as I understand which might is not always be desired in a web scenario

Again this wouldn't break existing code user code, and because every page has a unique name it wouldn't break ionic code either. So question is if its possible to figure out in flight if a page is loaded from a tabs page.

The biggest advantage of this approach is that it just extend existing configuration in a simple way to give devs more control incl disabling direct access as well as that it can be configured centrally as well as per page.

I really like the ionic navigation and would love to use it for all our app + web projects (great to have one toolchain), but url control is a client facing issue in web projects. So its not about that devs want things their way (maybe a little :)), but for us its about constant complaining from customers, c level etc. I understand that ionic is currently mostly focused on mobile but this seems like a rather small feature request to make the platform that much more useful for web devs.

@xinbenlv
Copy link

Still open?

@ionic-team ionic-team locked and limited conversation to collaborators Feb 10, 2018
@ionic-team ionic-team unlocked this conversation Nov 28, 2018
@brandyscarney brandyscarney transferred this issue from ionic-team/ionic-framework Nov 28, 2018
@ionitron-bot ionitron-bot bot added the triage label Nov 28, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests