Routes

    Routes defined on app init are default routes, they will be available for any View/Router in the app.

    If you have a multi-view/router app and you want to have some View/Router to have own strict routes and don’t want default routes be available in this , then you may specify the same routes parameter on View init:

    1. var view1 = app.views.create('.view-1', {
    2. routes: [
    3. {
    4. path: '/users/',
    5. url: './pages/users.html',
    6. },
    7. {
    8. path: '/user/',
    9. url: './pages/user.html',
    10. },
    11. ],
    12. });

    If you have a multi-view/router app and you want to have some View/Router to have additional routes and don’t want these additional routes are available in other Views, then you may specify the routesAdd parameter on View init:

    1. // This view will support all global routes + own additional routes
    2. var view2 = app.views.create('.view-2', {
    3. // These routes are only available in this view
    4. routesAdd: [
    5. {
    6. path: '/blog/',
    7. url: './pages/blog.html',
    8. },
    9. {
    10. path: '/post/',
    11. url: './pages/post.html',
    12. },
    13. ],
    14. })

    Route Properties

    Ok, now we will see what each route property means:

    Here is an example for most of the possible options:

    1. routes: [
    2. // Load via Ajax
    3. {
    4. path: '/about/',
    5. url: './pages/about.html',
    6. },
    7. // Dynamic page from content
    8. {
    9. path: '/news/',
    10. content: `
    11. <div class="page">
    12. <div class="page-content">
    13. <div class="block">
    14. <p>This page created dynamically</p>
    15. </div>
    16. </div>
    17. </div>
    18. `,
    19. },
    20. // By page name (data-name="services") presented in DOM
    21. {
    22. path: '/services/',
    23. pageName: 'services',
    24. },
    25. // By page HTMLElement
    26. {
    27. path: '/contacts/',
    28. el: document.querySelector('.page[data-name="contacts"]'),
    29. },
    30. // By template
    31. {
    32. path: '/template/:name/',
    33. template: `
    34. <div class="page">
    35. <div class="page-content">
    36. <div class="block">
    37. <p>Hello {{$route.params.name}}</p>
    38. </div>
    39. </div>
    40. </div>
    41. `,
    42. },
    43. // By template URL
    44. {
    45. path: '/blog/',
    46. templateUrl: './pages/blog.html',
    47. },
    48. // By component
    49. {
    50. path: '/posts/',
    51. component: {
    52. // look below
    53. },
    54. },
    55. // By component url
    56. {
    57. path: '/post/:id/',
    58. componentUrl: './pages/component.html',
    59. },
    60. // Async
    61. {
    62. path: '/something/',
    63. async: function (routeTo, routeFrom, resolve, reject) {
    64. // Requested route
    65. console.log(routeTo);
    66. // Get external data and return template7 template
    67. this.app.request.json('http://some-endpoint/', function (data) {
    68. resolve(
    69. // How and what to load: template
    70. {
    71. template: '<div class="page">{{users}}</div>'
    72. },
    73. // Custom template context
    74. {
    75. context: {
    76. users: data,
    77. },
    78. }
    79. );
    80. });
    81. }
    82. }
    83. ],

    Route Path

    As stated above, the route’s path property means the path/url that will be displayed in browser window address bar (if pushState enabled) when the following route will be loaded either by api or clicking on a link with same path.

    There is also support for dynamic paths. So if you have the following path in your route /blog/users/:userId/posts/:postId/ and click the link with the with the /blog/users/12/posts/25 href then on loaded page we access route.params object containing { userId: 12, postId: 25 }

    Route path matching is handled by Path To Regexp library, so everything what is supported there is supported in Framework7 as well. For example, if you want to add default route which match all paths, we can use regular expression like:

    1. // Default route, match to all pages (e.g. 404 page)
    2. {
    3. path: '(.*)',
    4. url: './pages/404.html',
    5. },

    Route Options

    Lets look at additional route options that can be passed in options property:

    ParameterTypeDescription
    animatebooleanwhether the page should be animated or not (overwrites default router settings)
    historybooleanwhether the page should be saved in router history
    pushStatebooleanwhether the page should be saved in browser state. In case you are using pushState, then you can pass here false to prevent route getting in browser history
    reloadCurrentbooleanreplace the current page with the new one from route
    reloadPreviousbooleanreplace the previous page in history with the new one from route
    reloadAllbooleanload new page and remove all previous pages from history and DOM
    contextobjectcustom/extended context for Template7/Component page (when route loaded from template, templateUrl, component or componentUrl)
    propsobjectprops that will be passed as Vue/React page component props

    async route property is very powerful tool designed to return dynamic route properties. It is a function with the following arguments:

    async(routeTo, routeFrom, resolve, reject)

    • routeTo - requested Route
    • routeFrom - currently active Route
    • resolve - callback function that you need to call to proceed route loading
    • reject - callback function that you need to call to abort route loading

    resolve callback function accepts two arguments:

    resolve(parameters, options)

    • parameters object - object with resolved route content. Must contain one of url, content, template, templateUrl, component or componentUrl properties
    • options object - object with Route Options

    reject callback function doesn’t have arguments:

    reject()

    Note that until you call resolve or reject in async method, routing will be blocked!

    For example:

    Route Events

    1. var app = new Framework7({
    2. routes: [
    3. // ...
    4. {
    5. path: '/users/',
    6. url: './pages/users.html',
    7. on: {
    8. pageBeforeIn: function (event, page) {
    9. // do something before page gets into the view
    10. },
    11. pageAfterIn: function (event, page) {
    12. // do something after page gets into the view
    13. },
    14. pageInit: function (event, page) {
    15. // do something when page initialized
    16. },
    17. pageBeforeRemove: function (event, page) {
    18. // do something before page gets removed from DOM
    19. },
    20. }
    21. },
    22. // ...
    23. ],
    24. });

    Please note, that such route events are actually DOM events, so each such handler will accept event as a first argument with the event itself and page as the second argument with page data.

    Also, context of such event handler (this) will point to related .

    Nested Routes

    It is possible to have nested routes (routes in routes) as well:

    1. routes = [
    2. {
    3. path: '/faq/',
    4. url: './pages/faq.html',
    5. },
    6. {
    7. url: './pages/catalog.html',
    8. routes: [
    9. {
    10. path: 'computers/',
    11. url: './pages/computers.html',
    12. },
    13. path: 'monitors/',
    14. url: './pages/monitors.html',
    15. },
    16. ...
    17. ],
    18. }
    19. ];

    What does it mean? To get better understanding, actually (under the hood) such routes will be merged into the following ones:

    1. routes = [
    2. {
    3. path: '/faq/',
    4. url: './pages/faq.html',
    5. },
    6. {
    7. path: '/catalog/',
    8. url: './pages/catalog.html',
    9. }
    10. {
    11. path: '/catalog/computers/',
    12. url: './pages/computers.html',
    13. },
    14. {
    15. path: '/catalog/monitors/',
    16. url: './pages/monitors.html',
    17. },
    18. ];

    So lets say we are on a /catalog/ page and have the following links:

    1. <a href="computers/">Computers - will work as expected. Link will be merged with the current route (/catalog/ + computers/) and we will have /catalog/computers/ which we have in our routes.

    2. <a href="./computers/">Computers</a> - will work the same as case 1 because ./ in the beginning of path means same sub level.

    3. <a href="/catalog/computers/">Computers</a> - will also work as expected the same as case 1 because / (slash) in the beginning means root. And we have such root route in merged routes.

    4. <a href="/computers/">Computers</a> - won’t work as expected because / (slash) in the beginning means root. And we don’t have such /computers/ root route in our routes.

    Detail Routes

    For Master Detail view, it is also possible to specify detailRoutes in addition to master: true on master route.

    When detailRoutes are specified then navigating to detail route will also preload its master route.

    But unlike nested routes (specified in routes parameters), detail routes paths don’t merged with master route path.

    1. routes = [
    2. {
    3. path: '/blog/',
    4. url: './news.html',
    5. master: true,
    6. detailRoutes: [
    7. {
    8. /* We need to specify detail route path from root */
    9. path: '/blog/:postId/',
    10. url: './post.html',
    11. },
    12. ],
    13. },
    14. // ...
    15. ]

    What routable tabs means and why is it good?

    • First of all, it provides opportunity to navigate to tabs by usual links instead of so called special tab-links.
    • Second, when navigating to such route you can load a page with required tab opened.
    • Third, with enabled Push State, the same tab will be opened when you navigate back and forward in history.
    • And the last but not least, when using routable tabs you can load tabs content in the same ways as for pages, i.e. using url, content, template, templateUrl, component or componentUrl

    First of all we need to specify tabs routes in app routes. Lets assume we have a page with routable tabs on /tabs/ route:

    1. routes = [
    2. {
    3. path: '/about-me/',
    4. url: './pages/about-me/index.html',
    5. // Pass "tabs" property to route
    6. tabs: [
    7. // First (default) tab has the same url as the page itself
    8. {
    9. path: '/',
    10. id: 'about',
    11. // Fill this tab content from content string
    12. content: `
    13. <div class="block">
    14. <h3>About Me</h3>
    15. <p>...</p>
    16. </div>
    17. `
    18. },
    19. // Second tab
    20. {
    21. path: '/contacts/',
    22. id: 'contacts',
    23. // Fill this tab content via Ajax request
    24. url: './pages/about-me/contacts.html',
    25. },
    26. // Third tab
    27. {
    28. path: '/cv/',
    29. id: 'cv',
    30. // Load this tab content as a component via Ajax request
    31. componentUrl: './pages/about-me/cv.html',
    32. },
    33. ],
    34. }
    35. ]

    On the /about-me/ page we may have the following structure for example:

    Almost the same as with usual Tabs but with the difference that there is no more tab-link-active and tab-active classes on tab links and tabs. These classes and tabs will be switched by router. And there is a new data-route-tab-id attribute, it is required for tabs switcher to understand which link related to the selected route.

    You can learn more about Routable Tabs and their additional events in appropriate sections of component page.

    Routable Modals

    Modals can routable as well. By Modals here we mean the following components: , Popover, , Login Screen, . Probably Popup and Login Screen have more use cases here.

    And same features as for routable tabs and pages:

    • it provides opportunity to open modals by usual links instead of so called special links or API,
    • with enabled Push State, the same modal will be opened when you refresh browser, navigate back and forward in history,
    • with routable modals you can load modal itself and its content in the same ways as for pages, i.e. using url, content, template, templateUrl, component or componentUrl
    1. routes = [
    2. ...
    3. // Creates popup from passed HTML string
    4. {
    5. path: '/popup-content/',
    6. popup: {
    7. content: `
    8. <div class="popup">
    9. <div class="view">
    10. <div class="page">
    11. ...
    12. </div>
    13. </div>
    14. </div>
    15. `
    16. }
    17. },
    18. // Load Login Screen from file via Ajax
    19. {
    20. path: '/login-screen-ajax/',
    21. loginScreen: {
    22. url: './login-screen.html',
    23. /* login-screen.html contains:
    24. <div class="login-screen">
    25. <div class="view">
    26. <div class="page">
    27. ...
    28. </div>
    29. </div>
    30. </div>
    31. */
    32. },
    33. },
    34. // Load Popup from component file
    35. {
    36. path: '/popup-component/',
    37. loginScreen: {
    38. componentUrl: './popup-component.html',
    39. /* popup-component.html contains:
    40. <template>
    41. <div class="popup-screen">
    42. <div class="view">
    43. <div class="page">
    44. ...
    45. </div>
    46. </div>
    47. </div>
    48. </template>
    49. <style>...</style>
    50. <script>...</script>
    51. */
    52. },
    53. },
    54. // Use async route to check if the user is logged in:
    55. {
    56. path: '/secured-page/',
    57. async(routeTo, routeFrom, resolve, reject) {
    58. if (userIsLoggedIn) {
    59. resolve({
    60. url: 'secured-page.html',
    61. });
    62. } else {
    63. resolve({
    64. loginScreen: {
    65. url: 'login-screen.html'
    66. } ,
    67. });
    68. }
    69. },
    70. }
    71. ]

    According to example above:

    • when you click on link with /popup-content/ href attribute it will open Popup from specified string content,
    • when you click on link with /login-screen-ajax/ href attribute it will perform Ajax request to login-screen.html file and open it as a Login Screen,
    • when you click on link with /popup-component/ href attribute it will perform Ajax request to popup-component.html file, parse it as a Router Component and open it as a Popup,
    • when you click on link with /secured-content/ href attribute it will load page from secured-page.html if user is logged in or open Login Screen from login-screen.html file is user is not logged in.

    Routable Panels

    Panels (Side Panels) can also be routable with same features as for routable modals and pages:

    • it provides opportunity to open Panel by usual links instead of so called special links or API,
    • with enabled Push State, the same Panel will be opened when you refresh browser, navigate back and forward in history,
    1. routes = [
    2. ...
    3. {
    4. path: '/left-panel/',
    5. panel: {
    6. content: `
    7. <div class="panel panel-left panel-cover">
    8. <div class="view">
    9. <div class="page">
    10. ...
    11. </div>
    12. </div>
    13. </div>
    14. `
    15. }
    16. },
    17. // Load Panel from file via Ajax
    18. {
    19. path: '/right-panel-ajax/',
    20. panel: {
    21. url: './right-panel.html',
    22. /* right-panel.html contains:
    23. <div class="panel panel-right panel-reveal">
    24. <div class="view">
    25. <div class="page">
    26. ...
    27. </div>
    28. </div>
    29. </div>
    30. */
    31. },
    32. },
    33. // Load Panel from component file
    34. {
    35. path: '/panel-component/',
    36. panel: {
    37. componentUrl: './panel-component.html',
    38. /* panel-component.html contains:
    39. <template>
    40. <div class="panel panel-left panel-cover">
    41. <div class="view">
    42. <div class="page">
    43. ...
    44. </div>
    45. </div>
    46. </div>
    47. </template>
    48. <style>...</style>
    49. <script>...</script>
    50. */
    51. },
    52. },
    53. ]

    According to example above:

    • when you click on link with /left-panel/ href attribute it will open Panel from specified string content,
    • when you click on link with /right-panel-ajax/ href attribute it will perform Ajax request to right-panel.html file and open it as a Right Panel,
    • when you click on link with /panel-component/ href attribute it will perform Ajax request to panel-component.html file, parse it as a Router Component and open it as a Panel,

    Note that routable Panels can’t be mixed with static Panels. So if you have static left panel in the app, then only right panel can be loaded as routable panel.

    Route Before Enter/Leave

    beforeEnter and beforeLeave route hooks can be very useful if you need to do additional checks, execute additional actions or load/send something before route load (enter) and unload (leave). It can be single method or array of methods to be executed. For example:

    1. routes = [
    2. {
    3. path: 'profile',
    4. url: 'profile.html',
    5. beforeEnter: function (routeTo, routeFrom, resolve, reject) {
    6. if (/* some condition to check user is logged in */) {
    7. resolve();
    8. } else {
    9. // don't allow to visit this page for unauthenticated users
    10. reject();
    11. }
    12. },
    13. },
    14. {
    15. path: 'profile-edit',
    16. url: 'profile.html',
    17. beforeLeave: function (routeTo, routeFrom, resolve, reject) {
    18. if (/* user didn't save edited form */) {
    19. app.dialog.confirm(
    20. 'Are you sure you want to leave this page without saving data?',
    21. function () {
    22. // proceed navigation
    23. resolve();
    24. },
    25. function () {
    26. // stay on page
    27. reject();
    28. }
    29. )
    30. } else {
    31. resolve();
    32. }
    33. }
    34. }
    35. ]

    And of course there are multiple hooks are supported when passed as array of functions:

    1. function checkAuth(to, from, resolve, reject) {
    2. if (/* some condition to check user is logged in */) {
    3. resolve();
    4. } else {
    5. reject();
    6. }
    7. }
    8. function checkPermission(to, from, resolve, reject) {
    9. if (/* some condition to check user edit permission */) {
    10. resolve();
    11. } else {
    12. reject();
    13. }
    14. }
    15. routes = [
    16. {
    17. path: '/profile/',
    18. url: 'profile.html',
    19. // check if the user is logged in
    20. beforeEnter: checkAuth,
    21. },
    22. {
    23. path: '/profile-edit/',
    24. url: 'profile-edit.html',
    25. // check if the user is logged in and has required permission
    26. beforeEnter: [checkAuth, checkPermission],
    27. }
    28. ]

    Alias

    We can pass route alias using route alias property. Alias in this case basically means that same route can have multiple paths to access:

    1. routes = [
    2. {
    3. path: '/foo/',
    4. url: 'somepage.html',
    5. alias: '/bar/',
    6. },
    7. {
    8. path: '/foo2/',
    9. url: 'anotherpage.html',
    10. alias: ['/bar2/', '/baz/', '/baz2/'],
    11. }
    12. ]

    According to the example above:

    • if we request page by /foo/ or /bar/ URL then first route will match and we get the page loaded from somepage.html
    • if we request page by /foo2/ , /bar2/, /baz/, /baz2/ URL then second route will match and we the page loaded from anotherpage.html

    Redirect

    We can pass route redirect using redirect property:

    • if we pass redirect as a string, we must pass here direct redirect url
    • if we pass redirect as a function, we need to call function’s resolve parameter with redirect url

    For example:

    Above example means:

    • when we request /bar/ URL then router will redirect to /foo/ URL and then search for route that match to this new URL. In this case it will match to the first route with path /foo/ and load the page from somepage.html
    • when we request /baz/?user=john we will redirect to URL /foo/?user=john that will match to first route
    • when we request /baz/ (without query) nothing will happen

    Note in redirects we pass URL, not the route path like in alias

    Keep Alive

    keepAlive routes available from Framework7 version 3.6.0.

    When keepAlive enabled, then once router loads such page, the page and, if applicable, its component (Vue, React or Router component) will be never destroyed. Instead, it will be detached from DOM and reused again when required.

    It can be useful to enable for “heavy” pages which are not too often updated. For example, page with map, or with heavy canvas or other computation. With usual logic, all those heavy computations will happen each time this page is visited. But with keepAlive it will be done once, and with all next visits, router will reuse already rendered page DOM element.

    It also can be useful if you really need to preserve page state. With enabled keepAlive, the next time page will be loaded, all DOM modifications and form elements state will be preserved.

    But there are things to pay attention:

    • It is not supported for async routes
    • It is supported only for pages (not for panels and modals)
    • If you have dynamic route path (like /some-page/:foo/:bar) or you rely on page route query (?foo=bar), then query and route params won’t be changed after its initial load.
    • page:beforeremove,pageBeforeRemove page events will never be fired for such page
    • If you use it as Vue component, then beforeDestroy,destroyed hooks will never be fired for such component. created, mounted hooks will fire only once.
    • If you use it as React component, then componentWillUnmount method will never be fired for such component. componentWillMount, componentDidMount methods will fire only once.
    • If you use it as F7’s Router component, then beforeDestroy, destroyed hooks will never be fired for such component. created, mounted hooks will fire only once.

    To avoid pitfalls with component’s and page lifecycle, it is recommend to rely on following page events:

    • page:mounted - will be always called for keepAlive route page when its DOM element attached or reattached again.

    • page:beforeunmount - will be always called for keepAlive route page when its DOM element is going to be detached from DOM.

    To make keepAlive route we just need to pass keepAlive: true to its parameters:

    1. import SomPageComponent from './some-page.js';
    2. var routes = [
    3. /* Usual route */
    4. {
    5. path: '/',
    6. url: './pages/home.html',
    7. },
    8. /* Alive route. Will be loaded from file first time, and then will reuse rendered DOM element */
    9. {
    10. path: '/map/',
    11. url: './pages/map.html',
    12. keepAlive: true,
    13. },
    14. /* Alive route. Will be created as component, and then will reuse rendered DOM element */
    15. {
    16. path: '/some-page/',
    17. component: SomPageComponent,
    18. keepAlive: true,
    19. ];