@rivierasolutions/state.js
v1.0.4
Published
A Passive View MVC framework for modern web applications.
Maintainers
Readme
state.js
An HTML - javascript framework that re-introduces the MVC (Model-View-Controller) architectural pattern into modern web applications.
Features
MVC
State.js follows the philosphy that even the simplest web application, like any other standalone application, features:
- a View (the HTML document),
- a Controller (the javascript code controlling the View)
- a Model (the javascript code defining content and processes relevant to the application).
State.js handles interactions between the View and the Controller. Interactions between the Controller and the Model may be sufficiently handled by existing frameworks (e.g. Redux for complex cases) or custom javascript code (for simple cases), and are thus out of scope of this framewrok.
The Passive View
State.js introduces the concept of a Passive View - A View defined only by it's layout and state, which specifically does not define any logic.
All logic that controls how the state or layout of the View is transformed throughout it's lifecycle is delegated to the Controller,
and should be decoupled and separate from the View.
This concept stands in stark contrast to leading web development frameworks like:
- React - Where the View's layout and state are tightly intertwined with the Controller's logic inside JSX components.
- Angular, Vue - Where View and Controller separation exists, however logic may easily leak into the View's layout
through even simple directive expressions like
<div *ngIf="isLoaded && getDataItems() > 0"></div>. Such leaks dramatically increase the complexity of the View - Controller Contract, and thus introduce tight coupling between the View and Controller.
The View State: a View - Controller Contract
State.js introduces the View State - A JSON object defined in the View, that serves as a Contract between the View and the Controller. Hence:
- The Controller may implement crucial transformations of the View's layout required by the web application exclusively by updating the View state.
- The View may define crucial endpoints for Controller interaction exclusively by defining the View State.
- A UI/UX designer is able to develop and test the View's layout without a coupled Controller by manually modifying the View State of a rendered View.
- A web developer is able to develop and test the Controller's logic without touching the View's layout, or by working on a stub View that defines an equivalent View State.
State.js feature summary
- Enables full decoupling of the View (HTML) from the Controller (javascript code) in web applications.
- Introduces the View State - a Contract between the View and the Controller, stored within the DOM tree as a JSON object.
- The View State is defined within the View (HTML), using sytnax similar to JSONpath queries in special HTML attribures or HTML elements.
- The Controller (javascript code) may retrieve and update the view state at any time.
- Supports web components
- Features a Visual Studio Code extension to automatically generate .d.ts Contracts for HTML files (Views) for typescript and type-aware javascript Controller development.
- Features a Google Chrome extension to manually manipulate the View State of a HTML file (View) without a coupled Controller for interactive, Controllerless View design (also compatible with MS Edge).
Installation
Install throught npm
npm install @rivierasolutions/state.jsOr include the minified version directly in your View's layout:
<script src="https://cdn.jsdelivr.net/gh/rivierasolutions/state.js@latest/dist/state.min.js" defer></script>Running the examples
- Clone, install & serve (on port 5500)
git clone https://github.com/rivierasolutions/state.js.git
cd state.js
npm install
npm run serve- Navigate to e.g.
http://localhost:5500/examples/helloWorld/index.htmlin your favourite browser.
Usage
Consider a basic View index.html...
<!doctype html>
<html lang="en">
<head>
<title>Hello state.js</title>
<script src="https://cdn.jsdelivr.net/gh/rivierasolutions/state.js@latest/dist/state.min.js" defer></script>
<script src="index.controller.js" defer></script>
</head>
<body>
<h1 state-content="@.headerMessage">Hello</h1>
<h2 state-if="@.showSubheader" state-content="@.subHeaderMessage"></h2>
<button state-listen="@.onToggleSubheader">
<state state-if="@.showSubheader">Hide subheader</state>
<state state-if-not="@.showSubheader">Show subheader</state>
</button>
</body>
</html>And a coupled Controller index.controller.js...
document.addEventListener('StateLoaded', () => {
document.state.listener('toggle', toggleSubheader);
document.state.update({
headerMessage: 'Hello World',
showSubheader: true,
subHeaderMessage: 'from state.js',
onToggleSubheader: { 'click': 'toggle' }
});
function toggleSubheader(event, context) {
document.state.update({ showSubheader: !document.state.current().showSubheader });
}
});Based on the path definitions (similar to JSONpath queries) defined in the special HTML attributes state-content, state-if, state-if-not and state-listen,
the initial View State of the View index.html is defined as:
{
"headerMessage": "Hello",
"showSubheader": false,
"subHeaderMessage": "",
"onToggleSubheader": {}
}Once the View State is fully loaded and the StateLoaded event if dispatched from the window.document object,
the Controller index.controller.js may:
- retrieve the current View State by calling
document.state.current() - update the View State by calling
document.state.update(myNewState).
Notice how upon the first document.state.update(...) call, the View State is updated by the Controller to:
{
"headerMessage": "Hello World",
"showSubheader": true,
"subHeaderMessage": "from state.js",
"onToggleSubheader": { "click": "toggleSubheader" }
}State.js automatically propagates the updated View State across the View's layout.
In particular:
- The
state-if="@.showSubheader"attribute adds or removes it's element from the DOM tree based on the truthiness of theshowSubheaderstate field. - The
state-content="@.subHeaderMessage"attribute renders text content in it's parent element based on the value of thesubHeaderMessagestate field. - The
state-listen="@.onToggleSubheader"attribute attaches DOM event listeners to it's parent element based on the keys and values of theonToggleSubheaderstate field. - The
<state>tag allows definingstate-attributes on text blocks without wrapping them in any other HTML tag.
View API
state-content="[path]" (html attribute)
Renders the value of the state field at [path] as this DOM element's text content.
The state field [path] is initialized to a string containing this DOM element's text content.
Example:
<p state-content="@.myParagraph">
Initial paragraph text
</p>state-if="[path]" (html attribute)
Removes this DOM element and it's subtree from the DOM if the state field at [path] evaluates to falsy.
The state field at [path] is initialized to false.
Example:
<p state-if="@.loading">
<progress> loading... </progress>
</p>Remarks
When the state field at [path] evaluates to falsy, the DOM element and it's subtree is not deleted.
It is instead wrapped in a <template state-if="[path]" state-placeholder></template> element at the same position in the DOM tree.
Conversely, this placeholder element's content is unwrapped when [path] evaluates to truthy.
state-if-not="[path]" (html attribute)
Removes this DOM element and it's subtree from the DOM if the state field at [path] evaluates to truthy.
The state field at [path] is initialized to false.
Example:
<p state-if-not="@.loading">
<div state-content="@.loadedContent"></div>
</p>Remarks
When the state field at [path] evaluates to truthy, the DOM element and it's subtree is not deleted.
It is instead wrapped in a <template state-if="[path]" state-placeholder></template> element at the same position in the DOM tree.
Conversely, this placeholder element's content is unwrapped when [path] evaluates to falsy.
state-foreach="[path]" (html attribute)
Renders this DOM element and it's subtree for each element of the array at the state field [path].
If [path] is not an array but is truthy, it is treated as an array with 1 element.
The state field at [path] is initialized to an empty array [].
Example:
<ul state-foreach="@.myList">
<li>
<span state-content="@.$index">0</span>
<span state-content="@.listItem">list item text here</span>
</li>
</ul>Remarks
When the View's layout is analyzed to load the initial View State, state.js will wrap any DOM elements
containing the state-foreach attribute in a <tempalte state-foreach="[path]" state-placeholder>[...]</tempalte>
tag at the same position in the DOM tree.
The DOM subtree of this placeholder element will subsequently be cloned after it for each element of the array at [path].
Paths starting with @. (of any state- attributes inside the state-foreach element's DOM subtree)
will be resolved relative to their respective array item, (i.e. their paths within the View State object will start at the array item).
To reference state fields beyond the array item, start the state- attribute's path with $.. This will always resolve the state attribute's path
relative to the root of the View State object regardless of scope (see state-scope="[path]" for more information).
Example:
<ul state-foreach="@.myList">
<li>
<span state-content="@.listItemText">
Updated with document.state.currrent().myList[...].listItemText
(varies per item)
</span>
<span state-content="$.footer">
Updated with document.state.current().footer
(equal for all items)
</span>
</li>
</ul>The special $index field, containing the array item's current index will be appended to each array item of state field [path].
state-listen="[path]" (html attribute)
Attaches DOM Event listeners defined in the state field [path] to this DOM element.
This provides any Controller attached to this View a way to interact with this element's input.
Example:
<button state-listen="@.onButtonEvents">My Button</button>document.state.listener({
'onClick': (event, context) => console.log(`Clicked button: ${context.id}`),
'onHover': (event, context) => console.log(`Hovered over button: ${context.id}`)
});
document.state.update({
onButtonEvents: {
'click': 'onClick',
'mouseover': 'onHover',
context: { id: 'My-Wonderfull-Button' }
}
});Remarks
The [path] state field is assumed to be an Object containing keys defined as DOM event names,
and values defined as names of javascript functions that will be triggered on the respective DOM event.
All functions used in [path] field must first have their names declared using the [element].state.listener()
method (see [element].state.listener(nameOrDict, fn) for details).
The special context field of the [path] state field will be passed as the 2nd argument of the called
listener function (the 1st argument being the DOM event itself).
Event listeners are added to DOM elements based on the keys and values in the [path] state field Object using:
DOMelement.addEventListener(key, getListenerByName(value));If a key-value pair is removed, or a value is updated to a different function reference, the previous event listener will be automatically removed by state.js.
state-attr-[name]="[path]" (html attribute)
Set's the value of attribute [name] on this DOM element to the value of the state field at [path].
The state field at [path] is initialized the value of attribute [name] on this DOM element.
Example:
<input name="myInput" type="text" state-attr-id="@.myInputId" state-attr-value="@.myInputValue">Remarks
Some attributes, like the value attribute for <input> tags, define initial values for special DOM element properties,
which will subsequently be updated based on user interaction.
State.js automatically handles two-way updates between these properties and the respective View State fields
for the following HTML element - attribute pairs:
<input>andvalue:<input type="text" state-attr-value="@.myValue"><textarea>andvalue:<textarea state-attr-value="@.myText"><select>andvalue:<select state-attr-value="@.mySelectedItem"><input>withtype="checkbox"andchecked:<input type="checkbox" state-attr-checked-if="@.isChecked"><input>withtype="file"andvalue:<input type="file" state-attr-value="@.filesToUpload"><details>andopen:<details state-attr-open-if="@.isOpen">[...]</details>- any tag with the
contenteditableattribute andvalue:<div contenteditable state-attr-value="@.myEditable" >Edit me.../div>
state-attr-[name]-if="[path]" (html attribute)
Add the attribute [name] to this DOM element if the value of the state field at [path] is truthy. Remove the attribute otherwise.
The state field at [path] is initialized to false.
Example:
<input name="myCheckbox" type="checkbox" state-attr-checked-if="@.isChecked">state-attr-[name]-if-not="[path]" (html attribute)
Add the attribute [name] to this DOM element if the value of the state field at [path] is falsy. Remove the attribute otherwise.
The state field at [path] is initialized to false.
Example:
<input name="myCheckbox" type="checkbox" state-attr-checked-if-not="@.notChecked">state-class-[name]-if="[path]" (html attribute)
Add the CSS class [name] to this DOM elements class attribute if the value of the state field at [path] is truthy. Remove the CSS class otherwise.
The state field at [path] is initialized to true if the DOM element contains CSS class [name], false otherwise.
Example:
<p state-class-error-if="@.hasError">
This text will be styled in case of an error.
</p>state-class-[name]="[path]" (html attribute)
Alias of state-class-[name]-if="[path]".
Example:
<p state-class-error="@.hasError">
This text will be styled in case of an error.
</p>state-class-[name]-if-not="[path]" (html attribute)
Add the CSS class [name] to this DOM elements class attribute if the value of the state field at [path] is falsy. Remove the CSS class otherwise.
The state field at [path] is initialized to false if the DOM element contains CSS class [name], true otherwise.
Example:
<p state-class-error-if-not="@.hasError">
This text will be styled if there are no errors.
</p><state></state> (html element)
A transparent container for defining state attributes.
Example:
<p>
Hello <state state-content="@.helloWhat">World</state>
</p><state-compose tag="[tagName]" src="[uri]"></state-compose> (html element)
Declares that each custom HTML element <[tagName]> will be filled with the View defined at [uri].
The [uri] may be a URL to an external HTML document, as well as an ID of a <template> element within
this HTML document.
Example:
<html>
<head>
<state-compose tag="app-my-element" src="my-element"></state-compose>
<state-compose tag="app-my-other-element" src="/compoonents/myOtherElement.html"></state-compose>
</head>
<body>
<app-my-elemment></app-my-elemment>
<app-my-other-elemment></app-my-other-elemment>
<template id="my-element">
<p state-content="@.componentStateContent">My own state!</p>
</template>
</body>
</html>Remarks
A new View State will be created of each element <[tagName]> (see [element].state.create(element) for details).
While the created View State is independent from the parent View's state by default, it may be referenced
in the parent View State using the state-pass attributte (see state-pass="[path]" for details).
state-compose elements may be defined anywhere in the View's layout (not necessarily in the <head> element).
When a state-compose element is defined multiple times for the same [tagName] in a View, the last
state-compose element is considered valid.
Cirular dependecies between Views declared with state-compose may cause infinite nesting (e.g. View A uses state-compose to include View B,
which in turn uses state-compose to include View A). To protect against such scenarios, state.js defines the maximum nesting depth = 20,
beyond which HTML elements with state-compose declarations will be ignored.
state-pass="[path]" (html attribute)
When defined on a custom HTML element declared in <state-compose>, this attribute will reference this custom element's
View State in this View's state at [path]. This means that:
- The entire View State of this custom HTML element will be included in the parent View State at
[path]. - When the state field
[path]is updated in the parent View State, those updates will be propagated to this custom HTML element's View State - WHen this custom HTML element's View State is updated, those updates will be propagated to the parent View's View State.
Example:
<html>
<head>
<state-compose tag="app-my-element" src="my-element"></state-compose>
</head>
<body>
<h1 state-content="@.myHeader"></h1>
<app-my-elemment state-pass="@.myElementState"></app-my-elemment>
<template id="my-element">
<p>
<state state-content="@.sommeText"></state>
<state state-content="@.someNumber"></state>
</p>
</template>
</body>
</html>await document.state.update({
myHeader: 'Hello World',
myElementState: {
sommeText: 'Text inside my element',
someNumber: 123
}
});
const state = document.state.current();
// state is equal to:
// {
// myHeader: 'Hello World',
// myElementState: {
// sommeText: 'Text inside my element',
// someNumber: 123
// }
// }Remarks
See <state-compose tag="[tagName]" src="[uri]"></state-compose> for further details.
state-ignore (html attribute)
When the View's layout is analyzed to load the initial View State, state.js will not analyze this DOM element or it's subtree.
Example:
<div state-ignore>
<ul>
<li>This element will be ignored by state.js</li>
<li>This one as well</li>
</ul>
<div>And this one</div>
</div>Remarks
To completely disable loading any state for the whole HTML document, place a state-ignore attribute on the <html> element.
Note that the StateLoaded DOM event will still be dispatched, and the document.state object will still be available,
but the initial state will be an empty object {}, any state- attributes in the document will be ignored by this View State,
and any calls to document.state.update(newState) will have no effect.
Example:
<html state-ignore>
<body>
No initial View State.
</body>
</html>Note that even if the View State for the whole HTMl document is ignored using <html state-ignore>, you may still
create new View States for DOM subtrees within the document using document.state.create()
(see [element].state.create(element) for details).
state-scope="[path]" (html attribute)
For internal use only. When this attribute is defined on an HTML element, paths of all state- attributes defined
on this element and it's subtree starting with @. will be resolved as relative to [path].[path] is then defined as the scope of this element's DOM tree in the View State.
Paths starting with $. will always be treated as 'absolute' paths, i.e. resolved relative to the root
of the View State object.
A Path starting with @. that does not have a state-scope attribute on any of it's parent elements
will be resolved as if it would start with $. (i.e. relative to the root of the View State object).
Example:
<body>
<h1 state-content="@.header"></h1>
<div state-scope="@.myContent.myScope">
<h2 state-content="$.header"><h2>
<span state-content="@.text"></span>
<div state-scope="@.mySubScope">
<h3 state-content="$.header"><h3>
<span state-content="@.subtext"></span>
</div>
<div>
</body>document.addEventListener('StateLoaded', () => {
document.state.update({
header: 'Hello from all headers!',
myContent: {
myScope: {
text: 'Hello from a scope!',
mySubScope: {
subtext: 'Hello from a sub-scope!'
}
}
}
});
});Remarks
When DOM subtrees are inserted for items of an Array referenced in a state-foreach attribute, the root element
of each subtree will automatically receive a state-scope attribute with a path pointing it's respective array item.
Example:
View definition:
<ul>
<li state-foreach="@.items">
<span state-content="@.$index"></span>:
<span state-content="@.text"></span>
</li>
<ul>Live View with applied View State:
<ul>
<template state-foreach="@.items" state-placeholder>
<li>
<span state-content="@.$index"></span>:
<span state-content="@.text"></span>
</li>
</template>
<li state-scope="$.items[0]">
<span state-content="@.$index">0</span>:
<span state-content="@.text">Hello</span>
</li>
<li state-scope="$.items[1]">
<span state-content="@.$index">1</span>:
<span state-content="@.text">World</span>
</li>
<li state-scope="$.items[2]">
<span state-content="@.$index">2</span>:
<span state-content="@.text">From state.js!</span>
</li>
<ul>state-placeholder (html attribute)
For internal use only. This attribute will be included in <template> elements added to the DOM by state.js
to store temporary DOM subtrees (e.g. to support the functionality of state-if or state-foreach attributes).
Are path expressions in state.js JSONPath?
Though very similar as first glance, path expressions in state.js are not JSONPath expressions (as defined in RFC 9535). Key differences include:
- All path expressions in
state-attributes must start with$.or@.. - The
@selector is valid at the start of any path expression, and serves as the identifier of the current scope's root (seestate-scope="[path]"). - All path expressions in
state-attributes must resolve with a single object or value. This means that:- Array ranges
$.list[2:3]and selections$.list[2,3,4]are not allowed. - Array filters of any kind
$.customer[?(@.age > 18)]are not allowed. - The "all children" operator
$.parentNode.*is not allowed. - The "all children named..." operator
$..anyChildNodeis not allowed.
- Array ranges
- Output mapping
$.[].{Name:name, Age:age, Hobbies:details.hobbies}instate-attributes is not allowed. - Segments may only use bracket notation for array indices (i.e.
$['store']['book'][0]['title']is not allowed, use$.store.book[0].titleinstead).
Remarks
Path expressions yielding multiple items (e.g. array ranges or filters) or remapping the output would introduce logic into the View, which is exactly what state.js is trying to prevent (see "The Passive View").
While the @ selector serves a similar purpose, it's JSONPath counterpart is defined as "valid only within filter selectors",
hence it's use at the start of a JSONPath would be invalid in the context of RFC 9535.
Bracket notation is only allowed for array indices for the sake of notation simplicity.
Controller API
[element].state (DOM element property)
The state object is the primary endpoint of state.js' Controller API. state objects are appended as properties
to Elements in the View's (i.e. a HTML document's) DOM.state objects allow the Controller to interact with a View State initialized and loaded for a given DOM subtree.
When state.js is succesfully loaded, a state object is guaranteed to be appended to the window.document property.
This represents the root View State for the entire View.
Example:
document.addEventListener('StateLoaded', () => {
const initialState = document.state.current();
document.state.update({ header: 'Hello World' });
});Remarks
The state object will also be appended to any elements for which [element].state.create(element) was called.
See [element].state.create(element) for details.
[element].state.current() (state object method)
Returns the current View State as a readonly JSON object.
Return Type
{ [key:string]: any } - the current View State.
Example:
document.addEventListener('StateLoaded', () => {
const initialState = document.state.current();
});Remarks
The Object returned by document.state.current() and all of it's properties are deep frozen using Object.freeze().
[element].state.update(newState, origin) (state object method)
Updates the current View State with either a partial object or an Array of paths and values.
Arguments
newState: { [key:string]:any } | [ { path: string, value: any } ]- Either a partialobjectthat will be merged into the current View State, or anArrayof path's within the View State to modify, and their respective new values.origin: string|undefined- This value will be passed in the detail of theStateUpdatedevent after this update has completed. Default value is"controller".
Return type
Promise<void> - The promise fulfills once the View State is completely updated.
Example:
document.addEventListener('StateLoaded', async () => {
const initialState = document.state.current();
await document.state.update({ header: 'Hello World' });
document.state.update([
{ path: '$.header', value: 'Hello Again!' }
]);
});Remarks
- Updates caused by special
state-attr-[name]="[path]"attributes, (e.g.<input state-attr-value="@.myInput">element updating it'svalueproperty) will have theoriginset tostate-attr-[name]="[path]" - Updates caused by a
state-pass="[path]"attribute propagating a View State update form a parent down to a composed child will have theoriginset tostate-pass-down="[path]" - Updates caused by a
state-pass="[path]"attribute propagating a View State update form a composed child up to a parent will have theoriginset tostate-pass-up="[path]"
[element].state.listener(nameOrDict, fn) (state object method)
Register a new listener function (or a collection of functions) for use with state-listen attributes.
Arguments
nameOrDict: string|{ [key:string]: (event: Event, context: any) => void }- When set to astring, defines the name offn. When set to anObject, defines a dictionary offunctionsto register (values) and thier names (keys).fn: ((event: Event, context: any) => void)|undefined- Defines thefunctionto register undernameOrDict(when set to astring). Ignored ifnameOrDictis set to anObject.
Return Type
void
Example:
<button state-listen="$.onAddButton">Add Item</button>
<ul>
<li state-foreach="$.items">
<state state-content="@.text">Stub text</state>
<button state-listen="@.onUpdateButton">Update</button>
<button state-listen="@.onRemoveButton">Remove</button>
</li>
<ul>document.addEventListener('StateLoaded', async () => {
document.state.listener('removeItem', removeItem);
document.state.listener({
addItem,
updateItem
});
let itemIdSequence = 0;
const items = [ _newItem('Hello'), _newItem('World') ];
document.state.update({ items, onAddButton: { 'click': 'addItem' } });
function addItem(event, context) {
items.push(_newItem('Added Item'));
document.state.update({ items });
}
function removeItem(event, context) {
const index = items.findIndex(i => i.id === context.id);
items.splice(index, 1);
document.state.update({ items });
}
function updateItem(event, context) {
const item = items.find(i => i.id === context.id);
item.text = 'Updated Item';
document.state.update({ items });
}
function _newItem(text) {
const id = ++itemIdSequence;
return {
id
text,
onUpdateButton: { 'click': 'updateItem', context: { id } },
onRemoveButton: { 'click': 'removeItem', context: { id } }
};
}
});Remarks
The state-listen attribute assumes the updated value to be an Object containing keys defined as DOM event names,
and values defined as names of javascript functions that will be triggered on the respective DOM event.
All functions passed to state-listen attributes must first have their names declared using [element].state.listener(nameOrDict, fn)
The special context field of the Object passed to state-listen attributes will be passed as the 2nd argument of the called
listener function (the 1st argument being the dispatched DOM event).
Event listeners are added to DOM elements based on the keys and values in the [path] state field Object using:
DOMelement.addEventListener(key, getListenerByName(value));If a key-value pair is removed, or a value is updated to a different function reference, the previous event listener will be automatically removed by state.js.
[element].state.create(element) (state object method)
Creates and loads a new View State on the DOM element element.
The new View State is completely independent from it's parent View State, features
it's own methods (element.state.current(), element.state.update() etc.) and dispatches it's own DOM events.
Arguments
element: Element- the DOM element for which the new View State will be created and loaded.
Return Type
Promise<ViewState> - A promise that fulfills to the new View State object once the new View State is loaded.
Example:
<html state-ignore>
<body>
<div id="container">
<h1 state-content="@.header"></h1>
</div>
<body>
</html>document.addEventListener('StateLoaded', async () => {
const myContainer = document.getElementById("container");
await document.state.create(myContainer);
myContainer.state.update({ header: 'Hello World' });
});Remarks
Once the new View State is loaded, the StateLoaded DOM event is dispatched from element.element will then contain a state object field element.state.
Even though the new View State may be referenced by it's "parent" state via the state-pass attribute,
the two states remain fundamentally independent, and will only interact with each other via the state.js API, i.e.
- The parent state will retrieve the new View State via it's
state.current()method. - The parent state will update the new View State via it's
state.update(...)method. - An update to the new View State (by other means than the parent state's
state-passattribute) will trigger a call tostate.update(...)in the parent state.
When [parent].state.create(element) is called, the View State will be created and loaded,
even if element has a state-ignore attribute.
[element].state.contract(namespace, className, wrap) (state object method)
Generates a .d.ts contract for this View State based on the state- attributes defined in the View.
The View State will be represented as an interface with all state fields (and their fields etc.) defined
as properties with their respective types.
Arguments
namepsace: string|undefined- The namespace in which the View State interface will be defined. Defaults toStateJs.Generated.className: string|undefined- The name of the View State interface. Defaults toViewState.wrap: boolean|undefined- When set to false the View State interface will be generated without it's wrapping namespace and supporting classes. Defaults totrue.
Return Type
string - The .d.ts contact containing the View State interface.
Remarks
State attributes will resolve to the following types in the View State contract:
state-content:anystate-if,state-if-not:anystate-attr-[name],state-attr-[name]-if,state-attr-[name]-if-not:anystate-class-[name],state-class-[name]-if,state-class-[name]-if-not:anystate-pass:anystate-foreach:Array<ForeachItem>(ForeachItemwill be defined as a separateinterface).state-listen:{ [key:string]: string } & { context: any }
StateLoaded (DOM Event)
Dispatched from a DOM element on which a View State is created, after the state has been created and finished loading.
Always dispatched from the document element after state.js has finished loading.
This event does not bubble up the DOM tree.
StateUpdated (DOM Event)
Dispatched from a DOM element containing a View State, after that View State has finished updating.
This event bubbles up the DOM tree.
The event's detail will contain an object { origin: string|undefined } with the value of the origin
argument passed to the state.update() call that resulted in this event.
StateComposed (DOM Event)
Dispatched from a custom HTML element after a View State has been automatically created for it.
For a View State to be automatically created, the custom HTML element must be first declared
via a <state-compose> element (see <state-compose tag="[tag]" src="[uri]"> for details).
This event bubbles up the DOM tree.
About
State.js was created by Piotr Wójcik ([email protected])
