Routes

Define Routes

First of all, when we init the Framework7 app we should pass default routes using routes array parameter:

var app = new Framework7({
  routes: [
    {
      name: 'about',
      path: '/about/',
      url: './pages/about.html',
    },
    {
      name: 'news',
      path: '/news/',
      url: './pages/news.html',
      options: {
        animate: false,
      },
    },
    {
      name: 'users',
      path: '/users/',
      templateUrl: './pages/users.html',
      options: {
        context: {
          users: ['John Doe', 'Vladimir Kharlampidi', 'Timo Ernst'],
        },
      },
      on: {
        pageAfterIn: function (e, page) {
          // do something after page gets into the view
        },
        pageInit: function (e, page) {
          // do something when page initialized
        },
      }
    },
    // Default route, match to all pages (e.g. 404 page)
    {
      path: '(.*)',
      url: './pages/404.html',
    },
  ],
});

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 View, then you may specify the same routes parameter on View init:

var view1 = app.views.create('.view-1', {
  routes: [
    {
      path: '/users/',
      url: './pages/users.html',
    },
    {
      path: '/user/',
      url: './pages/user.html',
    },
  ],
});

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:

// This view will support all global routes + own additional routes
var view2 = app.views.create('.view-2', {
  // These routes are only available in this view
  routesAdd: [
    {
      path: '/blog/',
      url: './pages/blog.html',
    },
    {
      path: '/post/',
      url: './pages/post.html',
    },
  ],
})

Route Properties

Ok, now will see what each route property means:

Parameter Type Description
name string Route name, e.g. home
path string Route path. Means this route will be loaded when we click link that match to this path, or can be loaded by this path using API
options object Object with additional route options (optional)
routes array Array with nested routes
Content Related Propertues
The following route properties define how (from where/what) content should be loaded
el HTMLElement Load page from DOM by passed HTMLElement
pageName string Load page from DOM that has same data-name attribute
content string Creates dynamic page from specified content string
url string Load page content via Ajax.

Also supports dynamic route params from route path using {{paramName}} expression, e.g.

{
  path: '/users/:userId/posts/:postId',
  url: 'http://myapp.com/posts/{{userId}}/{{postId}}'
}
template string
function
Load page content from passed Template7 template string or function
templateUrl string Load page content from url via Ajax, and compile it using Template7

Also supports dynamic route params from route path using {{paramName}} expression

component object Load page from passed Framework7 Router Component
componentUrl string load pages as a component via Ajax

Also supports dynamic route params from route path using {{paramName}} expression

async function(routeTo, routeFrom, resolve, reject) Do required asynchronous manipulation and the return required route content and options
Routable Tabs
tabs array Array with tab routes
Routable Modals
actions object Action Sheet route
popup object Popup route
loginScreen object Login screen route
popover object Popover route
sheet object Sheet route
Events
on object Object with event handlers
Alias & Redirect
alias string
array
Route alias, or array with route aliases. We need to specify here alias path
redirect string
function(route, resolve, reject)
Route redirect. We need to specify here redirect url (not path)

Here is an example for most of possible options:

routes: [
  // Load via Ajax
  {
    path: '/about/',
    url: './pages/about.html',
  },
  // Dynamic page from content
  {
    path: '/news/',
    content: `
      <div class="page">
        <div class="page-content">
          <div class="block">
            <p>This page created dynamically</p>
          </div>
        </div>
      </div>
    `,
  },
  // By page name (data-name="services") presented in DOM
  {
    path: '/services/',
    pageName: 'services',
  },
  // By page HTMLElement
  {
    path: '/contacts/',
    el: document.querySelector('.page[data-name="contacts"]'),
  },
  // By template
  {
    path: '/template/:name/',
    template: `
      <div class="page">
        <div class="page-content">
          <div class="block">
            <p>Hello {{$route.params.name}}</p>
          </div>
        </div>
      </div>
    `,
  },
  // By template URL
  {
    path: '/blog/',
    templateUrl: './pages/blog.html',
  },
  // By component
  {
    path: '/posts/',
    component: {
      // look below
    },
  },
  // By component url
  {
    path: '/post/:id/',
    componentUrl: './pages/component.html',
  },
  // Async
  {
    path: '/something/',
    async: function (routeTo, routeFrom, resolve, reject) {
      // Requested route
      console.log(routeTo);
      // Get external data and return template7 template
      app.request.json('http://some-endpoint/', function (data) {
        resolve(
          // How and what to load: template
          {
            template: '<div class="page">{{users}}</div>'
          },
          // Custom template context
          {
            context: {
              users: data,
            },
          }
        );
      });
    }
  }
],

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:

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

Route Options

Let's look at additional route options that can be passed in options property:

Parameter Type Description
animate boolean whether the page should be animated or not (overwrites default router settings)
history boolean whether the page should be saved in router history
pushState boolean whether 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
reloadCurrent boolean replace the current page with the new one from route
reloadPrevious boolean replace the previous page in history with the new one from route
reloadAll boolean load new page and remove all previous pages from history and DOM
context object custom/extended context for Template7/Component page (when route loaded from template, templateUrl, component or componentUrl)

Async Route

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:

routes = [
  {
    path: '/foo/',
    async(routeTo, routeFrom, resole, reject) {
      if (userIsLoggedIn) {
        resolve({ url: 'secured.html' })
      } else {
        resolve({ url: 'login.html' })
      }
    }
  }
]

Route Events

It is possible to add all page events inside of route for this page using on route property. For example:

var app = new Framework7({
  routes: [
    // ...
    {
      path: '/users/',
      url: './pages/users.html',
      on: {
        pageBeforeIn: function (event, page) {
          // do something before page gets into the view
        },
        pageAfterIn: function (event, page) {
          // do something after page gets into the view
        },
        pageInit: function (event, page) {
          // do something when page initialized
        },
        pageBeforeRemove: function (event, page) {
          // do something before page gets removed from DOM
        },
      }
    },
    // ...
  ],
});

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.

Nested Routes

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

routes = [
  {
    path: '/faq/',
    url: './pages/faq.html',
  },
  {
    path: '/catalog/',
    url: './pages/catalog.html',
    routes: [
      {
        path: 'computers/',
        url: './pages/computers.html',
      },
      {
        path: 'monitors/',
        url: './pages/monitors.html',
      },
      ...
    ],
  }
];

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

routes = [
  {
    path: '/faq/',
    url: './pages/faq.html',
  },
  {
    path: '/catalog/',
    url: './pages/catalog.html',
  }
  {
    path: '/catalog/computers/',
    url: './pages/computers.html',
  },
  {
    path: '/catalog/monitors/',
    url: './pages/monitors.html',
  },
];

So let's 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.

Routable Tabs

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. Let's assume we have a page with routable tabs on /tabs/ route:

routes = [
  {
    path: '/about-me/',
    url: './pages/about-me/index.html',
    // Pass "tabs" property to route
    tabs: [
      // First (default) tab has the same url as the page itself
      {
        path: '/',
        id: 'about',
        // Fill this tab content from content string
        content: `
          <div class="block">
            <h3>About Me</h3>
            <p>...</p>
          </div>
        `
      },
      // Second tab
      {
        path: '/contacts/',
        id: 'contacts',
        // Fill this tab content via Ajax request
        url: './pages/about-me/contacts.html',
      },
      // Third tab
      {
        path: '/cv/',
        id: 'cv',
        // Load this tab content as a component via Ajax request
        componentUrl: './pages/about-me/cv.html',
      },
    ],
  }
]

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

<div class="page">
  <div class="navbar">
    <div class="navbar-inner">
      <div class="title">About Me</div>
    </div>
  </div>
  <div class="toolbar tabbar">
    <div class="toolbar-inner">
      <a href="./" class="tab-link" data-route-tab-id="about">About</a>
      <a href="./contacts/" class="tab-link" data-route-tab-id="contacts">>Contacts</a>
      <a href="./cv/" class="tab-link" data-route-tab-id="cv">>CV</a>
    </div>
  </div>
  <div class="tabs tabs-routable">
    <div class="tab page-content" id="about"></div>
    <div class="tab page-content" id="contacts"></div>
    <div class="tab page-content" id="cv"></div>
  </div>
</div>

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 Tabs component page.

Routable Modals

Modals can routable as well. By Modals here we mean the following components: Popup, Popover, Actions Sheet, Login Screen, Sheet. 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
routes = [
  ...
  // Creates popup from passed HTML string
  {
    path: '/popup-content/',
    popup: {
      content: `
        <div class="popup">
          <div class="view">
            <div class="page">
              ...
            </div>
          </div>
        </div>
      `
    }
  },
  // Load Login Screen from file via Ajax
  {
    path: '/login-screen-ajax/',
    loginScreen: {
      url: './login-screen.html',
      /* login-screen.html contains:
        <div class="login-screen">
          <div class="view">
            <div class="page">
              ...
            </div>
          </div>
        </div>
      */
    },
  },
  // Load Popup from component file
  {
    path: '/popup-component/',
    loginScreen: {
      componentUrl: './popup-component.html',
      /* popup-component.html contains:
        <template>
          <div class="popup-screen">
            <div class="view">
              <div class="page">
                ...
              </div>
            </div>
          </div>
        </template>
        <style>...</style>
        <script>...</script>
      */
    },
  },
  // Use async route to check if the user is logged in:
  {
    path: '/secured-page/',
    async(routeTo, routeFrom, resolve, reject) {
      if (userIsLoggedIn) {
        resolve({
          url: 'secured-page.html',
        });
      } else {
        resolve({
          loginScreen: {
            url: 'login-screen.html'
          } ,
        });
      }
    },
  }
]

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.

Redirect & Alias

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:

routes = [
  {
    path: '/foo/',
    url: 'somepage.html',
    alias: '/bar/',
  },
  {
    path: '/foo2/',
    url: 'anotherpage.html',
    alias: ['/bar2/', '/baz/', '/baz2/'],
  }
]

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:

routes = [
  {
    path: '/foo/',
    url: 'somepage.html',
  },
  {
    path: '/bar/',
    redirect: '/foo/',
  },
  {
    path: '/baz/',
    redirect: function (route, resolve, reject) {
      // if we have "user" query parameter
      if (route.query.user) {
        // redirect to such url
        resolve('/foo/?user=' + route.query.user);
      }
      // otherwise do nothing
      else reject();
    }
  }
]

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