aspect-js

View-Module Binding      ≡ Table of Contents      DataSource


Routing

Seanox aspect-js can organize page presentation in views addressed by paths (routes). Routing supports a hierarchical directory structure based on the IDs of nested composites in the markup. It controls visibility and access permission for views through paths, the so-called view flow. Routing uses the DOM to insert and remove views depending on the situation.

+-----------------------------------------------+
|  Page (#)                                     |
|  +-----------------------------------------+  |
|  |  View A (#A)                            |  |
|  |  +-----------------------------------+  |  |
|  |  |  View B (#A#B)                    |  |  |
|  |  |  +-----------------------------+  |  |  |
|  |  |  |  View C (#A#B#C)            |  |  |  |
|  |  |  +-----------------------------+  |  |  |
|  |  +-----------------------------------+  |  |
|  +-----------------------------------------+  |
+-----------------------------------------------+

Contents Overview

Terms

Page

In a single page application, the page is the basic framework and runtime environment of the entire application.

View

A view is the primary projection of application modules (module), components or content. This projection can contain additional views and sub-views. Views can be static, always shown, or path-controlled with the attribute route. Paths address the complete chain of nested views and show the parent views in addition to the target view.

<body route>
  <header id="header" composite>
    always shown, regardless of the path
  </header>
  <main>
    <div id="a" composite route>
      shown with path #a
    </div>
    <div id="b" composite route>
      shown with path #b
    </div>
    <div id="c" composite route>
      shown with path #c  
    </div>
  </main>
  <footer id="footer" composite>
    always shown, regardless of the path
  </footer>
</body>

View Flow

View flow describes the access control and the sequence of views. The routing provides interfaces, events, permission concepts and interceptors with which the view flow can be controlled and influenced.

Navigation is based on paths that use the URL hash. It is triggered by changing the URL hash in the browser, by using hash links and in JavaScript with window.location.hash, window.location.href, Routing.route(path) and Routing.forward(path).

<a href="#a#b#c">Goto root + a + b + c</a>
<a href="##">Back to the parent</a>
<a href="##x">Back to the parent + x</a>
Routing.route("#a#b#c");
Routing.route("##");
Routing.route("##x");
Routing.forward("#a#b#c");
Routing.forward("##");
Routing.forward("##x");

Unlike the navigate method, forwarding is executed directly and does not trigger asynchronous forwarding by changing the location hash.

Relative paths without hash at the beginning are possible, but only work with Routing.route(path) and Routing.forward(path).

Routing.route("x#y#z");
Routing.forward("x#y#z");

Important
Links with these paths are interpreted by the browser as a reference to another page.

Routing accepts all paths (routes) that only use 7-bit ASCII characters. It covers the destinations from the root. Subsequent path components are treated as path parameters and can be used in the business logic of the views. Special characters in parameters are URL-encoded.

Permission Concept

The permission concept is based on permit methods in modules. The runtime calls the permit method during each (re-)rendering if the module implements it. These return values are possible:

undefined / no return value

The view is shown if the current path covers the path of the view.

true

The view is shown if any parent views are also shown.

false

The view and possible sub-views are not shown.

string

The return value is a path. The view and possible sub-views are not shown and the page/navigation is redirected to the returned path.

const model = {
    permit() {
        if (condition === 1)
            return;        
        if (condition === 2)
            return true;
        if (condition === 3)
            return "#redirect";
        return false;
    }    
};

Interceptors

Interceptors allow custom logic to be executed during navigation before the target path is evaluated and the view flow is processed by routing. They are intended to react to specific paths and can influence whether the navigation continues.

An interceptor is registered with Routing.customize(path, actor). The path can be either an exact route string or a regular expression. The actor is a function that is executed when the specified path matches the requested navigation target. The actor is a callback that receives the previous hash and the new hash as parameters.

Routing.customize("#a#b#c", (previousHash, newHash) => {
    console.log("Navigation from", previousHash, "to", newHash);
});

Interceptors are processed before routing checks path validity, resolves the target view and updates the view flow. They are suitable for tasks such as authentication checks, redirects, route migration or custom navigation handling.

Execution order

Interceptors are executed in the order in which they were registered.

All registered interceptors are checked. If an interceptor matches the requested path, its actor function is executed. Following interceptors receive the potentially modified navigation target.

An interceptor can stop further processing of the current navigation by returning false explicitly.

Routing.customize("#a#b", (oldHash, newHash) => {
    if (!User.isAuthenticated())
        return false;
});

Any other return value does not affect routing.

Path matching

A string interceptor matches the specified path and all nested paths below it.

Routing.customize("#a", actor);

matches:

#a
#a#b
#a#b#c

but not:

#abc
#c#b#a

Regular expressions can be used for pattern-based matching:

Routing.customize(/^#a#\d+$/, actor);

Interceptors do not create entries in the routing history. They are executed within the current navigation event.

An interceptor can modify the target navigation if required. For example, window.location.replace() can be used to replace the current URL without creating an additional browser history entry.

Routing.customize("#old-path", (oldHash, newHash) => {
    window.location.replace("#new-path");
});

Because interceptors are executed sequentially, subsequent interceptors operate on the updated navigation target.

Interceptors are executed before the normal routing process. Changes made by interceptors directly affect the following path resolution and view rendering.

Paths

Paths are used for navigation, routing and view-flow control. The target can be a view or, when using interceptors, a function. For SPAs (Single-Page Applications), the anchor part of the URL is used for navigation and routes.

https://example.local/example/#path

Similar to a file system, absolute and relative paths are supported. Paths consist of case-sensitive words that only use 7-bit ASCII characters above the space character. Characters outside this range are URL encoded. The words are separated by the hash character (#).

#a#b#c#d

Repeated use of the separator (#) allows jumps back in the path to be mapped. The number of repetitions indicates the number of returns in the direction of the root.

#a#b#c#d##x   -> #a#b#c#x
#a#b#c#d###x  -> #a#b#x
#a#b#c#d####x -> #a#x

The navigation can be effected by changing the URL hash in the browser (direct input), by using hash links, and in JavaScript with window.location.hash, window.location.href, Routing.navigate(path) and Routing.forward(path).

Relative paths without hash at the beginning are possible, but only work with Routing.route(path) and Routing.forward(path).

Routing.route("x#y#z");
Routing.forward("x#y#z");

Important
Links with these paths are interpreted by the browser as a reference to another page.

The following path types are supported.

Root Path

These paths are empty or contain only one hash character.

<a href="#">Back to the root</a>

Relative Path

Relative Paths are based on the current path and begin with either a word or a return. Return jumps also use the hash sign, whereby the number of repetitions indicates the number of return jumps.

<a href="##">Back to the parent</a>
<a href="##x">Back to the parent + x</a>
<a href="###">Back to the parent + parent</a>
<a href="###x">Back to the parent + parent + x</a>

Relative paths without hash at the beginning are possible, but only work with Routing.route(path) and Routing.forward(path).

Routing.route("x#y#z");
Routing.forward("x#y#z");

Absolute Path

Absolute Paths start with the root, represented by a leading hash sign (#).

<a href="#">Back to the root</a>
<a href="#a#b#c">Back to the root + a + b + c</a>

View-Module Binding      ≡ Table of Contents      DataSource