A data-first, event driven, parent-child structured javascript front-end framework
"User Controls" are created as JavaScript classes. (Class inheritance is supported.) Child controls are added as key / value pairs to parent controls.
Identifier tags @{identifier}
can be used in the control's HTML to easily create references to elements and to link element attributes / text content to control class properties. Supported element attributes are automatically updated on class property value changes, and supported input elements will automatically update the class property value and notify the change (see (#notifying-property-changes-externally)).
When tagging elements with an @{identifier}
id, a new class property is created with the javascript object reference to the element. When tagging element attributes / text content with an @{identifier}
tag, modular-ui will attempt to link the element attribute's value to an existing class property named the same as the @{identifier}
text.
Identifiers may only consist of the following characters: _ (underscore), a to z and A to Z.
Important
Take care not to use property names as element id @{identifier}
tags. modular-ui creates new properties for element references, and will fail to do so if the property already exists.
Empty attributes
Attributes such as hidden
and checked
are not set with a value in html. modular-ui sees these attributes as 'JavaScript only' attributes and removes it from the html template before adding it to the DOM, but sets the value of the attribute through JavaScript after the html has been inserted into the DOM. Empty attributes should be linked to class properties the same as normal attributes adding the @{identifier}
tag (e.g. checked=@{checkedProperty}
).
Example
class demo extends ui {
constructor() {
super();
this.coolProperty = 'Default cool value';
this.anotherCoolProperty = 'The second cool value';
}
get html() {
return `
<span id="@{_coolElement}">@{coolProperty}</span>
<label>@{anotherCoolProperty}</label>
<input type="text" value="@{anotherCoolProperty}"/>`;
}
Init() {
// Do some amazing things with the element
this._coolElement.style.color = 'green';
// Update property to automatically update the element value
this.coolProperty = 'Another amazing value';
}
}
In the above example the label text content and text input value will both be linked to the 'anotherCoolProperty' class property.
modular-ui makes use of a parent-child structure, where child controls are added as properties to parent controls.
Example:
{
Parent: {
controlType: "someControl",
property1: "value1",
property2: "value2",
child1: {
controlType: "someOtherControl",
childProperty1: "some value",
childProperty2: "some value"
},
child2: {
controlType: "evenAnotherControl",
childProperty1: "some value",
childProperty2: "some value",
grandChild1: {
controlType: "someControl",
grandChildProperty1: "val"
}
}
}
}
Including child controls' HTML output in the DOM can be done in one of two ways:
get html() {
return `
<span>Simple control containing child controls</span>
<div id="@{_controlsDiv}"</div>`
}
2) Add a div to the parent control with a custom javascript reference, and include the property name in Set().
This method is useful when child controls need to be placed in more than one div in the parent control.
Parent control:
get html() {
return `
<span>Simple control containing child controls</span>
<div id="@{_preferredPropertyName}"></div>`
}
Creating the child through Set():
parent.Set({
child1: {
controlType: "childControl",
parentElement: "_preferredPropertyName"
}
});
On control creation through Set(), properties not starting with "_" of boolean, string, number or array types are automatically equipped with getters and setters. The setters fires an event with the property name and updated value when the property value is set. The property values are stored in the control._properties object.
Set also triggers the Update(propertyName) override function when a property is set. This is however depreciated, and the events generated by the setters should be used instead to handle property value changes.
this.on('propertyName', val => {
// implementing logic to handle value change
})
Note: property change events are emitted locally only (see Events - scope = local).
When calling this.NotifyProperty('property_name')
, modular-ui will fire a 'data' event from the top level control containing the full path to the property.
Note
When using @{identifier}
tags in user editable HTML element attributes, modular-ui will automatically call 'NotifyProperty' for the associated class property when the element attribute value is changed by the user (through HTML data binding) or when the property is set with JavaScript.
Example
// This is the top level control created in the main JavaScript file
control.on('data', data => {
console.log(data);
});
console output:
{
grandParent: {
parent: {
child: {
coolProperty: 'new cool value'
}
}
}
}
moduler-ui implements a configurable event emitter.
Emitting a basic (local) event:
control.emit('eventName', 'some event data');
The scope can be passed to the event emitter as the third parameter:
- local (default): Only fire the event on the local control (where it has be emitted).
- bubble: Fire the event on the local control, and all its ancestors.
- top: Only fire the event on the top level parent control.
- local_top: Fire the event on the local control as well as on the top level parent control.
Including the scope:
control.emit('eventName', 'some event data', 'bubble');
Subscribing to events:
control.on('eventName', data =< {
// callback logic
});
// Cache the callback function to unsubscribe later
let c = control.on('eventName', data =< {
// callback logic
});
// Unsubscribe from event
control.off('eventName', c);
Options can optionally be passed as a JavaScript object
- immediate: true - (only for class property change events) Calls the 'listener' callback function immediately on subscription with the current value of the property (if existing);
- caller: [caller control] - Subscribes to the 'remove' event of the caller, and automatically unsubscribes from the event when the caller is removed. This helps to prevent uncleared references to the removed control's callback functions.
control.on('eventName', data =< {
// callback logic
}, { immediate: true, caller: this });
Subscribing to events for only one time. (After the event has fired, automatically unsubscribe.)
control.once('eventName', data =< {
// callback logic
});
// Cache the callback function to unsubscribe later
let c = control.once('eventName', data =< {
// callback logic
});
// Unsubscribe from event
control.off('eventName', c);
Options can optionally be passed as a JavaScript object
- caller: [caller control] - Subscribes to the 'remove' event of the caller, and automatically unsubscribes from the event when the caller is removed. This helps to prevent uncleared references to the removed control's callback functions.
control.once('eventName', data =< {
// callback logic
}, { caller: this });
Visual sorting of child controls can be done by setting the parent control's parent.orderBy
property to the name of the child controls property by who's values the child controls should be sorted. The sort order can be changed from ascending to decending by setting parent.orderAsc
to false. Ordering applied to existing child controls, and also applied when new child controls are added to the parent control.
Example: The child controls have a property named 'displayOrder'.
// Order child controls descending by displayOrder
parent.orderAsc = false;
parent.orderBy = 'displayOrder';
Visual filtering of child controls can be done through the parent control's parent.filter()
method. To apply a filter, pass a predicate function to the parent.filter()
function. To clear the filter, call parent.filter()
without any parameters. Visual filtering does not remove controls, but hides or shows the child control HTML elements. Filtering is applied to existing child controls, and also applied when new child controls are added to the parent control.
Example: The child controls have a property named 'age'.
// Filter to only show child controls with age > 10
parent.filter(child => child.age > 10);
// Clear the filter
parent.filter();
Example: The child controls have a property named 'age' and 'length'.
// Filter to only show child controls with age > 10
parent.filter(child => child.age > 10 && child.length > 162, {monitor: ["age", "length"]});
// Clear the filter
parent.filter();
To order the child controls
Emitted from a control after removal. The data returned is the control object
Emitted from a parent after a child control has been added, but before Set() is called on the child control.The data returned is the added control object.
Emitted after a control is initialized.
Change the display to show the control.
Change the display to hide the control.
use standard javascript to extend a class
class ClassA extends ClassB {
...
}
modular-ui build in function to extend a javascript class with muiltiple other classes
- Importand to note that if more that one superclass contains a property/ function that has the same name, it will be overwritten by the last class containing that propery/ function
class ClassA extends _uiClasses(ClassB, ClassC, ClassD) {
...
}
- Set() Performance improvement: Cache object paths on first call.
- Document built-in events