Unit 6: Routing

Routing is the process of selecting a path for data in a network. It enables the user to navigate the app and move between pages. The url (uniform resource locator) is the property that indicates what is the data to be fetched and displayed to the user; the active page. Through interactions such as clicking on links the user can change the active url to navigate to a different page.

Links

To enable routing, we need a way to change the current url. Well, in oneJS it is actually quite simple; just provide a value to the url property to any component and see how the url of the app changes when clicked.

import {app} from '@onejs-dev/core';
import {View, Input} from '@onejs-dev/components';

const App = () => {
return View({content: {direction: 'row', gap: 10}})([
Input({type: 'button', title: 'Home', url: '/'}),
Input({type: 'button', title: 'Products', url: '/products'})
]);
};

/* App Function: Renders the App Component in the screen */
app({component: App, theme: {preset: 'oneJS'}});

Note: On the web, you can provide the url property to any component and click on it to change the url. On ios and android not all the components are clickable. If you are developing cross-platform, we recommend placing the url property exclusively on View or Input({type: 'button'}) components.

Naming Convention

How are url paths specified? In oneJS all urls are absolute; the full path to the page needs to be specified. The root url is /. All other urls also begin with / and are followed by the path to the target page. The segments of the route are also separated by /, e.g.: /path/to/page. You may have heard about relative url trend, and it may sound attractive, but they need to know about the context they are defined in, breaking isolation. This is why we have not adopted them in oneJS, as it goes against the functional pattern.

View({url: '/products'})([
/* DO NOT do this: Relative url */
Input({type: 'button, url: './product1'}),
/* DO this: Absolute url */
Input({type: 'button, url: '/products/product1'})
]);

Show and Hide

You now know how to change the current url but routing also involves displaying and hiding information on the screen based on the active url. You can modify the visibility of a View, just by feeding the visible property. Remember the Visibility section from Unit 4.

The only thing left to connect the pieces, is how to use the current url to change the visibility of View components. Fortunately, oneJS provides a simple way of doing so. Since the url is a property that changes and affects the behavior of your app, it needs to be part of the state. Just specify the url to be matched as the source for your state variable; when it matches it will automatically become true and when it does not it will be false. You can use asterisks (*) in your path to indicate that any value is acceptable for a certain segment. It is way easier to see in action in the example below:

import {app, read} from '@onejs-dev/core';
import {View, Input, Text} from '@onejs-dev/components';

const state = {
    homePage: {source: {url: '/'}},
    productsPage: {source: {url: '/products'}}
}

const App = () => {
    return  View({content: {direction: 'column', gap: 10}})([
        View()([
            Input({type: 'button', title: 'Home', url: '/'}),
            Input({type: 'button', title: 'Products', url: '/products'})
        ]),
        View({visible: read('homePage')})(Text()('Home Page')),
        View({visible: read('productsPage')})(Text()('Products Page')),
    ]);
};

/* App Function: Renders the App Component in the screen */
app({component: App, state: state, theme: {preset: 'oneJS'}});

Behind the scenes oneJS is using the history API on the web, so you can seamlessly go back and forward in your url history.

Advanced Routing

There are complex scenarios where you want to render a list of items and dynamically generate the url on click. In these cases, you have no way of knowing in advance the full url, as it usually depends on the database id of the selected element. To address this, you can provide to the state variable source, a url containing a : value for the segment you want to retrieve data from. For instance, if the url source is products/: and the actual url is products/product1, the state variable will be equal to product1. You can test this in the example below:

import {app, read, readFlavor} from '@onejs-dev/core';
import {View, Input, Text} from '@onejs-dev/components';
/* Variable in path to fetch the product id  */
const state = {productId: {source: {url: '/:'}}};
/* Emulates a 'products' database response */
const products = {
    product1: {name: 'pizza', price: '$10'},
    product2: {name: 'risotto', price: '$15'},
    product3: {name: 'tiramisu', price: '$5'}    
};

const App = () => {
    return [
        Object.keys(products).map(product => Input({type: 'button', title: product, url: '/' + product})),
        read('productId') && View({content: {direction: 'column', gap: 10}, flavor: readFlavor('shadow')})([
            Text()('Id: ' + read('productId')),
            Text()('Name: ' + products[read('productId')].name),
            Text()('Price: ' + products[read('productId')].price),
        ]),
    ];
};

/* App Function: Renders the App Component in the screen */
app({component: App, state: state, theme: {preset: 'oneJS'}});

If you have followed this far, you are now able to create an interactive page enabling the user to serve different content based on their actions. Head to the following unit and learn how to add fantastic animations!