@seanox/aspect-js
Version:
full stack JavaScript framework for SPAs incl. reactivity rendering, mvc / mvvm, models, expression language, datasource, routing, paths, unit test and some more
1,167 lines (949 loc) • 41.1 kB
Markdown
◁ [Motivation](motivation.md)
≡ [Table of Contents](README.md#introduction)
[Expression Language](expression.md) ▷
- - -
# Introduction
## What is Seanox aspect-js?
Inspired by the good experiences with JSF (Java Server Faces) in terms of
functionality and easy integration into the markup, the idea for a similar
client-side full-stack solution with a minimalistic and lightweight approach for
the implementation of Single-Page Applications (SPAs) and Micro-Frontends was
born.
Seanox aspect-js takes the declarative approach of HTML and extends this with
expression language, reactivity rendering with additional attributes, Model View
Controller, view model binding, events, interceptors, resource bundle, NoSQL
datasource, test environment and much more.
# Features
- Easy integration in markup and JavaScript (clean code)
combinable with other JavaScript frameworks if they don't do the same thing do
and use a different syntax
- Lightweight implementation
requires no additional frameworks
- Component based architecture
- Namespaces and domain concept
for better structuring of components, modules and business logic
- Modularization (supports macros and imports at the runtime)
component concept for smart/automatic loading of composite resources at runtime
- Event handling
- Expression Language
meta-language extension with full JavaScript support
- Reactivity rendering
rendering reacts to changes in data objects and triggers partial rendering on
consumers
- Markup rendering
supports: conditions, custom tags, events, filter, interval, interceptors,
iterate, rendering, resources messages, validation, ...
- Markup hardening
makes it difficult to manipulate the attributes in the markup
non-visible components are removed from the DOM and only reinserted when used
- Model View Controller (MVC) / Model View ViewModel (MVVM)
supports view model binding, events and interceptors
- Routing to organize the page into views
supports paths (routes), interceptors and permission concepts
- Resource Bundle / Resource Messages
internationalization (i18n), localization (l10n) and text outsourcing
- NoSQL datasource based on XML
lightweight data management for aggregation / projection / transformation
- Micro Frontends
platform and framework for the implementation of micro-frontends
- Test environment
for automated unit tests and integration tests
- ...
## Contents Overview
- [Getting Started](#getting-started)
- [Scope](#scope)
- [Expression Language](#expression-language)
- [Attributes](#attributes)
- [output](#output)
- [import](#import)
- [condition](#condition)
- [interval](#interval)
- [iterate](#iterate)
- [id](#id)
- [composite](#composite)
- [route](#route)
- [events](#events)
- [validate](#validate)
- [message](#message)
- [render](#render)
- [release](#release)
- [DataSource](#datasource)
- [Resource Bundle (Messages/i18n/l10n)](#resource-bundle-messages--i18n-l10n)
- [Model View Controller](#model-view-controller)
- [Model](#model)
- [View](#view)
- [Controller](#controller)
- [View Model Binding](#view-model-binding)
- [Composite](#composite)
- [Binding](#binding)
- [Dock / Undock](#dock--undock)
- [Synchronization](#synchronization)
- [Validation](#validation)
- [Events](#events)
- [Routing](#routing)
- [Page](#page)
- [View](#view)
- [View-Flow](#view-flow)
- [Navigation](#navigation)
- [Permission Concept](#permission-concept)
- [Interceptors](#interceptors)
- [Paths](#paths)
- [Components](#components)
- [Scripting](#scripting)
- [Reactivity Rendering](#reactivity-rendering)
- [API Extensions](#api-extensions)
- [Events](#events-1)
- [Test](#test)
- [Task](#task)
- [Scenario](#scenario)
- [Suite](#suite)
- [Assert](#assert)
- [Configuration](#configuration)
- [Monitoring](#monitoring)
- [Control](#control)
- [Events](#events-2)
## Getting Started
The framework consists of one JavaScript file that can be included via the URL
of a release channel or downloaded as a release.
Release channels continuously provide the latest final major versions. This way,
Seanox aspect-js is always up to date.
Each release consists of different versions for different purposes. These
versions are also always available in two variants. The standard is the
compressed version for productive use. Optionally, the uncompressed and
documented max variant is also available for development and error analysis.
To begin, create an HTML file, e.g. _index.html_ and insert Seanox apect-js.
```html
<!-- development version, includes helpful comments -->
<script src="https://cdn.jsdelivr.net/npm/@seanox/aspect-js/release/aspect-js-max.js"></script>
```
or
```html
<!-- production and minimized version -->
<script src="https://cdn.jsdelivr.net/npm/@seanox/aspect-js/release/aspect-js.js"></script>
```
__The framework has been developed for the implementation of modular and
component-based Single-Page applications and Micro-Frontends. Due to the
automatic loading of resources, modules and data, a web server is required for
use.__
## Scope
Seanox aspect-js works exclusively in the HTML element `BODY`, which itself is
included.
## Expression Language
Expressions or the Expression Language (EL) is a simple access to the
client-side JavaScript and thus to the models and components in Seanox
aspect-js. In the expressions the complete JavaScript API is supported, which
is enhanced with additional keywords, so that also the numerous arithmetic and
logical operators can be used.
The expression language can be used from the HTML element `BODY` on in the
complete markup as free text, as well as in all attributes. Exceptions are the
HTML elements `STYLE` and `SCRIPT` whose content is not supported by the
expression Language. When used as free text, plain text is generated as output.
Adding markup, especially HTML code, is not possible this way and is only
supported with the attributes `output` and `import`.
```html
<body lang="{{DataSource.locale}}">
<p>
Today is {{new Date().toDateString()}}
and it's {{new Date().toLocaleTimeString()}} o'clock.
</p>
</body>
```
Expressions are interpreted by the renderer that starts after the page is
loaded. Thus, expressions may be visible when the page is loaded. Here it is
recommended to use the attributes [output](markup.md#output) and [import](
markup.md#import), which cause a direct output to the inner HTML of the
element.
```html
<p output="Today is {{new Date().toDateString()}}
and it's {{new Date().toLocaleTimeString()}} o'clock.">
</p>
```
Or the use of the attribute [release](markup.md#release), which makes HTML
elements and their contents visible only after rendering.
```html
<p release>
Today is {{new Date().toDateString()}}
and it's {{new Date().toLocaleTimeString()}} o'clock.
</p>
```
Expressions can create and use global variables at runtime.
```html
{{now:new Date()}}
<p>
Today is {{now.toDateString()}}
and it's {{now.toLocaleTimeString()}} o'clock.
</p>
```
[Learn more](expression.md)
## Attributes
In Seanox aspect-js, the declarative approach is implemented with attributes
that can be used and combined in all HTML elements starting from the HTML
element `BODY`, which is included. The values of the attributes can be static or
dynamic with the use of the expression language. If an attribute contains an
expression, the value is updated by the renderer with each refresh (render
cycle) based on the initial expression.
[Learn more](markup.md#attributes)
### output
Sets for the HTML element the value or result of its expression as inner HTML.
The behavior is similar to the [import](#import) attribute, except that the
output is updated with each render cycle. Supported values are text, one or more
elements as NodeList or Array, as well as absolute or relative URLs to a remote
resource and also the [DataSource-URL (locator)](datasource.md#locator) for
transformed content from the [DataSource](datasource.md).
The output attribute can be combined with the condition attribute and will then
only be executed if the condition is `true`.
```html
<p output="Today is {{new Date().toDateString()}}
and it's {{new Date().toLocaleTimeString()}} o'clock.">
</p>
<p output="xml:/example/content">
loading resource...
</p>
<p output="xml:/example/data xslt:/example/style">
loading resource...
</p>
```
[Learn more](markup.md#output)
### import
Loads the content for the HTML element at runtime and inserts it as inner HTML.
The behavior is similar to the [output](#output) attribute, except that the
import is done once and the import attribute is removed after successful
loading. As value one or more elements are supported as NodeList or Array, as
well as absolute or relative URLs to a remote resource and also the [DataSource
URL (locator)](datasource.md#locator) for transformed content from the
[DataSource](datasource.md).
The import attribute can be combined with the condition attribute and will then
only be executed if the condition is `true`.
```html
<p import="Today is {{new Date().toDateString()}}
and it's {{new Date().toLocaleTimeString()}} o'clock.">
</p>
<p import="xml:/example/content">
loading resource...
</p>
<p import="xml:/example/data xslt:/example/style">
loading resource...
</p>
<p import="https://raw.githubusercontent.com/seanox/aspect-js/master/test/resources/import_c.htmlx">
loading resource...
</p>
```
[Learn more](markup.md#import)
### condition
As a condition, the attribute specifies whether an element remains contained in
the DOM. The expression specified as the value must explicitly return `true` to
retain the element. If the return value is different, the element is temporarily
removed from the DOM and can be reinserted later by refreshing the __parent
element__ if the expression returns `true`.
```html
<article condition="{{Model.visible}}">
...
</article>
```
The use of the condition attribute in combination with embedded JavaScript is
possible as SCRIPT element with the type `composite/javascript` as Composite
JavaScript, because here the renderer has control over the script execution and
not the browser.
```html
<script type="composite/javascript" condition="{{Model.visible}}">
...
</script>
```
[Learn more](markup.md#condition)
### interval
Activates an interval-controlled refresh of the HTML element without the need to
actively trigger the refresh. The interval uses the inner HTML as a template
from which updated content is generated and inserted with each interval cycle.
The attribute expects milliseconds as value, which can also be formulated as
expression, where invalid values cause console output. Processing is concurrent
or asynchronous but not parallel. Processing will start after the specified time
when a previously started JavaScript procedure has finished. Therefore, the
interval should be understood as timely but not exact. The interval starts
refreshing automatically and ends when:
- the element no longer exists in the DOM
- the condition attribute is used that is not true
```html
<p interval="1000">
{{new Date().toLocaleTimeString()}}
</p>
```
With the combination of interval and variable expression, the implementation of
a permanent counter is very simple.
```html
{{counter:0}}
<p interval="1000">
{{counter:parseInt(counter) +1}}
{{counter}}
</p>
```
The interval attribute can be used in combination with embedded JavaScript as
composite JavaScript.
```html
<script type="composite/javascript" interval="1000">
console.log(new Date().toLocaleTimeString());
</script>
```
[Learn more](markup.md#interval)
### iterate
Iterative output is based on lists, enumerations and arrays. If an HTML element
is declared as iterative, the inner HTML is used as a template from which
updated content is generated and inserted as inner HTML with each render cycle.
as inner HTML. As value for the attribute a [variable expression](
expression.md#variable-expression) is expected, for which a meta-object will
be created, which allows access to the iteration in the template. Thus, the
variable expression `iterate={{tempA:Model.list}}` creates the meta-object
`tempA = {item, index, data}`.
```javascript
const Model = {
months: ["Spring", "Summer", "Autumn", "Winter"]
};
```
```html
<select iterate={{months:Model.months}}>
<option value="{{months.index}}">
{{months.item}}
</option>
</select>
```
[Learn more](markup.md#iterate)
### id
The ID (identifier) has an elementary meaning in Seanox aspect-js. It is the
basis for [view model binding](mvc.md#view-model-binding) and is used by
[Routing](routing.md#routing) for [views](routing.md#view) in the [view flow](
routing.md#view-flow) and thus as a destination for paths.
As with all attributes, the expression language can be used, with the difference
that the attribute is only read at the beginning. Due to the view model binding,
changes to an existing element at runtime have no effect as long as it exists in
the DOM.
[Learn more](markup.md#id)
### composite
Marks an element in the markup as a [Composite](composite.md). Composites are
essential components that require an identifier (ID / Composite ID).
```html
<article id="example" composite>
...
</article>
```
As a component, composites are composed of various [Ressourcen](
composite.md#ressourcen) (markup, CSS, JS) that can be outsourced to the
module directory based on the (composite) ID and are only loaded at runtime when
used.
Composites are also the basis for [view model binding](
mvc.md#view-model-binding), which involves connecting HTML elements in the
markup (view) with corresponding JavaScript objects (models). Models are static
JavaScript objects that provide data, states, and functions for the view,
similar to managed beans and DTOs (Data Transfer Objects). The view as
presentation and user interface for interactions and the model are primarily
decoupled. For the MVVM (Model-View-ViewModel) approach, as an extension to the
MVC ([Model View Controller](mvc.md#model-view-controller)), the controller
links views and models bidirectionally based on the composite IDsand so no
manual implementation and declaration of events, interaction or synchronization
is required.
The [Routing](routing.md#routing) uses composites as [views](routing.md#view)
for the primary projection of JavaScript objects (models), which means that they
can be used as targets for paths in the [view flow](routing.md#view-flow), which
has a direct influence on the visibility of the composites. When routing is
active, composites can be marked with attribute [route](#route) so that their
visibility is controlled by routing through paths and the permission concept.
[Learn more](markup.md#composite)
### events
Binds one or more [events](https://www.w3.org/TR/DOM-Level-3-Events) to an HTML
element. This allows event-driven synchronization of HTML elements with
corresponding JavaScript objects (models), as well as validation of the data to
be synchronized (see [validate](#validate) for more information) and
event-driven control and refreshing of other HTML elements (see [render](
#render) for more information).
```html
<span id="output1">{{#text1.value}}</span>
<input id="text1" type="text"
events="input change" render="#output1"/>
```
As with all attributes, the expression language is applicable here, with the
difference that changes at runtime have no effect, since the attribute or the
value for the view model binding is only processed initially as long as the HTML
element exists in the DOM.
[Learn more](markup.md#events)
### validate
The attribute `validate` requires the combination with the attribute `events`.
Together they define and control the synchronization between the markup of a
composite and the corresponding JavaScript object (model), where a property with
the same name must exist as a target for synchronization.
The validation works in two steps and uses the standard HTML5 validation at the
beginning. If this cannot determine deviations from the expected result or if no
HTML5 validation is specified, the validation of the JavaScript object is used
if the model provides a corresponding validate method
`boolean validate(element, value)` and the element to be validated is embedded
in a composite.
```html
<form id="Model" composite>
<input id="text1" type="text" placeholder="e-mail address"
pattern="^\w+([\w\.\-]*\w)*@\w+([\w\.\-]*\w{2,})$"
validate events="input change" render="#Model"/>
<input type="submit" value="submit" validate events="click"/>
</form>
```
The synchronization and the default action of the browser are directly
influenced by the validation. This can use for it four states as return values:
`true`, `not true`, `text`, `undefined/void`.
[Learn more](markup.md#validate)
### message
Message is an optional part of [Validation](#validate) and is used for text and
error output in case of an unconfirmed validation. This requires a combination
with the attributes [validate](#validate) and [events](#events).
```html
<form id="Model" composite>
<input id="text1" type="text" placeholder="e-mail address"
pattern="^\w+([\w\.\-]*\w)*@\w+([\w\.\-]*\w{2,})$"
validate message="Valid e-mail address required"
events="input change" render="#Model"/>
<input type="submit" value="submit" validate events="click"/>
</form>
```
```html
<form id="Model" composite>
<input id="text1" type="text" placeholder="e-mail address"
pattern="^\w+([\w\.\-]*\w)*@\w+([\w\.\-]*\w{2,})$"
validate message="{{Messages['Model.text1.validation.message']}}"
events="input change" render="#Model"/>
<input type="submit" value="submit" validate events="click"/>
</form>
```
[Learn more](markup.md#message)
### render
The attribute requires the combination with the [events](#events) attribute.
Together they define which targets are refreshed by the renderer with which
occurring events. The expected value is one or more space-separated CSS or Query
selectors that define the targets.
```html
<span id="output1">{{#text1.value}}</span>
<input id="text1" type="text"
events="input change" render="#output1"/>
```
__Alternatively, [reactive rendering](reactive.md) can be used, where changes in
the data objects trigger a partial update of the view.__
[Learn more](markup.md#render)
### release
Inverse indicator that an HTML element was rendered. The renderer removes this
attribute when an HTML element is rendered. This effect can be used for CSS to
display elements only in rendered state. A corresponding CSS rule is
automatically added to the HEAD when the page is loaded.
```html
<span release>{{'Show me after rendering.'}}</span>
```
[Learn more](markup.md#release)
## DataSource
DataSource is a NoSQL approach to data storage based on XML data in combination
with multilingual data separation, optional aggregation and transformation. A
combination of the approaches of a read only database and a CMS.
DataSource is based on static data. Therefore, the implementation uses a cache
to minimize network access.
The data is queried via XPath. The result can be concatenated and aggregated and
the result can be transformed with XSLT.
[Learn more](datasource.md#datasource)
## Resource Bundle (Messages/i18n/l10n)
(Resource)Messages is a static [DataSource](datasource.md) extension for
internationalization (i18n), localization (l10n) and client-related texts. The
implementation is based on a set of key-value or label-value data which is
stored in the `locales.xml` in the DataSource directory.
[Learn more](message.md)
## Model View Controller
The Model View Controller (MVC) is a design pattern for separating interaction,
data and presentation. A distinction should be made between I/O controller and
application controller. The pure MVC design pattern means the I/O controller for
the transmission of interactions. Since this is provided by the operating system
and browser, Seanox aspect-js ostensibly refers to the application controller.
```
+------------------------------------------+--------------+-----------------------+
| View | Controller | Model |
+------------------------------------------+--------------+-----------------------+
| Markup | Composite | JavaScript |
| | Reactive | |
+------------------------------------------+--------------+-----------------------+
| <form id="model" composite> | aspect-js | const model = { |
| <input id="message" events="input"/> | | message: "", |
| <button id="submit"/> | | submit: { |
| </form> | | onClick() { |
| | | } |
| | | } |
| | | } |
+------------------------------------------+--------------+-----------------------+
```
[Learn more](mvc.md)
### Model
Models are representable/projectable static JavaScript objects that can provide
and receive data, states and interactions for views, comparable to managed beans
and DTOs (Data Transfer Objects). As singletons/facades/delegates, they can use
other components and abstractions, contain business logic themselves, and be a
link between the user interface (view) and middleware (backend).
[Learn more](mvc.md#model)
### View
The view is exclusively responsible for the representation or projection of a
model. Where projection is an important term because the way a model is
represented is not restricted.
[Learn more](mvc.md#view)
### Controller
The (application)controller controls internal processes within an application
(view flow) and takes over the data flow between view and model with the
view-model binding, whereby we can also speak of MVVM (Model-View-ViewModel) and
MVCS (Model-View-Controller-Service) here.
[Learn more](mvc.md#controller)
### View Model Binding
The view-model binding takes over the bidirectional linking of the HTML elements
of the view with the models as static JavaScript objects and thus organizes the
data flow, communicates events as well as states and binds functions.
[Learn more](mvc.md#view-model-binding)
#### Composite
The basis for view-model binding is formed by composites, which are functionally
independent components consisting of markup, CSS, JavaScript, and optionally
other resources. All components are bound via, the also called Composite ID, ID
of the HTML construct, the model with the same name in JavaScript and the ID
corresponding to the HTML in CSS.
```html
<!DOCTYPE HTML>
<html>
<head>
<script src="aspect-js.js"></script>
</head>
<body>
<div id="example" composite></div>
</bod>
</html>
```
```javascript
const example = {
...
}
```
```css
#example {
...
}
```
The Composite ID is thus a unique identifier within the application. It is a
string of letters, numbers, and underscores that is at least one character long,
begins with an underscore or a letter, and is formed by combining the id and
composite attributes on an HTML element.
Composites, or better their composite ID, define the point in the global object
tree where the corresponding JavaScript model is located. All HTML elements with
an ID enclosed by a composite are then reflected as branches in the
corresponding JavaScript model if they are implemented accordingly in the model.
```javascript
const model = {
message: "Hello",
submit: {
...
}
};
```
```html
<html>
<body>
<form id="model" composite>
<input type="text" id="message"/>
<input type="submit" id="submit"/>
...
</form>
</body>
</html>
```
[Learn more](mvc.md#composite)
#### Binding
View-model binding is about linking markup/HTML (view) to the corresponding
JavaScript object (model). The binding passes interactions and state changes of
the view to the model and provides an interface for middleware functions and
services for the view. In this way, no manual implementation of events,
synchronization and interaction between view and application logic is required.
```javascript
const model = {
message: "Hello",
dock() {
...
},
undock() {
...
},
submit: {
onClick(event) {
...
}
}
};
```
```html
<html>
<body>
<form id="model" composite>
<input type="text" id="message" value="{{model.message}}" events="change"/>
<input type="submit" id="submit"/>
...
</form>
</body>
</html>
```
[Learn more](mvc.md#binding)
#### Dock / Undock
If a composite is used/inserted in the DOM, the corresponding model is
docked/linked and when removing from the DOM, the corresponding model is
undocked/unlinked. In both cases the model can optionally implement appropriate
methods. The method `dock` is executed before rendering, before inserting the
composite into the DOM, or after loading the page during initial rendering, and
can be used to prepare the view. The method `undock` is executed after the
composite is removed from the DOM and can be used to postprocess/clean the view.
```javascript
const model = {
dock() {
...
},
undock() {
...
}
};
```
```html
<html>
<body>
<div id="model" composite>
...
</div>
</body>
</html>
```
For composites in combination with a [condition](markup.md#condition), the call
of the methods depends on the result of the condition.
[Learn more](mvc.md#dock)
#### Synchronization
In addition to the static linking and assignment of HTML elements to JavaScript
objects (models), the view model binding also includes the synchronization of
values between the HTML elements and the fields of the JavaScript object. The
synchronization depends on events that are declared for the HTML element with
the attribute [events](markup.md#events) and so the synchronization is executed
only when one of the defined events occurs.
[Learn more](mvc.md#synchronization)
#### Validation
The synchronization of values between view (HTML elements) and the fields of
JavaScript models can be monitored and controlled by validation. Validation is
declared in HTML via the [validate](markup.md#validate) attribute in combination
with the [events](markup.md#events) attribute and requires a corresponding
validation method in the JavaScript object (model).
[Learn more](mvc.md#validation)
#### Events
Events, more precisely the interaction between view and model, are also
considered during view model binding. The methods for interaction will be
implemented only in the model. In the markup itself, no declaration is required.
The view model binding knows the available events for HTML. During binding, the
model is searched for corresponding methods that will be registered as event
listeners.
```javascript
const contact = {
mail: {
onClick(event) {
const mail = "mailto:mail@local?subject=Test&body=Greetings";
document.location.href = mail;
return false;
}
}
};
```
```html
<html>
<body>
<div id="contact" composite>
<p>
Example for use of events.
</p>
<button id="mail">
Click Me!
</button>
</div>
</body>
</html>
```
[Learn more](mvc.md#events)
## Routing
The presentation of the page can be organized in Seanox aspect-js in views,
which are addressed via paths (routes). For this purpose, the routing supports a
hierarchical directory structure based on the IDs of the nested composites in
the markup. The routing then controls the visibility and permission for
accessing the views via paths - the so-called view flow. For the view flow and
the permission, the routing actively uses the DOM to insert and remove the views
depending on the situation.
```
+-----------------------------------------------+
| Page (#) |
| +-----------------------------------------+ |
| | View A (#A) | |
| | +-----------------------------------+ | |
| | | View B (#A#B) | | |
| | | +-----------------------------+ | | |
| | | | View C (#A#B#C) | | | |
| | | +-----------------------------+ | | |
| | +-----------------------------------+ | |
| +-----------------------------------------+ |
+-----------------------------------------------+
```
### Page
In a single page application, the page is the elementary framework and runtime
environment of the entire application.
### View
A view is the primary projection of models/components/content. This projection
can contain additional substructures in the form of views and sub-views. Views
can be static or path-controlled. Paths address the complete chain of nested
views and display the parent views in addition to the target view.
### 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.
[Learn more](routing.md#routing)
## Navigation
Navigation is based on paths that use the hash in the URL. It is 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.route(path)` and `Routing.forward(path)`.
```html
<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>
```
```javascript
Routing.route("#a#b#c");
Routing.route("##");
Routing.route("##x");
```
```javascript
Routing.forward("#a#b#c");
Routing.forward("##");
Routing.forward("##x");
```
In difference to the navigate method, the forwarding is executed directly
instead of triggering an 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)`.
```javascript
Routing.route("x#y#z");
Routing.forward("x#y#z");
```
[Learn more](routing.md#navigation)
## Permission Concept
The permission concept is based on permit methods in the models, which are
called each (re-)rendering if the model has implemented the permit method.
[Learn more](routing.md#permission-concept)
## Interceptors
TODO:
Interceptors take effect very early and before the path is checked and optimized
by routing. The passed parameter is a meta-object with the triggered oldHash and
newHash.
Interceptors are designed to execute according to the order in which they are
registered. Each interceptor is always evaluated and executed if it matches the
specified criteria. Notably, interceptors do not create any entries in the
browser history. They have the capability to modify the new hash/path, e.g.
using the methods `window.history.replaceState()` or `window.location.replace()`
for such modifications. Following interceptors will operate on this potentially
altered hash/path. If any interceptor returns false explicitly, it will
terminate the logic within the hashchange event.
[Learn more](routing.md#interceptors)
### Paths
Paths are used for navigation, routing and controlling the view flow. The target
can be a view or a function if using interceptors. 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 also supported here.
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 (`#`).
[Learn more](routing.md#paths)
## Components
Seanox aspect-js is designed for a modular and component-based architecture. For
this purpose, the framework supports declarative marking of components in the
markup as well as automatic mechanisms for view-model binding and loading of
outsourced resources at runtime.
[Learn more](composite.md)
## Scripting
Seanox aspect-js uses composite JavaScript. A dialect based on the browser
JavaScript, enriched with [macros](#macros) -- a simple meta-syntax.
Composite JavaScript, which includes the modules, is not inserted as an element,
but executed directly with the eval method. Since an isolated and not the global
scope is used for this, variables, constants and methods are not directly usable
globally or across, which is why, among other things, the Composite-JavaScript
was enriched with [macros](#macros), which take over such tasks, among other
things.
[Learn more](scripting.md)
## Reactivity Rendering
In the reactive approach, changes to the data objects (models) trigger a partial
refresh of the consumers in the view. Consumers are all expressions that have
read access to the changed value of a data object. In the view, expressions can
be used in the HTML elements and in the free text. The data objects must then
use `Reactive(object)` or `Object.prototype.reactive()` for reactive rendering.
```javascript
const Model = {
value: ...
}.reactive();
```
In this example, the renderer automatically updates all HTML elements in the
DOM, which includes free-text that uses the value property from the model
directly or indirectly in an expression when the value of the value property
changes or, more precisely, when a changed value has been set final in the data
object, which can be relevant when using getters and setters.
Reactive works permanently recursively on all object levels and also on the
objects which are added later as values. Even if these objects do not explicitly
use Reactive, new instances are created for the referenced objects. Initiating
objects and reactive models are logically decoupled and are synchronized
bidirectionally. In contrast, views and reactive models, like models internally,
use proxies that behave and can be used like the initiating originals. However,
proxies are separate instances that are compatible but not identical to the
initiating object. This logical separation is necessary so that the renderer can
generate appropriate notifications when data changes and thus update the
consumers in the view.
[Learn more](reactive.md)
## API Extensions
The JavaScript API for Seanox aspect-js has been extended by some general
functions.
- [Namespace](extension.md#namespace)
- [Element](extension.md#element)
- [Math](extension.md#math)
- [Object](extension.md#object)
- [RegExp](extension.md#regexp)
- [String](extension.md#string)
- [window](extension.md#window)
- [XMLHttpRequest](extension.md#xmlhttprequest)
[Learn more](extension.md)
## Events
Seanox aspect-js provides various events that can be used to implement
extensions and to notify the application of certain operating states of the
framework and runtime environment.
[Learn more](events.md)
## Test
The Test API supports the implementation and execution of integration tests and
can be used for suites, scenarios and single test cases.
As a modular part of Seanox aspect-js, the Test API is included in all releases
except the core versions. Because the test API causes some special features in
terms of error handling and console output, the test API has to be activated
consciously at runtime.
```javascript
Test.activate();
Test.create({test() {
...
}});
Test.start();
```
[Learn more](test.md)
### Task
The smallest component in an integration test, used here as 'task', because
'case' is a keyword in JavaScript. It can be implemented alone, but is always
used in a scenario.
```javascript
Test.activate();
Test.create({test() {
Assert.assertTrue(true);
}});
Test.start();
```
Task is primarily a meta-object.
```
{name:..., test:..., timeout:..., expected:..., ignore:...}
```
[Learn more](test.m#task)
### Scenario
A scenario is a sequence of a lot of test cases (tasks).
```javascript
Test.activate();
Test.create({test() {
Assert.assertTrue(true);
}});
Test.create({name:"example", timeout:1000, test() {
Assert.assertTrue(true);
}});
Test.create({error:Error test() {
throw new Error();
}});
Test.create({error:/^My Error/i, test() {
throw new Error("My Error");
}});
Test.create({ignore:true, test() {
Assert.assertTrue(true);
}});
Test.start();
```
[Learn more](test.md#szenario)
### Suite
A suite is a complex bundle of different test cases, scenarios and other suites.
Usually a suite consists of different files, which then represent a complex
test. An example of a good suite is a cascade of different files und the test
can be started in any file and place. This makes it possible to perform the
integration test on different levels and with different complexity.
[Learn more](test.m#suite)
### Assert
The test cases are implemented with assertions. The Test API provides elementary
assertions, you can implement more. The function is simple. If an assertion was
not `true`, an error is thrown.
```javascript
Test.activate();
Test.create({test() {
Assert.assertTrue(true);
Assert.assertTrue("message", true);
Assert.assertFalse(false);
Assert.assertFalse("message", false);
Assert.assertEquals(expected, value);
Assert.assertEquals("message", expected, value);
Assert.assertNotEquals(unexpected, value);
Assert.assertNotEquals("message", unexpected, value);
Assert.assertSame(expected, value);
Assert.assertSame("message", expected, value);
Assert.assertNotSame(unexpected, value);
Assert.assertNotSame("message", unexpected, value);
Assert.assertNull(null);
Assert.assertNull("message", null);
Assert.assertNotNull(null);
Assert.assertNotNull("message", null);
Assert.fail();
Assert.fail("message");
}});
Test.start();
```
[Learn more](test.m#assert)
### Configuration
Optionally, the Test API can be configured with each start.
A meta-object is expected as parameter. The configuration contained in it is
partially adopted and the unknown is ignored.
```javascript
Test.start({auto: boolean, ouput: {...}, monitor: {...}});
```
[Learn more](test.m#configuration)
### Monitoring
Monitoring monitors the progress of the test and is informed of the various
steps and statuses during execution. The monitor is optional. Without this, the
console is used to output information about the test process.
[Learn more](test.m#monitoring)
### Control
The test progress and the execution of the tests can be controlled by the
Test API.
```javascript
Test.start();
Test.start({auto: boolean});
```
The start can be done manually or when using `auto = true`, by loading the
page. If the page is already loaded, the parameter `auto` is ignored and the
start is executed immediately.
Further methods for controlling and monitoring test execution are available.
[Learn more](test.m#control)
### Events
Events and their callback methods are another way of monitoring test execution.
The callback methods are registered at the Test API for corresponding events and
then work similar to the monitor.
```javascript
Test.listen(Test.EVENT_START, function(event, status) {
...
});
Test.listen(Test.EVENT_PERFORM, function(event, status) {
...
});
Test.listen(Test.EVENT_FINISH, function(event, status) {
...
});
```
[Learn more](test.m#events)
- - -
◁ [Motivation](motivation.md)
≡ [Table of Contents](README.md#introduction)
[Expression Language](expression.md) ▷