framework7io / framework7-vue

Deprecated! Build full featured iOS & Android apps using Framework7 & Vue
http://framework7.io/vue/
MIT License
674 stars 154 forks source link

Access to router from initial view #70

Closed devgeeks closed 6 years ago

devgeeks commented 7 years ago

If I have navigated to a route, this.$router is available to a page .vue component. However, the initially loaded App.vue (the template passed to new Vue() much like https://github.com/nolimits4web/Framework7-Vue-Webpack-Template/blob/a8d4f63fb45a7284c92c6af96f78741895a5fa9d/src/main.js#L33), does not seem to have access to the router.

What I am trying to accomplish is to be able to programatically call the router from that view (e.g.: redirect to a route or say redirect to a route based on an event).

I have seen some similar talk in #13, but that solution seems far far too fragile (that chain of $children[0], etc).

Any suggestions what I have missed?

devgeeks commented 7 years ago

It appears to be available at this.$f7.mainView.router. Would that be the safest place to rely on it?

bencompton commented 7 years ago

I have an open pull request that should make this better. I found that self.$parent.$parent.$router and self.$parent.$parent.$route didn't work on every component, so I replaced them with references to variables declared just before containing the router and current route respectively. With my pull request, changing a route programmatically within any component can now done like so:

$router.changeRoute('/some-path/');

It defaults to the main view, but can be used to change to a different route in any view:

$router.changeRoute('/some-path/', leftPanelView);
nolimits4web commented 7 years ago

From my point of view F7 app may have different routers (each View is a router) so the root App component doesn't have such property because it may contain different routes (Views). I've just pushed commit to support named Views (by passing name property on View initialization) #1322 https://github.com/nolimits4web/Framework7/commit/fb8713103830ca9765944d6f3b277f84d61c1617

So when you init View with, e.g., name: 'left' it will be accessible via this.$f7.views.left.router or this.$f7.leftView.router. Main view is always available on this.$f7.viewMain.router and this.$f7.views.main.router, so yes @devgeeks it is the safe option to rely on.

@bencompton what do you think about having multiple $routers?

bencompton commented 7 years ago

@nolimits4web, as long as we still have nested routes and route change events that components listen to, I'll be happy. I think having a router instance per view could be an improvement if we can make it so that only the components under a view only listen to their parent view's routing events. Right now, for example, the pages component has this code in its onRouteChange event:

if (view === self.$parent.f7View && !alreadyOnPage) {

I wouldn't mind eliminating the need to do that and not have useless route event calls on components that don't care (by the way, $parent now works perfectly in React, so no reason not to use it now).

Another good reason to have a router instance per view is if we ever link routers with the HTML5 history API. That would allow one view's router to be linked to the main browser URL, while perhaps others are not.

I can look into updating my pull request to have separate routers per view, unless you wanted to give it a try. Let me know.

bencompton commented 7 years ago

So I suppose with this change, routes would no longer be passed in like this:

   ...
    framework7: {
      root: '#app', //Should be same as app el
      animateNavBackIcon: true,
      routes: Routes,
    },
   ...

Instead, it would be done something like this:

<f7-view main url="/" :routes="mainRoutes" :dynamic-navbar="true">
...
...
<f7-view navbar-fixed name="left" url="/" :routes="leftRoutes">

That look right?

nolimits4web commented 7 years ago

@bencompton I still didn't have enough time to check your PR (hopefully will do and merge it wishing few days) but this thing with multiple routers is already here by default. As for routes specification, it should stay the same as it may be required to support same routes (pages) in different views. You can see such behavior in many apps especially the ones which use multiple views/tabs layout

bencompton commented 7 years ago

@nolimits4web I'll wait until you have had a chance to look over my PR and see what thoughts you have afterwards.

Good point--it could be annoying to have to pass in the same routes into every view when all of them need the same routes. At the same time, the current API doesn't allow each view to have a separate set of routes. Not sure that's all that big of a deal, since the routes for all views could still be combined into one routes array.

nolimits4web commented 7 years ago

@bencompton I think we may add same "routes" parameter for View, then it may work like global app routes override for the current view, or like merge for the current view. So when we parse the appropriate route we first check is it presented in View parameters and (if not) we look for it in global app routes

nolimits4web commented 7 years ago

@bencompton i've merged your PR to this branch https://github.com/nolimits4web/Framework7-Vue/tree/router. Most of things look good, i've removed 'query-string' dependency as we have built-in Dom7.parseUrlQuery. The next thing i think we need to remove 'universal-router' as a dependency as well as i can't really see the way to pack it into the plugin's file (in dist/ folder) and it is size pretty significant, i can update my previous findMatchingRoute function to look better for nested routes. But anyway, great job! 👍

nolimits4web commented 7 years ago

@bencompton i've added you as a collaborator so you can PR or commit directly to the "router" branch

nolimits4web commented 7 years ago

@bencompton and we need findMatchingRoute func to be not async as we must return "true" to the preroute if we don't find matching route

bencompton commented 7 years ago

@nolimits4web I agree with all of your concerns about universal-router and that we should get rid of it.

I also like your idea of being able to override app-level routes on each view and not taking away app-level routes. So maybe we modify changeRoute to do something like this?

...
let matchingRoute;

if (view.routes) matchingRoute = findMatchingRoute(view.routes, location);
if (!matchingRoute) matchingRoute = findMatchingRoute(this.routes, location);
...
bencompton commented 7 years ago

@nolimits4web I have been thinking about ways to replace universal-router. One idea I came up with is not to change findMatchingRoute to support nested routes, but instead to flatten the routes so that your original findMatchingRoute function can be used as-is. Tabs add a maximum of 2 levels of nesting, and other future scenarios for nested routes (modals, accordions, etc.) would be just as simple, so full-on nested route support seems like overkill.

The flattenRoutes function would to convert this...

  [
  ...
  {
    path: '/nested-routes/page-with-tabs/',
    component: PageWithTabs,
    tabs: [
      {
        path: '/',
        tabId: 'tab1',
        component: Tab1
      },
      {
        path: '/tab-2/',
        tabId: 'tab2',
        component: Tab2
      },
      {
        path: '/tab-3/',
        tabId: 'tab3',
        routes: [
          {
            path: '/',
            component: Tab3
          },
          {
            path: '/alternate-content/',
            component: Tab3AlternateContent
          }
        ]
      }
    ]
  }
  ...  
  ]   

...to this:

[
  ...
  {
    path: '/nested-routes/page-with-tabs/',
    component: PageWithTabs,
    tab: {
      tabId: 'tab1',
      component: Tab1
    }
  },
  {
    path: '/nested-routes/page-with-tabs/tab-2/',
    component: PageWithTabs,
    tab: {
      tabId: 'tab2',
      component: Tab2
    }
  },
  {
    path: '/nested-routes/page-with-tabs/tab-3/',
    component: PageWithTabs,
    tab: {
      tabId: 'tab-3',
      component: Tab3
    }
  },
  {
    path: '/nested-routes/page-with-tabs/tab-3/alternate-content/'
    component PageWithTabs,
    tab: {
      tabId: 'tab-3',
      component: Tab3AlternateContent
    }
  }
  ...  
] 
nolimits4web commented 6 years ago

Issue is closed because of outdated/irrelevant/not actual

If this issue is still actual and reproducible for latest version of Framework7 & Framework7-Vue, please create new issue and fill the issue template correctly: