Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web

Bobril - IV - Routing

5.00/5 (5 votes)
13 Aug 2017CPOL4 min read 22.3K   119  
Simple explanation of bobril routing mechanism

Introduction

In this article, we will learn how to handle routes definition by bobril embedded routing mechanism.

Background

Bobril is a component-oriented framework written by Boris Letocha (software architect and developer in GMC Software Technology). For more information, see the first article.

Using the Code

Preparing Environment

At first, we need to have prepared bobril-build on the computer. Follow the steps in the first article to perform bobril-build installation.

Now you can start a new project again or use a predefined skeleton simpleApp from bobril-build github repository.

The following example will use it. To get the final code, download the full sample.

Defining Routes

Bobril has few methods for defining the application route tree:

  • b.route - defines a route url, name, handler and a list of sub-routes
  • b.routes - registers routes to the application and calls b.init
  • b.routeDefault - defines the default route if no sub-route is specified in the current url

Example of the route definition in app.ts:

JavaScript
import * as b from 'bobril';
import { mainPage } from './page';
import { pageOne } from './pages/pageOne';
import { pageTwo } from './pages/pageTwo';

b.routes(
    b.route({ handler: mainPage }, [
        b.route({ url: '/one', name: 'one', handler: pageOne }),
        b.route({ url: '/two/:text?', name: 'two', handler: pageTwo }),
        b.routeDefault({ handler: pageOne })
    ])
);

The whole application will be handled by a handler mainPage with sub-routes one and two on urls '/one' and '/two' handled by handlers pageOne and pageTwo.

The default handler pageOne will be used when no sub-route is specified.

The url for page two contains a parameter specification after a second slash. It is defined by a colon and a name of the parameter. The question mark defines the parameter as optional. Route parameters can then be found in the handler's context at ctx.data.routeParams.

Handling Routes in Pages

Now, we need to define the mainPage to render some own content and the visual content of the active sub-route. To do this, we will use a function provided in component's ctx.data.activeRouteHandler, so we can change the code of page.ts as the following:

JavaScript
import * as b from 'bobril';

export const mainPage = b.createComponent({
    render(_ctx: b.IBobrilCtx, me: b.IBobrilNode): void {
        me.children = [
            tag('h1', 'Routing example'),
            tag('hr'),
            tag('div', me.data.activeRouteHandler()),
        ];
    }
});

This code will render a header, a line and the visual content of current active sub-route.

Now, we need to define sub-pages and transitions between these pages. Bobril offers the following functions and interface for these purposes:

  • b.IRouteTransition - interface for a transition definition (target name, parameters, etc.)
  • b.createRedirectReplace - creates IRouteTransition object for redirect without saving history
  • b.createRedirectPush - creates IRouteTransition object for redirect with saving history
  • b.runTransition - runs a transition according to an input IRouteTransition object
  • b.link - changes an input IBobrilNode to a link to the route of a specified name and with specified optional params

Example of the redirect definition from the page one to the page two in pages/pageOne.ts:

JavaScript
import * as b from 'bobril';
import { textbox } from '../components/textbox';
import { button } from '../components/button';

let value = '';

export const pageOne = b.createComponent({
    render(ctx: b.IBobrilCtx, me: b.IBobrilNode): void {
        me.children = [
            textbox({ value, onChange: (newVal) => { value = newVal; b.invalidate(ctx); } }),
            button({
                title: 'Confirm',
                onClick: () => {
                    b.runTransition(b.createRedirectPush('two', { text: value }));
                    return true;
                }
            })
        ];
    }
});

The code in a button's onClick callback creates and runs a transition to the page two with an object defining the value of a text parameter.

The transition above has to be handled by the pageTwo handler defined in pages/pageTwo.ts:

JavaScript
import * as b from 'bobril';
import { tag } from '../helper';
import { loggedIn } from '../page';

export interface IData {
    routeParams: { text?: string };
}

export interface ICtx extends b.IBobrilCtx {
    data: IData;
}

export const pageTwo = b.createComponent<IData>({
    render(ctx: ICtx, me: b.IBobrilNode): void {
        let value = (ctx.data.routeParams.text || '').trim();
        me.children = [
            tag('p', 'Your text: ' + (!!value ? value : 'nothing')),
            b.link(tag('a', 'Go back'), 'one')
        ];
    }
});

The page receives the text parameter value in its ctx.data.routeParams.text so we can specify the context and data interfaces and use text in the render function of a page component. It also defines a link node to the page one by function b.link.

Transition Availability

There are some cases when we need to manage whether a current page on a current route is available or whether we can leave the current page. For these purposes, we can use the following static functions of IBobrilComponent:

  • canActivate - It can stop the current transition in a target handler by returning false or redirect to the new specified transition
  • canDeactivate - Can stop the current transition in the a source handler by returning false or redirect to the new specified transition

So for example, we can handle leaving the page one with empty value of a textbox by adding canDeactivate function definition to the page one component definition in pages/pageOne.ts:

JavaScript
canDeactivate() {
    return !!value.trim() || confirm('The textbox is empty. Are you sure?');
}

or handle the not logged user on accessing the page two by adding canActivate function definition to the page two component definition in pages/pageTwo.ts:

JavaScript
canActivate() {
    if (loggedIn)
        return true;
    alert('You are not logged in!');
    return b.createRedirectReplace('one');
}

Note: The loggedIn variable is declared and set in mainPage and imported to the pageTwo. See the page.ts in the attached full sample.

Summary

As you can see, the bobril framework contains really simple routing mechanism able to fulfill all standard requirements.

History

  • 2017-07-30: Revision (bobril-build@0.71.1, bobril@7.3.2, TS 2.4.2)
  • 2017-02-01: Revision (bobril-build@0.59.2, bobril@5.2.1)
  • 2016-11-04: Revision
  • 2015-12-16: Changed to simpleApp based on bobril-build
  • 2015-11-22: Article created

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)