<input class="in" type="text">
<output class="out"></output>
<script src="seemple.min.js"></script>
<script>
const app = new Seemple();
app.bindNode('x', '.in, .out');
app.x = 'A framework for single-page applications creation';
</script>
Introduction
Features
- Reactive API which allows to solve hard and confusing problems easily
- High robustness of developed apps
- The ability to refactor legacy applications without rewriting them from scratch
- Only couple of hours is needed to master the framework because of an absence of complex concepts
- One of the most user-friendly documentations among JavaScript libraries
Who needs such framework?
Seemple.js fills the gap between a junior and a senior
- For those who is new at JavaScript, wishing to master the development of single-page applications
- For full-stack devs, for whom front-end development is in the second place after the back-end
- For all those who are not satisfied with the current order of things in the universe of web development
What's the business need solved by Seemple.js framework?
It is not a secret that the barrier to entry into the web development is becoming higher, the list of requirements to a developer is bigger, a number of dependencies in projects may be up to several hundred. Despite the fact that the studying of JavaScript is very popular today, the demand for developers who can accomplish tasks successfully is greater than the supply.
Due to extreme simplicity of the framework, even novice web developers can quickly start to do small, then medium and then large web applications. This means that web studios are able to save money by hiring younger professionals, who, in turn, could not find a job before.
It is only for beginners?
Two things that present Seemple.js as a simple framework are use of selectors of bound DOM nodes when declaring a two-way data binding and an absence of any restrictions on requirements of an architecture and design patterns. In other aspects Seemple.js is a modern general-purpose framework developed using today's technologies. At least, Seemple.js may cause an interest because it uses the full potential of getters and setters in JavaScript.
Website instructions
Importance levels
The API is split up into three parts.
The first level - the most important stuff
After you've learned the most important API parts you can hardily start doing fantastic things. Classes, methods and properties marked with need to be learned primarily.
The second level - recommended to learn
If you already know the quick-start basics, you can look at less important (but still important) methods and properties of the framework.
The third level - other methods and properties
If you want to know everything about Seemple.js, turn on "Advanced mode" checkbox from the menu.
Warning. If you open a link to a method or a property of the third level of importance, the "Advanced mode" is turned on automatically.
Inaccuracies and typos
In a footer of every article of the documentation you can find a link to a source of that article. You can fix a typo directly at Github editor. If you cannot do this somehow, then select the text of the typo and press CTRL + Enter to send a message to the developer.
The documentation is written using JSDoc3 and GitHub Flavored Markdown.
Modules
If you use CommonJS you can see paths to modules at every article of the documentation. A size of resulting JavaScript bundle can be reduced by the import of needed parts of the framework.
// every static function or a class can be imported as CJS module
const SeempleArray = require('seemple/array');
const propBinder = require('seemple/binders/prop');
const bindNode = require('seemple/bindnode');
The import of the main module pulls entire framework. Usually it's not required.
const Seemple = require('seemple');
A pure module of Seemple class (without
binders
,Array
andObject
) lives at'seemple/seemple'
Examples
Hello World!
Writing your first application is very easy. You should:
1. Create an HTML file with the following content
<!DOCTYPE html>
<html>
<head>
<title>My first Seemple.js application</title>
</head>
<body>
<input type="text" class="my-input">
<div class="my-output"></div>
<script
src="https://finom.github.io/seemple/seemple.min.js">
</script>
<script src="js/app.js"></script>
</body>
</html>
2. Create the application class at js/app.js
// store html binder in a short variable
const htmlBinder = Seemple.binders.html;
// create the class which inherits Seemple
class Application extends Seemple {
constructor() {
super();
// bind a property x and the text field
this.bindNode('x', '.my-input');
// bind the property x and the ".my-output" block
this.bindNode('x', '.my-output', htmlBinder());
// if the property "х" has changed,
// inform about it in the console
this.on('change:x', () =>
console.log(`x is changed to "${this.x}"`));
}
}
const app = new Application();
3. That's it!
Now you can open the developer's console (by pressing F12) and write:
app.x = 'Hello World!';
Cool, isn't it? You can work with such magical properties directly.
Links
1. TodoMVC - a to-do list. (Source code with annotations)
2. TreeView of unlimited nesting depth.
3. Markdown editor made with few lines of code.
4. Simple SoundCloud player - music search via SoundCloud API.
5. Contact List - allows to add, remove, sort, search and change contacts.
Class Seemple
CommonJS module: 'seemple/seemple'
The class Seemple
is a core of the Seemple.js framework which is inherited by Seemple.Array, Seemple.Object (and every class of an application you create). It contains the main functionality of the framework: mediators, dependencies, two-way data binding, an event engine, etc.
This class usually isn't used directly. Instead, it is inherited by other classes.
Links
Examples
Creating of an instance
const seemple = new Seemple();
Inheritance
class MyClass extends Seemple {
constructor() {
this.sayHello();
}
sayHello() {
alert("Hello World!");
}
}
Inheritance using Seemple.Class
const MyClass = Seemple.Class({
'extends': Seemple,
constructor() {
this.sayHello();
},
sayHello() {
alert("Hello World!");
}
});
Seemple#$()
Does the same as Seemple#selectAll
Examples
this.$('.my-element');
Seemple#bindNode(key, node, binder, eventOptions)
→
object
Binds a property of an object to HTML node, implementing two-way data binding
Seemple#bindNode is the only method of the Seemple class which is responsible for changing DOM (except array renderer). It creates a bridge between value of a property and state of HTML node on the page: from a simple input to a complicated widget (the complexity of elements is unlimited). After using this method, it isn't necessary to monitor the synchronizations between model and view.
Note that the method has static alternative, which works just the same but accepts any object as the first argument, shifting rest arguments to the right.
const bindNode = require('seemple/bindnode');
const object = {};
bindNode(object, key, node, binder, eventOptions);
// instead of this.bindNode(key, node, binder, eventOptions);
The method acepts three arguments: a property name, HTML node and a binding rule (a binder). In its turn, a binder is an ordinary object and it can have the following properties: on
, getValue
, setValue
, initialize
, destroy
(Read more here: binder). All the five properties are optional. It also allows to declare one-way data bindings (any direction).
The
bindNode
method supports the many-to-many bindings. Several elements can be bound to one property and several properties can be bound to one element, including ones from different instances of various classes.
this.bindNode('myKey', '.my-element', {
on: 'click',
getValue() { ... },
setValue() { ... }
});
For example, you want to bind a property of an object to a input[type="checkbox"]
node:
this.bindNode('myKey', '.my-checkbox', {
// when is element state changed?
// - after 'click' event
on: 'click',
// how to extract element state?
// - return 'checked' value
getValue() {
return this.checked;
},
// how to set element state?
// - set 'checked' value
setValue(v) {
this.checked = !!v;
}
});
After binding is declared, you can set value of an object property in your most habitual way and HTML node (in this case, a checkbox) will change its state immediately. After clicking on the checkbox, the property value will be changed to the corresponding one as well.
// sets checked = true
this.myKey = true;
More complicated example: binding object property to jQuery UI widget
<div class="my-slider"></div>
this.bindNode('myKey', '.my-slider', {
// when is element state changed?
// - after 'slide' event
on: 'slide',
// how to extract element state?
// - return 'value' of the widget
getValue() {
return $(this).slider('option', 'value');
},
// how to set element state?
// - set 'value'
setValue(v) {
$(this).slider('option', 'value', v);
},
// how to initialize the widget?
// you can initialize the slider in any way,
// but 'initialize' function provides some syntactic sugar
initialize() {
$(this).slider({ min: 0, max: 100 });
}
});
// will set the slider value 42
this.myKey = 42;
It looks easy but you may ask a question: "What should I do to avoid writing these rules every time?". Indeed, there can be a lot of elements of the same type on the page: text fields, drop down menus, fields from the HTML5 specification as well as third party widgets (see the example above).
As observed in this documentation, the third argument is not obligatory for the ones of the bindNode
method (see below). This problem is solved by the Seemple.defaultBinders array which contains functions checking an HTML node against a set of rules and returns corresponding binder or undefined
. You get an opportunity to reduce your code a great deal, putting binding rules into a separate part of your code and to use a syntax for binding without the third argument:
this.bindNode('myKey', '.my-element');
How to do it? You should add a function checking your element against a set of rules to the beginning of the Seemple.defaultBinders array.
const checkboxBinder = () => {
return {
on: 'click',
getValue() {
return this.checked;
},
setValue(v) {
this.checked = !!v;
}
}
};
// the unshift method adds the function
// to the beginning of the Seemple.defaultBinders array
Seemple.defaultBinders.unshift(node => {
// check if the element is a checkbox
if(node.tagName == 'INPUT' && node.type == 'checkbox') {
// if checking is OK, return a new binder
return checkboxBinder();
}
});
this.bindNode('myKey', '.my-checkbox');
this.myKey = true;
What should you do if you need to pass arguments for initializing some plugin or a widget? You can call the function that returns a binder manually.
const uiSlider = (min, max) => {
return {
on: 'slide',
getValue() {
return $(this).slider('option', 'value');
},
setValue(v) {
$(this).slider('option', 'value', v);
},
initialize() {
$(this).slider({ min: min, max: max });
}
}
};
this.bindNode('myKey1', '.my-slider1', uiSlider(0, 100));
this.bindNode('myKey2', '.my-slider2', uiSlider(1, 1000));
this.myKey1 = 42;
this.myKey2 = 999;
For global access to the binder you can add new property to Seemple.binders.
Seemple.binders.uiSlider = uiSlider;
// ...
this.bindNode('myKey1', '.my-slider1', Seemple.binders.uiSlider(0, 100));
this.bindNode('myKey2', '.my-slider2', Seemple.binders.uiSlider(1, 1000));
Seemple.defaultBinders OOB has a support for all form elements without any exception: select
(including multiple
), textarea
, output
, input
(including all types from the specification of HTML5: text
, checkbox
, radio
, range
, number
, date
, search
, time
, datetime
, datetime-local
, color
and others). That means it is not necessary to designate a binder for standard elements.
<input type="color" class="my-color-input">
this.bindNode('myColor', '.my-color-input');
this.myColor = '#66bb6a';
Besides, after the binding, a new non-standard :bound(KEY)
CSS selector is available for you.
this.bindNode('myKey', '.my-element');
// will find the element '.my-inner-element' inside '.my-element'
this.bindNode('myAnotherKey', ':bound(myKey) .my-inner-element');
And the syntax of possible event names is extended:
this.bindNode('myKey', '.my-element');
// will handle the click on the '.my-element' element
this.on('click::myKey', () => { ... });
// will handle the click on the '.my-element .my-inner-element'
this.on('click::myKey(.my-inner-element)', () => { ... });
If a node is not found
"Bound element is missing"
error will be thrown. Check out Seemple#bindOptionalNode.
Sandbox definition
Seemple#bindNode can associate a class instance with the "main" HTML element on the page creating so-called sandbox. It is necessary to limit the instance influence on other HTML nodes. A special property sandbox
is used for binding a sandbox.
<div class="my-sandbox">
<!-- your HTML code -->
</div>
this.bindNode('sandbox', '.my-sandbox');
The definition of the sandbox adds lots of conveniences for you. For example:
- allows to use the Seemple#select and Seemple#$ methods
- adds a new
:sandbox
selector for the Seemple#bindNode, Seemple#select, Seemple#$ methods and others - adds syntactic sugar for delegated DOM events in the Seemple#on method
Keep in mind that you can bind only one sandbox element to
sandbox
property. You can try Seemple#bindSandbox which does the same thing but removes previous binding.
// declare a sandbox
this.bindNode('sandbox', '.my-sandbox');
// .my-element is being searched for in the sandbox
this.bindNode('myKey', ':sandbox .my-element');
// it is not required to specify a key
// for the delegated events inside a sandbox
this.on('click::(.my-button)', () => { ... });
// will put the .inner-node element
// which is inside the sandbox into the console
console.log(this.$('.inner-node'));
Important features of the method and special flags
The fourth argument of bindNode
method is eventOptions
. As usual this object can include special flags or custom data which will be passed to bind
and bind:KEY
event handlers.
this.on('bind:x', evt => {
console.log(evt.foo); // bar
});
this.bindNode('x', node, binder, { foo: 'bar' });
To understand important features of bindNode
it is required to read information below but it's not required to remember all these flags.
A flag exactKey=false
If key
string includes a dot then such string will be interpreted as a path to a property of nested object. Seemple will listen all changes of given object tree.
this.a = { b: { c: 'foo' } };
this.bindNode('a.b.c', node);
this.a.b.c = 'bar'; // updates node by bar
const oldB = this.a.b;
this.a.b = { c: 'baz' }; // updates node by baz
// node is not updated because
// the connection with the object subtree is destroyed
oldB.c = 'fuu';
In case if you need to use property name as is, use exactKey
flag with true
value.
this['a.b.c'] = 'foo';
this.bindNode('a.b.c', node, binder, {
exactKey: true
});
this['a.b.c'] = 'bar';
A flag getValueOnBind
When getValue
is given then a state of an element will be extracted and assigned to bound property immediately after bindNode
call in case if the property has undefined
value. To force this behavior even if the property has non-undefined value use getValueOnbind
flag with true
value. To cancel this behavior use the same flag with false
value.
A flag setValueOnBind
When setValue
is given then the value of the property will be set as element state immediately after bindNode
call in case if the property has non-undefined value. To force this behavior even if the property is undefined
use setValueOnBind
flag with true
value. To cancel this behavior use the same flag but with false
value.
Flags debounceGetValue=true
and debounceSetValue=true
One of the most important feature of bindNode
is that the logic of property change and the logic of element state change uses debounce pattern. It means that if bound property is changed many times in a short time then bound element state will be updated only once after small delay (thanks to debounceSetValue=true
). If element state is changed many times in a short time (eg corresponding DOM event is triggered), the property gets new value only once after minimum delay (thanks to debounceGetValue=true
).
const input = document.querySelector('.my-input');
this.bindNode('x', input);
this.x = 'foo';
console.log(input.value === 'foo'); // false
setTimeout(() => {
console.log(input.value === 'foo'); // true
});
To cancel this behavior (e. g. initiate synchronous binding) use debounceSetValue
and/or debounceGetValue
flags with false
value.
Flags debounceSetValueOnBind=false
and debounceGetValueOnBind=false
As described above bindNode
uses debounce pattern on property change and on bound node change. This doesn't apply to a moment when bindNode
is called. To remind, when the method is called a property or a node is changed immediately. When debounceGetValueOnBind
and/or debounceSetValueOnBind
are set to true
then debounce is turned on for binding initialization as well.
Flags debounceSetValueDelay=0
и debounceGetValueDelay=0
These flags allow to set debounce delay. debounceSetValueDelay
is used when debounceSetValue
or debounceSetValueOnBind
is true
, debounceGetValueDelay
is used when debounceGetValue
or debounceGetValueOnBind
is true.
A flag useExactBinder=false
Even if you pass a binder to bindNode
, the framework tries to find default binder at Seemple.defaultBinder and extend it by properties of the passed object. This feature makes possible to use partially re-defined default binder.
For example, we want to bind input[type="text"]
to a property. By default, the standard binder contains "on"
property with "input"
value for this kind of node. It means that the value of the instance property and node state will be synchronized when a user releases a key of the keyboard or pastes text from clipboard. In case if you want synchronization to be performed after the "blur"
DOM event, you need to pass an object containing the only "on"
property as the third argument. This object will extend the default binder, having retained getValue
and setValue
values.
this.bindNode('myKey', '.my-input', { on: 'blur' });
To cancel this behavior and use the binder as is, you can use useExactBinder
flag with true
value.
this.bindNode('x', node, binder, {
useExactBinder: true
});
Returns object - self
Fires bind bind:KEY
Arguments
Name | Type | Details |
---|---|---|
key | string seemple | A property name |
node | string node $nodes | An HTML element which must be bound to a |
binder optional | binder | A binder containing the following properties: |
eventOptions optional | eventOptions | An event options which accepts |
Links
Seemple#bindNode(bindings, binder, eventOptions)
→
object
Alternative syntax: passing of an object
To the Seemple#bindNode method an object can be passed to avoid multiple invocation of the method and reduce code. Keys of the object are property names and values can get the following look:
- A node
- An object with properties
node
andbinder
- An array of objects with properties
node
andbinder
If binder
arg is passed as the second argument then it wil be used as the binder for those elements for which a binder wasn't specified.
Returns object - self
Arguments
Name | Type | Details |
---|---|---|
bindings | object | (see the example) |
binder optional | binder | (see above) |
eventOptions optional | eventOptions | (see above) |
Examples
this.bindNode({
foo: '.custom-checkbox',
'bar.length': 'textarea'
});
this.bindNode({
foo: {
node: ':sandbox .aaa',
binder: Seemple.binders.html()
},
bar: '.bbb',
baz: [{
node: '.ccc'
}, {
node: document.querySelector('.ddd'),
binder: Seemple.binders.prop('baz')
}]
}, {
// will be used as a binder for .bbb and .ccc
setValue(value) {
foo(value);
}
});
Seemple#bindNode(batch, commonEventOptions)
→
object
Alternative syntax which makes possible to define unlimited amount of bindings per one bindNode
call.
The method variation allows to pass an array of objects which need to include the following properties:
key
- a property namenode
- an HTML element which must be bound to akey
binder
- a binder (optional)event
- event options (optional)
The second arg object includes common event options for all bindings and extends event
object (properties from event
has more priority).
Returns object - self
Arguments
Name | Type | Details |
---|---|---|
batch | array | A batch of bindings |
commonEventOptions optional | eventOptions | Common event options |
Examples
this.bindNode([{
key: 'a',
node: '.my-node',
binder: {
setValue(v) {
doSomething(v);
}
}
}, {
key: 'b',
node: document.querySelectorAll('.bar')
event: {
foo: 'bar'
}
}, {
key: 'c.d.e',
node: jQuery('.baz'),
binder: Seemple.binders.html(),
event: {
silent: true,
exactKey: true
}
}], {
getValueOnBind: false
});
Seemple#bindOptionalNode()
Works just the same as Seemple#bindNode but it does not throw an exception if the node
argument is an empty array, undefined
or non-existent.
The method has static alternative
Links
Examples
this.bindOptionalNode('myKey', '.my-element');
Seemple#bindSandbox(node, eventOptions)
Binds a sandbox
Unbinds previous sandbox (if exists) and binds new one.
The method has static alternative
Fires bind bind:sandbox
Arguments
Name | Type | Details |
---|---|---|
node | string node $nodes | A sandbox DOM node |
eventOptions optional | eventOptions | An event options |
Links
Examples
this.bindSandbox('.my-element');
Seemple#calc(target, source, handler=(v)=>v, eventOptions)
Creates a dependency of one property value on values of others
calc
creates a dependency of a property (target
argument) on values of other properties (source
argument). When source property is changed, target
is re-calculated automatically.
Note that the method has static alternative, which works just the same but accepts any target object as the first argument, shifting rest arguments to the right.
const calc = require('seemple/calc');
const object = {};
calc(object, target, source, handler, eventOptions);
// instead of this.calc(target, source, handler, eventOptions);
source
arg has few variations.
A string
A target
property is dependent on source
property.
this.b = 1;
this.calc('a', 'b', b => b * 2);
console.log(this.a); // 2
An array of strings
A target
is dependent on properties listed at source
array.
this.b = 1;
this.c = 2;
this.d = 3;
this.calc('a', ['b', 'c', 'd'], (b, c, d) => b + c + d);
console.log(this.a); // 6
An object with properties object
and key
At this case target
property is dependent on a property from another object.
const someObject = { b: 1 };
this.calc('a', {
object: someObject,
key: 'b'
}, b => b * 2);
console.log(this.a); // 2
key
property also accepts an array of property names.
const someObject = {
b: 1,
c: 2,
d: 3
};
this.calc('a', {
object: someObject,
key: ['b', 'c', 'd']
}, (b, c, d) => b + c + d);
console.log(this.a); // 6
An array of object with properties object
и key
This variation allows to define dependency from properties of different objects.
const someObjectX = {
b: 1,
c: 2
};
const someObjectY = {
d: 3
};
this.calc('a', [{
object: someObjectX,
key: ['b', 'c']
}, {
object: someObjectY,
key: 'd'
}], (b, c, d) => b + c + d);
console.log(this.a); // 6
A combination of strings (own properties) and objects
this.b = 1;
this.c = 2;
const someObject = {
d: 3,
e: 4
};
this.calc('a', ['b', 'c', {
object: someObjectX,
key: ['d', 'e']
}], (b, c, d, e) => b + c + d + e);
console.log(this.a); // 10
For reasons of code purity, the combination of strings and objects inside source
array is not recommended. Instead, pass an object whose object
property refers to source object. An example below makes the same job as shown at the previous example.
this.b = 1;
this.c = 2;
const someObject = {
d: 3,
e: 4
};
this.calc('a', [{
object: this, // the target object is "this"
keys: ['b', 'c']
}, {
object: someObjectX,
key: ['d', 'e']
}], (b, c, d, e) => b + c + d + e);
console.log(this.a); // 10
A path to source property
If source property name includes a dot then the method initiates a dependency on a property from nested object.
this.b = { c: { d: 1 } };
this.e = { f: { g: 2 } };
this.calc('a', ['b.c.d', 'e.f.g'], (d, g) => d + g);
console.log(this.a); // 3
The same thing works for external sources.
this.b = { c: { d: 1 } };
const someObject = { e: { f: { g: 2 } } };
this.calc('a', [{
object: this
key: 'b.c.d'
}, {
object: someObject
key; 'e.f.g'
}], (d, g) => d + g);
console.log(this.a); // 3
The method is protected from circular references (for example,
a
depends onb
,b
depends onc
andc
depends ona
) and if there is a calculation error, it does not block the page and does not throw an exception about the stack over-flow.
As you may noticed, arguments of handler
function always follow the same order as source properties appear.
In case if you want to change a value of one source property and make it so that target property will not be recalculated, then use Seemple#set method with skipCalc
flag.
this.calc('a', 'b', handler);
this.set('b', newValue, {
skipCalc: true
});
Important features of the method and special flags
The fourth argument of calc
method is eventOptions
. As usual this object can include special flags or custom data which will be passed to change:TARGET
event handler.
this.on('change:a', evt => {
console.log(evt.foo); // 'bar'
});
this.calc('a', source, handler, { foo: 'bar' });
A flag debounceCalc=true
After calc
is called, target property is calculated with no delays. But when source property is changed the debounce pattern is used. That means that target property will be changed in few milliseconds and only once even if source properties was changed many times in a short time.
this.b = 1;
this.c = 2;
this.d = 3;
this.calc('a', ['b', 'c', 'd'], (b, c, d) => b + c + d);
this.on('change:a', () => {
// the handler will be called only once
// despite that source properties was changed thrice
console.log(`a is changed to ${this.a}`); // a is changed to 60
});
this.b = 10;
this.c = 20;
this.d = 30;
console.log(this.a); // 6 instead of 60
To cancel debounce pattern when source properties are changed, in other words to make the calculation synchronously (as it was in previous versions of the method) pass debounceCalc
with false
value to the method.
this.b = 1;
this.c = 2;
this.d = 3;
this.calc('a', ['b', 'c', 'd'], (b, c, d) => b + c + d, {
debounceCalc: false
});
this.on('change:a', () => {
// the handler will be called thrice
// every time when b, c or d are changed
// a is changed to... 15, 33, 60
console.log(`a is changed to ${this.a}`);
});
this.b = 10;
this.c = 20;
this.d = 30;
console.log(this.a); // 60
A flag debounceCalcOnInit=false
As described above, target property is calculated immediately after the calc
is called. To turn on debounce on calc
call pass debounceCalcOnInit
with true
value to the method.
this.on('change:a', () => {
// the handler will be called only once in a moment
console.log(`a is changed to ${this.a}`); // a is changed to 6
});
this.b = 1;
this.c = 2;
this.d = 3;
this.calc('a', ['b', 'c', 'd'], (b, c, d) => b + c + d, {
debounceCalcOnInit: true
});
console.log(this.a); // undefined
In real world debounceCalcOnInit
flag is unlikely to be useful. Just keep in mind that you can enable "total debounce" if needed.
A flag debounceCalcDelay=0
The flag can be used to set debounce delay when debounceCalc
or debounceCalcOnInit
is set as true
.
A flag setOnInit=true
It is known that target property gets new value after calc
is called. To cancel this behavior and don't calculate a property immediately use setOnInit
with false
value.
this.calc('a', 'b', b => b * 2, {
setOnInit: false
});
console.log(this.a); // undefined
// but if this.b is changed the target property will be calculated
this.b = 1;
A flag exactKey=false
As described above, it's possible to use a path to source property using a string that contains dots. In case if you need to use exact name of source property use exactKey
with true
value.
this['foo.bar.baz'] = 1;
this.calc('a', 'foo.bar.baz', fooBarBaz => fooBarBaz * 2, {
exactKey: true
});
console.log(this.a); // 2
A flag promiseCalc=false
This flag allows to return Promise
instance from the calculating function. Target property gets its value from resolved promise.
Warning!
Promise
cannot be canceled. Use thepromiseCalc
feature carefully and don't allow multiple calls of heavy functions.
this.calc('a', ['b', 'c'], (b, c) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(a + b)
}, 1000);
});
}, {
promiseCalc: true
});
this.b = 1;
this.c = 2;
// "a" will be changed in a second
this.calc('response', 'data', async (data) => {
const resp = await fetch(url, {
method: 'post',
body: data
});
return resp.json();
}, {
promiseCalc: true
});
Arguments
Name | Default | Type | Details |
---|---|---|---|
target |
| string | A property which needs to be calculated |
source |
| string array | Which properties the target property is depended on |
handler optional | (v)=>v
| function | A function which returns a new value |
eventOptions optional |
| eventOptions | An object which can contain some special flags or data for |
Examples
this.calc('greeting', 'name', name => `Hello, ${name}!`);
this.name = 'World';
// ... in a moment
alert(this.greeting); // 'Hello, World!'
The calculation of the rectangle perimeter with two sides known (and the calculation of the sides with the perimeter known)
this.a = 3;
this.b = 4;
this
.calc('p', ['a', 'b'], (a, b) => (a + b) * 2)
.calc('a', ['p', 'b'], (p, b) => p/2 - b)
.calc('b', ['p', 'a'], (p, a) => p/2 - a);
alert(this.p); // 14
this.on('change:p', () => {
// "The perimeter has been changed and equals 18"
console.log(`The perimeter has been changed and equals ${this.p}`);
});
this.a = 5;
Seemple#calc(batch, commonEventOptions)
Extra syntax for Seemple#calc. Allows to define few calculated properties per single call of the method.
The first argument is an object whose keys are property names and values are objects with the following data:
source
- which properties the target property is depended on;handler
- a function which returns a new value of a property (by default it equals to(value) => value
);event
- event options.
The second argument contains common event options which extend event
of every item (properties of event
have higher priority).
source
can take any kind of look as described above (a string, an array of strings etc).
Arguments
Name | Type | Details |
---|---|---|
batch | array | An object which includes all information about calculated properties |
commonEventOptions optional | eventOptions | Event options which are common for all listed calculated properties |
Examples
this.calc({
x: {
source: ['a', 'b'],
handler: (a, b) => a + b
},
y: {
source: {
object: someObject,
key: 'c'
},
event: {
setOnInit: false
}
},
z: {
source: [{
object: this,
key: 'x'
}, {
object: someObject,
key: 'd'
}],
handler: (x, d) => x + d
}
}, {
debounceCalc: false
});
Seemple#instantiate(key, class, updateCallback)
→
object
Creates fixed class instance
Note that the method has static alternative.
The method creates and fixes an instance of a class as given property value. When you try to override the property, instead of actual overriding, the instance is updated. Thus, we achieve integrity of an application, made on with Seemple.js.
The method is an addon over Seemple#mediate and it overrides a mediator.
Class instance is being created during the call of instantiate
method. The current property value becomes the first argument of the class constructor. Provisions should be made so that either undefined (if a property hasn't contained any data before), or an object you should do something with (for example, to extend class instance by object properties) will get into the class constructor.
It looks easy in practice: you create an ordinary class which almost always receives some data to be handled (for example, to use it at Seemple.Object#setData method).
While attempting to assign another value (an object or an array) to the property, the inner mechanism of instantiate
method performs the following instead of the actual assignment:
- If
updateCallback
function is given, the method calls it with two arguments: current property value and the data which code is trying to assign. - If given class is inherited from Seemple.Object, the instance is updated with new data using Seemple.Object#setData method.
- If given class is inherited from Seemple.Array, the instance is updated with new data using Seemple.Array#recreate method.
- If
updateCallback
function isn't given and the class isn’t inherited from Seemple.Object or Seemple.Array, the instance is extended by object properties which is trying to be assigned.
A feature of this method is an absence of limitations on the class source. Any function-constructor that can be initialized using
new
operator and not only Seemple's successors can act as a class.
Returns object - self
Arguments
Name | Type | Details |
---|---|---|
key | string array | A key or an array of keys |
class | function | A class whose instance becomes the property value |
updateCallback optional | function | A function which is called at every attempt of assigning new data to the property, allowing to customize logic of class instance updating with new data. The function receives two arguments: the current property value (class instance) and data which are attempted to assign. |
Examples
class MyClass {
// ...
}
// ...
this.instantiate('x', MyClass);
// trying to assign another value to x property
this.x = { a: 42 };
// this.x is still MyClass instance
alert(this.x instanceof MyClass); // true
alert(this.x.a); // 42
The use of updateCallback
.
this.instantiate('x', MyClass, (instance, data) => {
updateSomeHow(instance, data);
});
Getting a parent and property name. Besides data (the first argument), two arguments are passed to class constructor: a reference to an instance which has called instantiate
and a name of a property.
class MyClass extends Seemple {
constructor(data, parent, key) {
// parent is MyParentClass instance
// which is created x property
// key equals to "x"
}
}
class MyParentClass extends Seemple {
constructor() {
super().instantiate('x', MyClass);
}
});
A non-standard way of using updateCallback
for ignoring any changes of a property.
this.instantiate('x', MyClass, () => {});
In case if your class doesn't support the use of new
operator, use Seemple#mediate method instead of instantiate
.
this.mediate('x', (data, currentValue) => {
return currentValue instanceof SomeClass
? Object.assign(currentValue, data)
: SomeLib.initInstance(SomeClass, data);
});
An abstract example with nested data (for brevity, "class instance fields" syntax is used)
// app.js
class App extends Seemple {
constructor(appData) {
this.appData = appData;
this.instantiate('appData', AppData);
}
}
// app-data.js
class AppData extends Seemple.Object {
constructor(data) {
super(data)
.instantiate({
friends: Friends,
settins: Settings
});
}
}
// friend.js
class Friend extends Seemple.Object {
constructor(data) {
super(data);
}
}
// friends.js
class Friends extends Seemple.Array {
get Model() { return Friend; }
trackBy = 'id';
constructor(data) {
super(...data);
}
}
// settings.js
class Settings extends Seemple.Object {
constructor(data) {
super(data)
.instantiate('credentials', Credentials);
}
}
// credentials.js
class Credentials extends Seemple.Object {
constructor(data) {
super(data);
}
}
// app-init.js
var app = new App({
settings: {
name: 'Vasiliy Vasiliev',
credentials: {
email: 'vasia.vasia@gmail.com'
}
},
friends: [{
name: 'Yulia Zuyeva',
id: 1
}, {
name: 'Konstantin Konstantinopolsky',
id: 2
}, {
name: 'nagibator3000',
id: 3
}]
});
// data can be serialized and passed to a server
JSON.stringify(app.appData);
// next just to assign new data to appData property
// yet the object structure won’t be changed
app.appData = {
settings: {
name: 'Petr Petrov',
credentials: {
email: 'petr.petrov@gmail.com'
}
},
friends: [{
name: 'Yulechka Zuyeva',
id: 1
}, {
name: 'Konstantin Konstantinopolsky',
id: 2
}]
};
Seemple#instantiate(keyClassPairs, updateCallback)
→
object
Additional syntax for Seemple#instantiate, which accepts key-class object as the first argument.
Returns object - self
Arguments
Name | Type | Details |
---|---|---|
keyClassPairs | object | key-class object |
updateCallback optional | function | A function which is called at every attempt of assigning new data to a property |
Examples
this.instantiate({
x: Class1,
y: Class2,
z: Class3
}, (instance, data) => {
instance.doSomethingWith(data);
});
Seemple#mediate(key, mediator)
→
object
Transforms property value on its changing
This method is used for transforming property value on its changing. For example, you want the property value to be always either of a certain type or an integer value, or to be no less than zero and no more than a hundred etc.
Note that the method has static alternative, which works just the same but accepts any target object as the first argument, shifting rest arguments to the right.
const mediate = require('seemple/mediate');
const object = {};
mediate(object, key, mediator);
// instead of this.mediate(key, mediator);
Returns object - self
Arguments
Name | Type | Details |
---|---|---|
key | string array | A key or an array of keys |
mediator | function | A function-mediator which returns a new value. It gets the following arguments: new value, previous value, a key, an object itself |
Examples
this.mediate('x', value => String(value));
this.x = 1;
alert(typeof this.x); // "string"
An array of keys
this.mediate(['x', 'y'], value => String(value));
Seemple#mediate(keyMediatorPairs)
Alternative syntax of the Seemple#mediate method which accepts "key-mediator" object as an argument
Arguments
Name | Type | Details |
---|---|---|
keyMediatorPairs | object | An object with key-mediator properties |
Examples
this.mediate({
x: String,
y: Number,
z: Boolean
});
this.x = 1;
this.y = 2;
this.z = 3;
alert(typeof this.x); // "string"
alert(typeof this.y); // "number"
alert(typeof this.z); // "boolean"
Seemple#off(names, callback, context)
→
object
Deletes an event handler
It deletes a handler which has been created before. All the three arguments are optional. You can delete both all the events (without passing any argument) and separate ones (having passed only the event name, the event name and the handler, the event name and the handler and the context).
Returns object - self
Fires removeevent removeevent:NAME
Arguments
Name | Type | Details |
---|---|---|
names optional | eventNames | A list of event names which are separated by spaces (for example, |
callback optional | eventHandler | A function-handler |
context optional | object | A context |
Links
Examples
this.off('change:x bind');
The deletion of all events
this.off();
The deletion of an event with definite handler
const handler = function() {
//...
}
this.on('change:x', handler);
this.off('change:x', handler);
The deletion of an event with definite context
const object = {};
this.on('change:x', handler, object);
this.off('change:x', handler, object);
Seemple#on(names, callback, triggerOnInit, context)
→
object
Adds an event handler
The method adds an event handler for an instance of the Seemple
class. Refer to the complete list of possible events with the description here: eventNames.
Note that the method has static alternative, which works just the same but accepts any target object as the first argument, shifting rest arguments to the right.
const on = require('seemple/on');
const object = {};
on(object, names, callback, triggerOnInit, context);
// instead of this.on(names, callback, triggerOnInit, context);
Returns object - self
Fires addevent addevent:NAME
Arguments
Name | Type | Details |
---|---|---|
names | eventNames | An event name or some names which are separated by a space (for example, |
callback | eventHandler | A function which is caused by the event |
triggerOnInit optional | boolean | If |
context optional | object | A context of the handler. In other words, |
Links
Examples
this.on('foo', () => {
alert('Custom Event is fired');
});
this.trigger('foo');
Passing a context
this.on('foo', function() {
alert(this.a); // 5
}, { a: 5 });
this.trigger('foo', 'Hello world');
Calling a handler immediately after event initialization
// Displays "bar" at once and waits for a firing of "foo" event
this.on('foo', () => {
alert('bar');
}, true);
Seemple#on(evtnameHandlerObject, triggerOnInit, context)
→
object
Alternative syntax: "eventname-handler" pairs
In the Seemple#on method the object with the key-element pairs can be passed to avoid multiple invocation of the method and reduce your code.
Returns object - self
Arguments
Name | Type | Details |
---|---|---|
evtnameHandlerObject | object | An object where keys are event names and values are event handlers |
triggerOnInit optional | boolean | If |
context optional | object | A context of the handler |
Examples
this.on({
'custom': evt => ...,
'click::x': evt => ...,
'change:y': evt => ...,
});
Seemple#onDebounce(names, callback, debounceDelay, triggerOnInit, context)
→
object
Adds an event handler which is called only once during a definite period of time
The method allows to add an event handler to an instance of the Seemple class, debouncing the handler. Callback function can be called only once during a definite period of time. As to the rest the method works the same as Seemple#on.
The method has static alternative.
Returns object - self
Fires addevent addevent:NAME
Arguments
Name | Type | Details |
---|---|---|
names | eventNames | An event name or some names which are separated by a space (for example, |
callback | eventHandler | A handler which is caused by an event |
debounceDelay optional | number | A delay |
triggerOnInit optional | boolean | If |
context optional | object | A context of a handler. In other words, |
Links
Examples
this.onDebounce('change:x', () => {
alert(`x = ${this.x}`); // x = 100
}, 300);
this.x = 1;
for(let i = 0; i < 100; i++) {
this.x++;
}
Seemple#onDebounce(evtnameHandlerObject, debounceDelay, triggerOnInit, context)
→
object
Alternative syntax: "eventname-handler" pairs
Returns object - self
Arguments
Name | Type | Details |
---|---|---|
evtnameHandlerObject | object | An object where keys are event names and values are event handlers |
debounceDelay optional | number | A delay |
triggerOnInit optional | boolean | If |
context optional | object | A context of the handler |
Links
Examples
this.onDebounce({
'custom': evt => { ... },
'click::x': evt => { ... },
'change:y': evt => { ... }
});
Seemple#once(names, callback, context)
→
object
Adds an event handler which can be called only once
The method works the same as Seemple#on but the passing handler can be called only once.
Note that the method has static alternative
Returns object - self
Fires addevent addevent:NAME
Arguments
Name | Type | Details |
---|---|---|
names | eventNames | An event name or some names which are separated by a space (for example, |
callback | eventHandler | A handler which is caused by an event |
context optional | object | A context of a handler |
Links
Examples
this.x = 1;
this.once('change:x', () => {
alert('x is changed');
});
this.x = 2; // displays 'x is changed'
this.x = 3; // does nothing
Seemple#once(evtnameHandlerObject, context)
→
object
Alternative syntax: "eventname-handler" pairs
Returns object - self
Arguments
Name | Type | Details |
---|---|---|
evtnameHandlerObject | object | An object where keys are event names and values are event handlers |
context optional | object | A context of a handler |
Links
Examples
this.once({
'custom': evt => { ... },
'click::x': evt => { ... },
'change:y': evt => { ... }
});
Seemple#parseBindings(node, eventOptions)
→
$nodes
Parses DOM tree, declaring property bindings on which are double braced.
Starting with 1.1 version, Seemple includes a simple DOM parser that handles double braced syntactic constructions. parseBindings
method receives HTML string, DOM node or selector corresponding to DOM node as the first argument.
As the method is DOM template engine (and not HTML-replace parser), all child DOM nodes of the passed element remain in their same state (for example, DOM events aren’t erased).
The supported syntax
- HTML binding ```html <!-- will create text node in place of {{user.name}} and will bind name property from user object to this node JS: this.user = {name: 'Joe'}
--> Hello, {{user.name}}
2. Binding form elements.
```html
<!--
will bind "x" instance property to a text field (two-way data binding)
JS: this.x = 'some value';
-->
<input type="text" value="{{x}}">
<!--
For binding textarea and select you should use value attribute
-->
<textarea value="{{x}}"></textarea>
<select value="{{x}}">...</select>
<!--
will bind "x" instance property to a checkbox (two-way data binding)
JS: this.x = true;
-->
<input type="checkbox" checked="{{x}}">
- Attributes binding. ```html <!-- href attribute value will depend on "category" and "someObject.page" value (one-way data binding) JS: this.category = 'seemple'; this.someObject = { page: 42 };
--> A link
```
If you want to use something else instead of braces look at Seemple.parserBrackets
Why do we need this method?
In case if you develop a large form with standard HTML5 fields, the method will help you save time on declaring numerous bindings. Besides, parseBindings
is useful in case of creating a very simple collection which doesn’t require implementation of a complicated model.
Returns $nodes - A collection of DOM nodes, which is passed to the method as an argument.
Arguments
Name | Type | Details |
---|---|---|
node | string node $nodes | HTML string, selector, DOM node or collection of DOM nodes |
eventOptions optional | eventOptions | Event options which will be passed to all internal calls of Seemple#bindNode |
Examples
Parsing of given node
this.parseBindings(node);
Parsing of nodes matching given selector
this.parseBindings('.my-node');
HTML string parsing
const $node = this.parseBindings('<h3>Hello, {{name}}</h3>');
this.name = 'Arthur Philip Dent';
Seemple#remove(key, eventOptions)
→
object
Deletes a property
Returns object - self
Fires delete delete:KEY
Arguments
Name | Type | Details |
---|---|---|
key | string | A property name or an array of names to remove |
eventOptions optional | eventOptions | An event options |
Examples
this.remove('myKey');
this.remove(['myKey1', 'myKey2']);
Using eventOptions
this.remove('myKey', {
silent: true
});
Seemple#select(selector)
→
node
null
Returns HTML node corresponding to a selector from a sandbox
The method is very similar to Seemple#selectAll, but it returns only one element or null
.
The method has static alternative
Returns node null
Arguments
Name | Type | Details |
---|---|---|
selector | string | A selector |
Examples
this.bindNode('sandbox', '.app');
this.select('.my-element');
// the same as
this.nodes.sandbox.querySelector('.my-element');
// the same as
$('.app').find('.my-element')[0];
Seemple#selectAll(selector)
→
$nodes
Synonym: Seemple#$
Returns HTML nodes corresponding to a selector from a sandbox
After sandbox declaration using Seemple#bindNode method, you can get its descendants. Besides, the method supports the :bound(KEY)
selector.
The method has static alternative
Returns $nodes
Arguments
Name | Type | Details |
---|---|---|
selector | string | A selector |
Examples
this.bindNode('sandbox', '.app');
nodes = this.selectAll('.my-element');
// the same as
nodes = this.$('.my-element'); // $ is a short reference to selectAll
// the same as
nodes = this.$nodes.sandbox.find('.my-element');
// the same as
nodes = $('.app').find('.my-element');
The :bound(KEY)
selector
this.bindNode('myKey', '.my-element');
nodes = this.selectAll(':bound(myKey) .my-another-element');
// the same as
nodes = this.$nodes.myKey.find('.my-another-element');
// the same as
nodes = $('.my-element').find('.my-another-element');
Seemple#set(key, value, eventOptions)
Sets a property value allowing to pass event options object as the third argument
The list of the supported flags:
silent
- do not call thechange
andchange:KEY
eventssilentHTML
- do not change states of bound HTML nodesforce
- call thechange
andchange:KEY
events even though the property value has not been changedforceHTML
- change a state of bound element even though the property value has not been changed. This option is usable if the bound element has been rendered after the binding (for example, someoption
tags have been added toselect
tag)skipMediator
- prevents the property transformation by a mediator (see Seemple#mediate)skipCalc
- prevents the work of dependencies created with Seemple#calc
The method has static alternative.
Fires change change:KEY beforechange beforechange:KEY
Arguments
Name | Type | Details |
---|---|---|
key | string | A key |
value | * | A value |
eventOptions optional | eventOptions | Event options |
Examples
this.on('change:myKey', evt => {
alert(evt.value);
});
// the same as this['myKey'] = 3
// or this.myKey = 3
// alerts 3
this.set('myKey', 3);
Using eventOptions
this.on('change:myKey', evt => {
alert(evt.value);
});
// handler isn't called
this.set('myKey', 4, {
silent: true
});
Passing custom data to a handler
this.on('change:myKey', evt => {
alert(evt.myCustomFlag);
});
// alerts 42
this.set('myKey', 4, {
myCustomFlag: 42
});
Seemple#set(keyValuePairs, eventOptions)
Alternative "key-value" syntax of the Seemple#set method
Arguments
Name | Type | Details |
---|---|---|
keyValuePairs | object | An object containing key-value pairs |
eventOptions optional | eventOptions | An event object |
Examples
this.set({
myKey1: 1,
myKey2: 2
});
Passing eventOptions
as second argument
this.set({
myKey: 3
}, {
myFlag: 'foo'
});
Seemple#trigger(names, arg)
→
seemple
Fires an event
After adding event handlers using Seemple#on, Seemple#onDebounce or Seemple#once, any event can be fired manually using this method.
Note that the method has static alternative
Returns seemple - self
Arguments
Name | Type | Details |
---|---|---|
names optional | eventNames | An event name or some names which are separated by a space |
arg optional | * | Any arguments which will be passed to every handler |
Links
Examples
this.on('foo bar', (a, b, c) => {
alert(a + b + c);
});
this.trigger('foo', 1, 2, 3); // alerts 6
Seemple#unbindNode(key, node, eventOptions)
→
object
Destroys a binding between given property and HTML node
Using this method you can delete the binding between a property and HTML node, which has been added recently and no longer needed.
Returns object - self
Fires unbind unbind:KEY
Arguments
Name | Type | Details |
---|---|---|
key | string null | A key or an array of keys. If you pass |
node optional | string node $nodes | HTML node |
eventOptions optional | eventOptions | Event object ( |
Examples
this.bindNode('myKey', '.my-element');
// changes the property value and the state of the HTML element
this.myKey = true;
this.unbindNode('myKey', '.my-element');
// only the property value is being changed now
this.myKey = false;
Seemple#unbindNode(bindings, eventOptions)
→
object
Alternative syntax which allows to pass an object with bindings to unbindNode
. Look at Seemple#bindNode(2) for more information
Returns object - self
Arguments
Name | Type | Details |
---|---|---|
bindings | object | (see the example) |
eventOptions optional | eventOptions | (see above) |
Examples
this.unbindNode({
foo: '.aaa'
bar: {
node: '.bbb'
},
baz: [{
node: '.ccc'
}, {
node: '.ddd'
}]
});
Seemple#unbindNode(batch, eventOptions)
Alternative syntax of the method which allows to easily unbind unlimited amount of bindings by single unbindNode
call.
The variation makes possible to pass an array which includes objects with the following properties:
key
- a property namenode
- a node bound tokey
(optional)
This variation is useful because it matches one variation of Seemple#bindNode method, allowing to store bindings in a variable to easily remove them when needed.
Arguments
Name | Type | Details |
---|---|---|
batch | array | An array of bindings |
eventOptions optional | eventOptions | (see above) |
Examples
const temporaryBindings = [{
key: 'a',
node: '.my-node',
binder: {
setValue(v) {
doSomething(v);
}
}
}, {
key: 'b',
node: document.querySelectorAll('.bar')
event: {
foo: 'bar'
}
}, {
key: 'c.d.e',
node: jQuery('.baz'),
binder: Seemple.binders.html(),
event: {
silent: true,
exactKey: true
}
}]
this.bindNode(temporaryBindings);
// these bindings are no longer needed
this.unbindNode(temporaryBindings);
Seemple#$nodes: $nodes
An object contains collections (jQuery, Zepto, built-in micro-library instance inherited from Array.prototype
) of bound nodes for quick access.
Links
Examples
this.bindNode('myKey', '.my-node');
this.$nodes.myKey; // the same as $('.my-node')
Seemple#isSeemple: boolean
isSeemple
always equals true
for instances of Seemple
Examples
alert(object.isSeemple);
Seemple#nodes: node
The object contains bound elements in the form of separate DOM nodes for quick access
Pay attention, every object property has got the first node of the bound ones to the corresponding property. Use Seemple#$nodes for getting all the nodes bound to a certain property.
Links
Examples
this.bindNode('myKey', '.my-node');
this.nodes.myKey; // the same as $('.my-node')[0]
Seemple.Class(prototype, statics)
→
class
CommonJS module: 'seemple/class'
An implementation of classes based on the prototype inheritance
The Class
function allows to use the classic OOP when ECMAScript 2015 classes cannot be used somehow.
Returns class - class (a class constructor to be exact)
Arguments
Name | Type | Details |
---|---|---|
prototype | object | Methods and properties |
statics optional | object | Static methods and properties |
Examples
const A = Seemple.Class({
method1() { ... }
});
const B = Seemple.Class({
// B is inherited from A
extends: A,
method2() { ... }
});
const C = Seemple.Class({
// С is inherited from B
extends: B,
method2() {
// calling the parent method
B.prototype.method2.apply(this, arguments);
},
method3(a, b) { ... }
});
const D = Seemple.Class({
// D is inherited from C
extends: C,
method3() {
// calling the parent method
C.prototype.method2.call(this, arguments);
}
});
Passing an object with static methods and properties
const MyClass = Seemple.Class({
method() { ... }
}, {
staticProp: 'foo',
staticMethod() {
return 'bar';
}
});
alert(MyClass.staticProp); // foo
alert(MyClass.staticMethod()); // bar
Seemple.bindNode() → object
CommonJS module: 'seemple/bindnode'
Binds a property of an object to HTML node, implementing two-way data binding
This static method works the same as Seemple#bindNode and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.bindNode(object, 'x', '.my-node');
Seemple.bindOptionalNode() → object
CommonJS module: 'seemple/bindoptionalnode'
Initializes two-way data binding but does not throws an exception if node
argument is an empty array, undefined
or non-existent
This static method works the same as Seemple#bindOptionalNode and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.bindOptionalNode(object, 'x', '.my-node');
Seemple.bindSandbox() → object
CommonJS module: 'seemple/bindsandbox'
Binds or re-binds sandbox node
This static method works the same as Seemple#bindSandbox and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.bindSandbox(object, '.my-node');
Seemple.calc() → object
CommonJS module: 'seemple/calc'
Creates a dependency of one property value on values of others
This static method works the same as Seemple#calc and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
object.a = 40;
object.b = 2;
Seemple.calc(object, 'sum', ['a', 'b'], (a, b) => a + b);
alert(object.sum); // 42
Seemple.chain(object)
→
object
CommonJS module: 'seemple/chain'
Allows chained calls of universal methods
The function accepts any object and returns an instance of externally inaccessible class which adopts universal methods allowing them to be called in a chain to change given object.
Universal method is a method which exist at Seemple prototype and have a static alternative (e. g. Seemple#bindNode and Seemple.bindNode)
Returns object - An instance of the class which adopts universal methods
Arguments
Name | Type | Details |
---|---|---|
object | object function | An object |
Examples
const object = {};
Seemple.chain(object)
.calc('a', 'b', b => b * 2)
.set('b', 3)
.bindNode('c', '.node');
// the same as
// Seemple.calc(object, 'a', 'b', b => b * 2)
// Seemple.set(object, 'b', 3)
// Seemple.bindNode(object, 'c', '.node');
Seemple.instantiate() → object
CommonJS module: 'seemple/instantiate'
Creates fixed class instance
This static method works the same as Seemple#instantiate and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.instantiate(object, 'x', SomeClass);
object.x = { a: 42 };
alert(this.x instanceof SomeClass); // true
alert(this.x.a); // 42
Seemple.lookForBinder(node)
→
binder
CommonJS module: 'seemple/lookforbinder'
Returns a binder corresponding to an element. If it is not found, it returns undefined
. The function uses Seemple.defaultBinders for the search.
Returns binder - binder
Arguments
Name | Type |
---|---|
node | node |
Links
Examples
const element = document.createElement('input');
element.type = 'text';
console.log(Seemple.lookForBinder(element));
// will return something similar to the following object
{
on: 'input',
getValue() {
return this.value;
},
setValue(v) {
this.value = v;
}
}
Seemple.mediate() → object
CommonJS module: 'seemple/mediate'
Transforms property value on its changing
This static method works the same as Seemple#mediate and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.mediate(object, 'x', String);
object.x = 42;
alert(typeof object.x); // string
Seemple.off() → object
CommonJS module: 'seemple/off'
Deletes an event handler
This static method works the same as Seemple#off and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.on(object, 'foo', evt => {
//...
});
Seemple.off(object, 'foo');
Seemple.on() → object
CommonJS module: 'seemple/on'
Adds an event handler
This static method works the same as Seemple#on and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.on(object, 'foo', evt => {
alert(evt.hello);
});
Seemple.trigger(object, 'foo', { hello: 'World' });
Seemple.onDebounce() → object
CommonJS module: 'seemple/ondebounce'
Adds an event handler which is called only once during a definite period of time
This static method works the same as Seemple#onDebounce and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.onDebounce(object, 'foo', evt => {
//...
});
Seemple.once() → object
CommonJS module: 'seemple/once'
Adds event handler which can be called only once
This static method works the same as Seemple#once and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.once(object, 'foo', evt => {
//...
});
Seemple.parseBindings() → $nodes
CommonJS module: 'seemple/parsebindings'
Parses DOM tree, declaring bindings with properties enclosed in double braces
This static method works the same as Seemple#parseBindings and all its variations, but accepts any kind of JavaScript object as first argument.
Returns $nodes - Resulting collection of DOM nodes
Links
Examples
const object = {};
const $node = Seemple.parseBindings(object, `
<h3>Hello, {{name}}</h3>
`);
object.name = 'World';
Seemple.remove() → object
CommonJS module: 'seemple/remove'
Deletes a property
This static method works the same as Seemple#remove and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
Seemple.remove(object, 'x');
Seemple.select() → node null
CommonJS module: 'seemple/select'
Returns HTML node corresponding to a selector from a sandbox
This static method works the same as Seemple#select and all its variations, but accepts any kind of JavaScript object as first argument.
Returns node null
Links
Examples
const object = {};
Seemple.bindNode(object, 'sandbox', '.app');
Seemple.select(object, '.my-element');
Seemple.selectAll() → $nodes
CommonJS module: 'seemple/selectall'
Returns HTML nodes corresponding to a selector from a sandbox
This static method works the same as Seemple#selectAll and all its variations, but accepts any kind of JavaScript object as first argument.
Returns $nodes
Links
Examples
const object = {};
Seemple.bindNode(object, 'sandbox', '.app');
Seemple.selectAll(object, '.my-element');
Seemple.set() → object
CommonJS module: 'seemple/set'
Sets a property value allowing to pass an event object as the third argument
This static method works the same as Seemple#set and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.set(object, 'x', 42, {
someOption: true
});
Seemple.toSeemple() → seemple
CommonJS module: 'seemple/toseemple'
The function, converting any nested tree of objects and arrays into Seemple.Object and Seemple.Array instances
Returns seemple - a newly created instance of Seemple
Examples
const seemple = Seemple.toSeemple({
a: 1,
b: {
c: 2
},
d: [{e: 1}, {e: 2}, {e: 3}]
});
Seemple.trigger() → object
CommonJS module: 'seemple/trigger'
Fires an event
This static method works the same as Seemple#trigger and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.on(object, 'foo', evt => {
alert(evt.hello);
});
Seemple.trigger(object, 'foo', { hello: 'World' });
Seemple.unbindNode() → object
CommonJS module: 'seemple/unbindnode'
Breaks a binding between given property and HTML node
This static method works the same as Seemple#unbindNode and all its variations, but accepts any kind of JavaScript object as first argument.
Returns object - The first argument
Links
Examples
const object = {};
Seemple.unbindNode(object, 'x', '.my-node');
Seemple.useDOMLibrary($)
CommonJS module: 'seemple/usedomlibrary'
Forces to use a definite DOM library
By default, Seemple uses a library at window.$
reference. If there is no such a variable in the global namespace or it does not include a necessary set of methods, the built-in micro-library is used.
The useDOMLibrary
method makes Seemple.js use a library you would like to use in spite of its absence in the global namespace or for a different reason (for example, if two different versions of jQuery are used on a page).
Arguments
Name | Type | Details |
---|---|---|
$ | function | Any jQuery-like library (jQuery itself, Zepto, or |
Examples
Seemple.useDOMLibrary(jQuery.noConflict());
Seemple.defaultBinders: array
CommonJS module: 'seemple/defaultbinders'
An array of functions which return a corresponding binder
defaultBinders
is the array of functions which check an element in turn against given rules in these functions and return a binder (see binder). This array is used when the third argument has not been passed to the Seemple#bindNode method. See more detailed information about bindings in Seemple#bindNode documentation.
Links
Examples
Seemple.defaultBinders.unshift(element => {
// check if the element has "foo" class name
if(element.classList.contains('foo')) {
// if checking is OK, return a new binder
return {
on: ...,
getValue: ...,
setValue: ...
};
}
});
// ...
this.bindNode('myKey', '.foo.bar');
Seemple.parserBrackets: object
CommonJS module: 'seemple/parserbrackets'
Contains brackets for bindings parser
parserBrackets
object allows to change default syntax of the parser. It contains two properties: left
(left bracket, "{{" by default) and right
(right bracket, "}}" by default)
Examples
Use syntax [[=property]]
instead of {{property}}
Seemple.parserBrackets.left = '[[=';
Seemple.parserBrackets.right = ']]';
Seemple.binders object
CommonJS module: 'seemple/binders'
A namespace for binders. Out of the box it contains general-purpose binders and can be extended by you so as not to make a mess of the global namespace.
Take a little agreement into consideration: every property from the Seemple.binders
namespace must be made as a function which returns a binder (such functions usually called "binder creators").
At this documentation the properties from
Seemple.binders
are used directly. But to make your code more readable it's recommended to assign them to a variable.
const html = Seemple.binders.html;
// ...
this.bindNode('x', node, html());
Or import them as CJS/ES2015 module:
// import few binders per once
import { html, text, prop } from 'seemple/binders';
// import binders separately
import html from 'seemple/binders/html';
Links
Examples
Seemple.binders.myCoolBinder = (var1, var2) => {
return {
on: 'click',
getValue() { ... },
setValue() { ... },
initialize() { ... },
destroy() { ... }
};
};
this.bindNode('myKey', '.my-element',
Seemple.binders.myCoolBinder('Hello', 'World'));
Seemple.binders.attr(attribute, mappingFn)
→
binder
Returns a binder which changes an attribute of DOM node depending on an object property value
The value can be transformed using mappingFn
argument.
Returns binder
Arguments
Name | Type | Details |
---|---|---|
attribute | string | Attribute name |
mappingFn optional | function | Mapping function |
Examples
this.bindNode('image', 'img.my-image', Seemple.binders.attr('src'));
this.image = 'http://example.com/cats.jpg';
The usage of mapping function
this.bindNode('myKey', '.my-node',
Seemple.binders.attr('foo', value => `Hello, ${value}`));
this.myKey = 'World'; // the foo attr now has value "Hello, World"
Seemple.binders.className(className, bool=true)
→
binder
Returns a binder which switches over DOM node class name depending on an object property value. If property value equals true
non-strictly, a class name is added, otherwise - it's removed. The logic can be changed by passing false
as the second argument and in this way, a class name will be added when a property value equals false
non-strictly and vice versa.
Returns binder
Arguments
Name | Default | Type |
---|---|---|
className |
| string |
bool optional | true
| boolean |
Examples
this.bindNode('myKey', '.my-element',
Seemple.binders.className('foo'));
this.myKey = true; // adds the 'foo' class
this.myKey = false; // removes the 'foo' class
this.bindNode('myKey', '.my-element',
Seemple.binders.className('foo', false));
this.myKey = false; // adds the 'foo' class
this.myKey = true; // removes the 'foo' class
Seemple.binders.dataset(property, mappingFn)
→
binder
Returns a binder which changes given dataset property of bound DOM node depending on an object property value.
The property value can be transformed using mappingFn
argument.
Returns binder
Arguments
Name | Type | Details |
---|---|---|
property | string | A property of |
mappingFn optional | function | Mapping function |
Examples
this.bindNode('myKey', '.my-element', Seemple.binders.dataset('myProp'));
this.myKey = 'foo';
The usage of mapping function
this.bindNode('myKey', '.my-element',
Seemple.binders.dataset('myProp', value => `Hello, ${value}`));
this.myKey = 'foo'; // the attr data-my-prop now has value "Hello, foo"
Seemple.binders.display(bool=true)
→
binder
Returns a binder which controls a visibility of DOM node (using style.display
) depending on an object property value
Returns binder
Arguments
Name | Default | Type | Details |
---|---|---|---|
bool optional | true
| boolean | If the argument equals |
Examples
this.bindNode('myKey', '.my-element', Seemple.binders.display(true));
this.bindNode('myKey', '.my-element', Seemple.binders.display(false));
Seemple.binders.existence(bool=true)
→
binder
Returns a binder which controls an existence of DOM node at DOM tree depending on an object property value
The binder works the same way as Seemple.binders.display, but instead of visibility change the existence at page DOM is changed. The binder is useful for:
- Big appications: show one or another page depending on route state;
- For infinite scrolling;
- For other cases where you need to hide an element but its existence at DOM tree isn't necessary.
Returns binder
Arguments
Name | Default | Type | Details |
---|---|---|---|
bool optional | true
| boolean | If the argument equals |
Examples
this.bindNode('myKey', '.my-element', Seemple.binders.existence(true));
this.bindNode('myKey', '.my-element', Seemple.binders.existence(false));
Seemple.binders.html(mappingFn)
→
binder
Returns a binder which changes innerHTML
of bound DOM node depending on an object property value
The property value can be transformed using mappingFn
argument.
Returns binder
Arguments
Name | Type | Details |
---|---|---|
mappingFn optional | function | Mapping function |
Examples
this.bindNode('myKey', '.my-element', Seemple.binders.html());
// sets innerHTML="<div>foo</div>"
this.myKey = '<div>foo</div>';
The usage of mapping function
this.bindNode('myKey', '.my-element',
Seemple.binders.html(value => `Hello, ${value}`));
// sets innerHTML="Hello, <div>foo</div>"
this.myKey = '<div>foo</div>';
Seemple.binders.input(type)
→
binder
Returns a binder which binds an object property to an input
element value. It is not obligatory to use the binder directly because it is included in the Seemple.defaultBinders list.
Returns binder
Arguments
Name | Type | Details |
---|---|---|
type optional | string | Input type |
Examples
this.bindNode('myKey', '.my-input', Seemple.binders.input('range'));
Seemple.binders.output() → binder
Returns a binder which binds an object property to an output
element value. It is not obligatory to use the binder directly because it is included in the Seemple.defaultBinders list.
Returns binder
Examples
this.bindNode('myKey', '.my-output', Seemple.binders.output());
Seemple.binders.progress() → binder
Returns a binder which binds an object property to a progress
element value. It is not obligatory to use the binder directly because it is included in the Seemple.defaultBinders list.
Returns binder
Examples
this.bindNode('myKey', '.my-progres', Seemple.binders.progress());
Seemple.binders.prop(property, mappingFn)
→
binder
Returns a binder which changes given property of DOM node depending on an object property value
The property value can be transformed using mappingFn
argument.
Returns binder
Arguments
Name | Type | Details |
---|---|---|
property | string | A property name |
mappingFn optional | function | Mapping function |
Examples
this.bindNode('disabled', '.my-button',
Seemple.binders.prop('disabled'));
// sets disabled = true property for the node
this.disabled = true;
// sets disabled = false property for the node
this.disabled = false;
The usage of mapping function
this.bindNode('myProp', '.my-node'
Seemple.binders.prop('foo', value => `Hello, ${value}`));
// foo property of the element now has value "Hello, World"
this.myProp = 'World';
Seemple.binders.select(multiple)
→
binder
Returns a binder which binds an object property to a select
element value. It is not obligatory to use the binder directly because it is included in the Seemple.defaultBinders list.
Returns binder
Arguments
Name | Type | Details |
---|---|---|
multiple optional | boolean | If select is |
Examples
this.bindNode('myKey', '.my-select', Seemple.binders.select(true));
Seemple.binders.style(property, mappingFn)
→
binder
Returns a binder which changes given style property of bound DOM node depending on an object property value
The property value can be transformed using mappingFn
argument.
Returns binder
Arguments
Name | Type | Details |
---|---|---|
property | string | A property of |
mappingFn optional | function | Mapping function |
Examples
this.bindNode('myKey', '.my-progres',
Seemple.binders.style('backgroundColor'));
this.myKey = 'red'; // background-color of .my-progress is red now
The usage of mapping function
this.bindNode('myKey', '.my-element',
Seemple.binders.style('backgroundImage', value => `url("${value}")`));
this.myKey = 'cats.jpg'; // backgroundImage now equals to "url("cats.jpg")"
Seemple.binders.text(mappingFn)
→
binder
Returns a binder which changes textContent
of bound DOM node depending on an object property value
The property value can be transformed using mappingFn
argument.
Returns binder
Arguments
Name | Type | Details |
---|---|---|
mappingFn optional | function | Mapping function |
Examples
this.bindNode('myKey', '.my-element', Seemple.binders.text());
this.myKey = 'foo'; // sets textContent as "foo"
The usage of mapping function
this.bindNode('myKey', '.my-element',
Seemple.binders.text(value => `Hello, ${value}`));
this.myKey = 'foo'; // sets textContent as "Hello, foo"
Seemple.binders.textarea() → binder
Returns a binder which binds an object property to a textarea
element value. It is not obligatory to use the binder directly because it is included in the Seemple.defaultBinders list.
Returns binder
Examples
this.bindNode('myKey', '.my-textarea', Seemple.binders.textarea());
Class Seemple.Object
CommonJS module: 'seemple/object'
Seemple.Object
is a class which is responsible for key-value data. Its goal is to separate "service" properties from data that can be passed to a server or kept in a local storage. The class is inherited from the Seemple class and includes all its properties and methods.
Imagine you create an object which includes "a"
, "b"
and "c"
properties. Let's assume that "a"
and "b"
are the properties which must be sent to a server, and "c"
property is just responsible for some application state (for example, it contains the sum of "a"
and "b"
). The server does not need the "c"
property. So we have to separate the properties which are responsible for data from ones which are not.
In order to declare such properties from others, you can make use of the Seemple.Object#addDataKeys method.
this.addDataKeys(['a', 'b']);
this.a = 1;
this.b = 2;
this.c = 3;
If you don't know which properties are specified in advance, you can always use the Seemple.Object#setData method, which declares not only properties responsible for data but sets their values at once.
this.setData({
a: 1,
b: 2
});
this.c = 3;
After an application has found out what is data, Seemple.Object instance can be converted into an ordinary object by the Seemple.Object#toJSON method and passed to a server or kept in a local DB (for example, in localStorage
).
// will return an object { a: 1, b: 2 }
this.toJSON();
Events
On add or change of data properties set
and modify
events are fired. On remove of data properties remove
and modify
events are fired. That means you can listen any changes of Seemple.Object
instance with the only modify
event name.
this.on('modify', () => {
alert('an object is modified');
});
Arguments
Name | Type | Details |
---|---|---|
data optional | object | Data |
Examples
Creation of an instance with two specified properties
// the same as new Seemple.Object().setData({ a: 1, b: 2 });
new Seemple.Object({ a: 1, b: 2 });
The inheritance
class MyClass extends Seemple.Object {
constructor(data) {
super(data).sayHello();
}
sayHello() {
alert("Hello World!");
}
}
The inheritance using Seemple.Class function
const MyClass = Seemple.Class({
extends: Seemple.Object,
constructor(data) {
this.setData(data).sayHello();
},
sayHello() {
alert("Hello World!");
}
});
Data enumerating, using for..of
const seempleObject = new Seemple.Object({ a: 1, b: 2 });
for(let item of seempleObject) {
console.log(item); // 1 .. 2
}
Seemple.Object#addDataKeys(keys)
→
seempleObject
Adds given property names to a list of property names which are responsible for data
This method is used when you need to declare keys which are responsible for data but values of corresponding properties are not known yet.
Returns seempleObject - self
Fires set modify
Arguments
Name | Type | Details |
---|---|---|
keys | string array | An array of property names or a list of args |
Examples
this.addDataKeys(['a', 'b']);
this.addDataKeys('a', 'b');
Seemple.Object#each(callback, thisArg)
→
seempleObject
Iterates properties which are responsible for data through callback
function
This method is very similar to Array.prototype.forEach
. It allows to iterate over data properties when for..of syntax is not available.
Returns seempleObject - self
Arguments
Name | Type | Details |
---|---|---|
callback | function | A function which is called on every iteration |
thisArg optional | * | A context of a function |
Examples
this
.setData({ a: 1, b: 2 })
.addDataKeys('c')
.each((value, key) => {
console.log(key, value);
}, this);
;
// >>> a 1, b 2, c undefined
Seemple.Object#entries() → array
Returns an array of [key, value]
pairs of properties which are responsible for data
Returns array - entries
Examples
for(const [key, value] of this.entries()) {
console.log(key, value);
}
Seemple.Object#isDataKey(key)
→
boolean
Checks if given string a name of data property
Returns boolean - a result of checking
Arguments
Name | Type |
---|---|
key | string |
Examples
console.log(this.isDataKey('a'));
Seemple.Object#keyOf(value)
→
string
null
Searches for specified property value among others which are responsible for data and returns one key if this value has been found (very similar to Array.prototype.indexOf
function)
Returns string null
Arguments
Name | Type | Details |
---|---|---|
value | * | A value of any type which should be found among data keys |
Examples
const seempleObject = new Seemple.Object({
a: 1,
b: 2
});
seempleObject.c = 3;
seempleObject.keyOf(1); // 'a'
seempleObject.keyOf(2); // 'b'
seempleObject.keyOf(3); // null
Seemple.Object#keys() → array
Returns an array of property names which are responsible for data
Returns array - keys
Examples
const keys = this.keys();
Seemple.Object#removeDataKeys(keys)
→
seempleObject
Deletes given property names from a list of property names which are responsible for data (but does not delete the properties)
Returns seempleObject - self
Fires remove modify
Arguments
Name | Type | Details |
---|---|---|
keys | string array | An array of property names or a list of args |
Examples
this.removeDataKeys(['a', 'b']);
this.removeDataKeys('a', 'b');
Seemple.Object#setData(key, value, eventOptions)
→
seempleObject
Synonym: Seemple.Object#jset
Sets a property and adds property name to a list of property names which are responsible for data
This method does two things:
Sets a specified value to a given property.
Adds a key to data keys list which it makes property available for using in the Seemple.Object#each, Seemple.Object#keys, Seemple.Object#toJSON and other methods.
When
replaceData
flag is set astrue
then all other properties will be removed from the list of data properties.
Apart from that, the method works the same as Seemple#set.
Returns seempleObject - self
Fires change change:KEY modify set
Arguments
Name | Type | Details |
---|---|---|
key | string | A key |
value | * | A value |
eventOptions optional | eventOptions | An event options |
Links
Examples
this.setData('a', 1).setData('b', 2);
// assign 3 to the 'c' property,
// but do not add the key to a list of keys which are responsible for data
this.set('c', 3);
this.each((value, key) => {
console.log(key, value);
});
// displays 'a' 1 and 'b' 2
console.log(this.keys()); // displays ['a', 'b']
console.log(this.toJSON()); // displays { a: 1, b: 2 }
After using the Seemple.Object#setData method, you can work with a property like with an ordinary one
this.setData('a', 1).setData('b', 2);
this.set('a', 3);
this.b = 4;
The use of alternative method name jset
this.jset('a', 1);
Seemple.Object#setData(keyValuePairs, evtOpts)
→
seempleObject
Alternative syntax of the Seemple.Object#setData method that uses a "key-value" object for setting several properties at once
Returns seempleObject - self
Arguments
Name | Type | Details |
---|---|---|
keyValuePairs | object | A key-value object |
evtOpts | eventOptions | A event object |
Examples
this.setData({
a: 1,
b: 2
});
If replaceData
event option set as true
, then other properties will be removed from a list of data properties
this
.addDataKeys(['a', 'b', 'c'])
.setData({
a: 1,
b: 2
}, {
replaceData: true
});
console.log(this.keys()); // ['a', 'b']
Seemple.Object#toJSON(recursive=true)
→
object
Converts an instance and its internal properties into an ordinary object
The method works recursively, calling toJSON
for all internal properties which have such method. To cancel this behavior pass false
as the only argument.
Returns object
Arguments
Name | Default | Type |
---|---|---|
recursive optional | true
| boolean |
Examples
const seempleObject = new Seemple.Object({
a: 1,
b: 2,
c: new Seemple.Object({
d: 3,
e: 4
})
});
// returns {a: 1, b: 2, c: { d: 3, e: 4 }}
console.log(seempleObject.toJSON());
// returns {a: 1, b: 2, c: SeempleObject}
console.log(seempleObject.toJSON(false));
Seemple.Object#values() → array
Returns an array of property values which are responsible for data
Returns array - values
Examples
const values = this.values();
Seemple.Object#isSeempleObject: boolean
The isSeempleObject
property always equals true
for instances of Seemple.Object class
Examples
console.log(object.isSeempleObject);
Class Seemple.Array
CommonJS module: 'seemple/array'
The Seemple.Array
serves as collection class in Seemple framework. It is inherited from the Seemple class, obtaining all parent possibilities without exception. Besides, Seemple.Array
has all methods that ordinary array has.
All the methods which have been adopted from the built-in Array work like their originals
A programmer familiar to the methods of native Array
can understand immediately by means of which method adds (push
, unshift
, splice
), deletes (pop
, shift
, splice
), sorts (sort
, reverse
) items etc.
For the reason that methods work the same as original ones (with a few exceptions), they are not presented in this documentation separately but they are gathered in the Seemple.Array#METHOD section.
this.push(1, 2, 3);
this.pop();
All methods adopted from built-in Array
which modify an array can be called with an event object passing
The method_
syntax is used for this, where underscore at the end of a method name means that the last argument is an event object. Such methods are not given in this documentation because it is necessary to remember their syntax only. See Seemple.Array#METHOD_.
this.push_(1, 2, 3, {
silent: true
});
this.pop_({
foo: 'bar'
});
A developer has an opportunity of catching any data modification
While using a methods adopted from built-in Array
, events with corresponding name are fired. Calling the push
method, the push
event is fired, calling the shift
method, the shift
event is fired, calling the sort
method, the sort
event is fired, and so on.
this.on('push', evt => {
console.log('push is called');
});
this.push(1, 2, 3);
While adding items, add
and addone
events are fired. The first one is fired once on adding (for example, you have added few items using push
and the event has been fired only once), the second one is fired once on every added item. On the add
event triggering, an array of added items is passed to an event object as added
property and on the addone
triggering, each particular added item is passed to it as addedItem
property.
this.on('add', evt => {
console.log(evt.added); // [1,2,3]
});
this.push(1, 2, 3);
// the handler will be called three times,
// as three new item have been added to the array
this.on('addone', evt => {
console.log(evt.addedItem); // 1 ... 2 ... 3
});
this.push(1, 2, 3);
On removing items the same logic is observed: remove
triggers once even though several items have been removed (removed items are contained in the removed
property) and the removeone
event triggers for each removed item individually (removed item is contained in the removedItem
property).
this.push(1, 2, 3, 4, 5);
this.on('remove', evt => {
console.log(evt.removed); // [2, 3, 4]
});
this.splice(1, 3);
this.push(1, 2, 3, 4, 5);
// the handler will be called three times,
// as three items have been removed from the array
this.on('removeone', evt => {
console.log(evt.removedItem); // 2 ... 3 ... 4
});
this.splice(1, 3);
On every modification of an array the modify
event is fired, allowing to catch all changes in the array (adding, removing, re-sorting).
this.on('modify', evt => {
console.log(evt.added);
console.log(evt.removed);
});
length
is an ordinary property which can be bound to HTML node or you can listen changes using the change:length
event name.
For example, on adding three items using the
push
method with three arguments, the following events:push
,add
,addone
(three times),modify
,change:length
are fired.
Model
The Seemple.Array#Model property specifies a class of items that an array contains. It is recommended to inherit Model
from the Seemple.Object class or the Seemple.Array one (in case if it is necessary to get a collection of collections) in order to get the opportunity of converting an array into an ordinary one recursively by using the Seemple.Array#toJSON method.
Automatic rendering
Seemple.Array
can render HTML nodes on a page automatically in any modifications of an array. The Seemple.Array#itemRenderer property is used for that. You do not have to worry about rebuilding the HTML tree, Seemple.Array
does it for you. For detailed information read the documentation of Seemple.Array#itemRenderer.
Links
Examples
An instance creation
new Seemple.Array();
An instance creation with length specifying
new Seemple.Array(42);
Items passing on creation
new Seemple.Array('Hi', {a: 'b'});
The inheritance
class MyClass extends Seemple.Array {
constructor(items) {
super(...items).sayHello();
}
sayHello() {
alert("Hello World!");
}
}
The inheritance using Seemple.Class function
const MyClass = Seemple.Class({
extends: Seemple.Array,
constructor(items) {
this.recreate(items).sayHello();
},
sayHello() {
alert("Hello World!");
}
});
Seemple.Array#METHOD()
Any method from Array.prototype
Seemple.Array includes all the methods existing in the native JavaScript Array:
- join
- pop
- push
- reverse
- shift
- unshift
- slice
- splice
- sort
- filter
- forEach
- some
- every
- map
- indexOf
- lastIndexOf
- reduce
- reduceRight
- copyWithin
- find
- findIndex
- fill
- includes
- entries
- keys
- concat
Yet they work the same as the Array.prototype
. There are only a few remarks:
- The
forEach
method returns an array itself instead ofundefined
- The methods which should return a new array (
splice
,slice
,filter
,map
...) return a newSeemple.Array
instance. - The
keys
,values
andentries
methods return an array instead of an iterator.
Moreover, the methods generate events which are fired on any array modification. For more detailed information look at Seemple.Array.
Links
Examples
this.push(1, 2, 3);
const seempleArray = this
.forEach((value, index) => {
//...
})
.map((value, index) => {
//...
});
console.log(seempleArray.isSeempleArray); // true
this.reverse();
Seemple.Array#METHOD_()
Any method from Array.prototype
with the possibility of passing event object
Having found out more about Seemple.Array#METHOD, it becomes clear that the methods do not support an event object passing as they exactly duplicate the syntax and the number of arguments of the built-in Array
. The METHOD_
syntax allows to pass some data to event handlers.
The list of available flags:
Links
Examples
this.push_(1, 2, 3, {
silent: true
});
this.pop_({
silent: true
});
this.on('modify', evt => {
alert(evt.flag); // 42
});
this.push_(1, 2, 3, {
flag: 42
});
<virtual>Seemple.Array#Model(data, seempleArray, index)
The property defines a class of items which will be inserted to a collection
Every time items are added to an array, the built-in handler checks if added item is Model
instance and converts it into the one if the check fails. It is recommended to inherit Model
from the Seemple.Object or Seemple.Array class (in case if it is necessary to get a collection of collections) to get an opportunity of an array conversion into ordinary one by Seemple.Array#toJSON method.
Use Seemple.Array#mediateItem for more flexible control of an item class (for example, if you need to use one class for certain items and another one - for others).
Arguments
Name | Type | Details |
---|---|---|
data | object | Data which have been passed to a constructor |
seempleArray | seempleArray | An array where an item has been added to |
index | number | Current index of an instance in the parent array |
Links
Examples
// define a model
class MyModel extends Seemple.Object {
constructor(data, parentArray, index) {
super(data);
this.doSomething();
}
doSomething() { ... }
}
// define collection class
class MyArray extends Seemple.Array {
get Model() {
return MyModel;
}
}
const myArray = new MyArray();
// add two items
myArray.push({
a: 1,
b: 2
}, {
a: 3,
b: 4
});
console.log(myArray[0] instanceof MyModel); // true
console.log(myArray[1] instanceof MyModel); // true
// returns [{ a: 1, b: 2 }, { a: 3, b: 4 }]
myArray.toJSON();
Seemple.Array#mediateItem(mediator)
Transforms an item value
This method is used to transform Seemple.Array
items. Note that calling of this method overrides the Seemple.Array#Model property.
Arguments
Name | Type | Details |
---|---|---|
mediator | function | A function which should return transformed value of an item |
Links
Examples
// all the array items are integers
this.mediateItem(item => parseInt(item) || 0);
this.push(1, 2, 3, 4, 5);
// all the array items are strings
this.mediateItem(String);
this.push(6, 7);
this.unshift(true, {});
// ["true", "[object Object]", "1", "2", "3", "4", "5", "6", "7"]
console.log(seempleArray.toJSON());
this.mediateItem(item => {
if(item.something) {
return new FirstModel(item);
} else {
return new SecondModel(item);
}
});
<virtual>Seemple.Array#onItemRender(item, renderEvent)
A function which is called before "render"
event
The virtual method can be used instead of "render"
event handler.
At the same time onRender
method is called in a rendered item with the only argument - an event object.
Arguments
Name | Type | Details |
---|---|---|
item | object | A rendered node of a collection |
renderEvent | object |
|
Examples
class MyModel extends Seemple.Object {
constructor(data) {
super(data);
}
onRender(renderEvt) {
this.bindNode('isChecked', ':sandbox .my-checkbox');
this.bindNode('text', ':sandbox .text', Seemple.binders.html());
}
});
class MyArray extends Seemple.Array {
get Model() {
return MyModel;
}
itemRenderer() {
return '<li>'
}
constructor() {
this.bindNode('sandbox', '.my-form');
}
onItemRender(item, renderEvt) {
// ...
}
});
const app = new MyArray();
Seemple.Array#orderBy(keys, orders=asc)
→
seempleArray
Sorts by properties of array items
This method works almost like orderBy from lodash. It accepts a key or a list of keys as the first arg and an order (asc/desc) or a list of orders as the second arg.
Returns seempleArray - self
Fires sort
Arguments
Name | Default | Type | Details |
---|---|---|---|
keys |
| string array | A key or a list of keys |
orders optional | asc
| string array | An order or a list of orders corresponding to the list of keys |
Examples
A little example taken from lodash documentation
this.recreate([
{ 'user': 'fred', 'age': 48 },
{ 'user': 'barney', 'age': 34 },
{ 'user': 'fred', 'age': 42 },
{ 'user': 'barney', 'age': 36 }
]);
// sort by 'user' in ascending order and by 'age' in descending order
this.orderBy(['user', 'age'], ['asc', 'desc']);
// → [{"user":"barney","age":36},{"user":"barney","age":34},{"user":"fred","age":48},{"user":"fred","age":42}]
Seemple.Array#pull(indexOrValue, eventOptions)
→
*
null
Removes an item with specified index from an array or an item itself
Returns * null - A deletee or null
Fires pull remove removeone modify
Arguments
Name | Type | Details |
---|---|---|
indexOrValue | object number | An item index (a number) which has to be removed or the deletee itself (an object) |
eventOptions optional | eventOptions | An event object in case if it is needed to pass some data to event handlers or to set some service flags (eg. |
Examples
Passing of an index
let removed;
this.recreate(['a', 'b', 'c']);
removed = this.pull(1);
console.log(removed); // 'b'
console.log(this.join(',')); // 'a,c'
Passing of a deletee object
const object1 = {};
const object2 = {};
const object3 = {};
let removed;
this.push(object1, object2, object3);
removed = this.pull(object2);
console.log(removed === object2); // true
console.log(this.length); // 2
Seemple.Array#recreate(array, eventOptions)
→
seempleArray
Recreates the Seemple.Array instance
The method allows to convert any array or array-like into the Seemple.Array instance. If nothing has been passed as the first argument, the instance is cleansed.
Returns seempleArray - self
Fires recreate modify add addone remove removeone
Arguments
Name | Type | Details |
---|---|---|
array optional | array | An array or array-like object |
eventOptions optional | eventOptions | An event object |
Links
Examples
// cleanse the array and add 5 new items
this.recreate([1, 2, 3, 4, 5]);
// cleanse the array
this.recreate();
Seemple.Array#rerender(eventOptions)
→
seempleArray
Rerenders DOM nodes of items which are included into an instance
This method renders array items in an array container. If a node which is associated to an array item has already been created, the method reinserts it into container of the array instead of rerendering it all over again.
The method can be useful in case when items have been added to the array before declaring a sandbox or a container.
To force items rerender (e. g. you use custom template engine) pass a property forceRerender
with true
value to an event object.
Returns seempleArray - self
Arguments
Name | Type | Details |
---|---|---|
eventOptions optional | eventOptions | An event options |
Examples
this.rerender({
forceRerender: true
});
Seemple.Array#restore(selector, eventOptions)
→
seempleArray
Restores Seemple.Array instance from existing HTML nodes on a page.
In case if a collection is pre-rendered on the page (e. g. via webserver), the method can restore the collection from existing HTML nodes.
<!-- One, Two, Three are prerendered -->
<ul class="collection-node">
<li>One</li>
<li>Two</li>
<li>Three</li>
<script type="text/html" class="renderer">
<li></li>
</script>
</ul>
class MyModel extends Seemple.Object {
constructor(data) {
super(data);
this.addDataKeys('value');
}
onRender() {
this.bindNode('value', ':sandbox', Seemple.binders.html())
}
});
class MyCollection extends Seemple.Array {
get itemRenderer() {
return ':sandbox .renderer';
}
constructor() {
this
.bindNode('sandbox', '.collection-node')
.restore(':sandbox li');
}
});
const myCollection = new MyCollection();
myCollection.push({
value: 'Four'
});
console.log(myCollection.toJSON());
// [{value: 'One'}, {value: 'Two'}, {value: 'Three'}, {value: 'Four'}]
If selector
arg isn't passed then the collection will be restored from child nodes that placed in a container ("container"
or "sandbox"
).
The method fires "render"
event and calls onRender
and onItemRender
functions (look at the docs) for every newly added item similar to usual rendering.
Returns seempleArray - self
Fires recreate modify add addone
Arguments
Name | Type | Details |
---|---|---|
selector optional | selector | A selector |
eventOptions optional | eventOptions | An event options |
Seemple.Array#toJSON(recursive=true)
→
array
Converts Seemple.Array instance into native array
The method works recursively, calling toJSON
for all items which have such method. To cancel this behavior pass false
as the only argument.
Returns array
Arguments
Name | Default | Type |
---|---|---|
recursive optional | true
| boolean |
Examples
const seempleArray = new Seemple.Array([1, 2, new SeempleArray(3, 4)]);
// returns [1, 2, [3, 4]]
console.log(seempleArray.toJSON());
// returns [1, 2, SeempleArray]
console.log(seempleArray.toJSON(false));
Seemple.Array#isSeempleArray: boolean
The isSeempleArray
property always equals true
for instances of Seemple.Array class
Examples
console.log(object.isSeempleArray);
<virtual>Seemple.Array#itemRenderer: string function
HTML string, a selector or a function which is responsible for rendering DOM nodes of an array on a page
The itemRenderer
overridable property allows to render corresponded HTML nodes of items of an array without a programmer's participation. On a new item insertion into an array, HTML node is created automatically. This node becomes a sandbox (see. Seemple#bindNode) (this behavior can be canceled, read below) for inserted item and it is inserted into HTML container which is defined in an array.
For brevity, the "class fields" syntax will be used in examples at this article.
Where is created node inserted?
In order to define a place where rendered HTML nodes will be inserted, it is necessary to define a container. HTML sandbox should be declared for an array or a special container
key should be bound to the HTML container for that. Read more detailed information about bindings and sandboxes in Seemple#bindNode.
Rendering to a sandbox:
<ul class="my-list"></ul>
class MyArray extends Seemple.Array {
itemRenderer = '<li>';
get Model() { return MyModel; }
constructor() {
// sandbox definition
super().bindNode('sandbox', '.my-list');
}
});
Now all newly created <li>
nodes will get into the .my-list
node.
If you do not want to insert HTML nodes straight into the sandbox, you can bind container
property to needed HTML node. This logic is required in case if a sandbox is not limited by collection items and it includes other HTML nodes.
<div class="my-widget">
<h1>This is my awesome list</h1>
<ul class="my-list"></ul>
</div>
class MyArray extends Seemple.Array {
itemRenderer = '<li>';
get Model() { return MyModel; }
constructor() {
super();
// sandbox definition
this.bindNode('sandbox', '.my-widget');
// a definition of container for HTML nodes
this.bindNode('container', '.my-list');
}
}
In the example above the HTML nodes will be inserted into .my-list
instead of .my-widget
.
The itemRenderer
property supports a few variants of defining, but they all must contain or return the only HTML node.
HTML string as a property value
class MyArray extends Seemple.Array {
get Model() { return MyModel; }
itemRenderer = '<div class="my-div">foo</div>';
constructor() { ... }
}
A selector as a property value
In case if you carry over the templates for the items to the HTML page, itemRenderer
supports the selector as a value. When this occurs, Seemple.Array will search for HTML node in DOM tree and it will extract innerHTML
of found node. In case if a node is not found, an exception will be thrown.
HTML string is different from a selector due to the presence of the
<
symbol in a string.
<script type="text/html" id="my-template">
<div class="my-div">foo</div>
</script>
class MyArray extends Seemple.Array {
get Model() { return MyModel; }
itemRenderer = '#my-template';
constructor() { ... }
}
A function as a property value
The usage of a function as the itemRenderer
property value gives an additional code flexibility if it is necessary to dynamically generate HTML node for rendering. A function can return:
HTML string
class MyArray extends Seemple.Array {
itemRenderer() {
return '<div class="my-div">foo</div>';
}
}
A selector
class MyArray extends Seemple.Array {
itemRenderer() {
return '#my-template';
}
}
DOM node
class MyArray extends Seemple.Array {
itemRenderer() {
return document.createElement('div');
}
}
Parent renderer overlapping by the render
property
Sometimes it is convenient to declare a renderer inside a model class. The renderer
property overlaps the itemRenderer
value if it is specified for a child item of a collection.
class MyModel extends Seemple.Object {
renderer = '<div class="my-div">foo</div>';
}
class MyArray extends Seemple.Array {
Model = MyModel,
itemRenderer = '<frameset>bar</frameset>';
constructor() { ... }
}
In this case you do not have to designate itemRenderer
at all because render
of a child item adopts all its roles. The syntax remains the same: HTML string, a selector or a function can be used.
render
and afterrender
events
After an item has been inserted into an array and its HTML node has already been created but it hasn't been inserted into array container yet, the render
event is fired on inserted item. After its triggering you can declare needed data bindings.
afterrender
is fired when HTML node is inserted into the container.
<form class="my-form"></form>
class MyModel extends Seemple.Object {
constructor(data) {
super(data);
// wait for the event triggering
this.on('render', () => {
// define bindings
this.bindNode('isChecked', ':sandbox .my-checkbox');
this.bindNode('text', ':sandbox .text',
Seemple.binders.html());
});
}
});
class MyArray extends Seemple.Array {
get Model() { return MyModel; }
itemRenderer = `<label>
<input type="checkbox" class="my-checkbox">
<span class="text"></span>
</label>`;
constructor() {
super();
this.bindNode('sandbox', '.my-form');
this.push({
isChecked: true,
text: 'Buy a raccoon'
}, {
isChecked: false,
text: 'Sell the raccoon'
});
}
});
const app = new MyArray();
The code above will create the following HTML tree:
<form class="my-form">
<label>
<input type="checkbox" class="my-checkbox">
<span class="text">Buy a raccoon</span>
</label>
<label>
<input type="checkbox" class="my-checkbox">
<span class="text">Sell the raccoon</span>
</label>
</form>
And it will bind the checkboxes to the corresponding isChecked
and text
properties.
Remember, the opportunity of listening delegated events is implemented in the framework. The array can listen to an event of an item rendering, using the *@render
event name (see the documentation of eventNames).
this.on('*@render', () => {
alert('Child item is rendered');
});
Rendered HTML node becomes a sandbox for inserted item allowing to use the
:sandbox
selector and some other possibilities after rendering. If an item enters a few collections at once, set thebindRenderedAsSandbox
property tofalse
for an item to cancel this behavior.
class MyModel extends Seemple.Object {
bindRenderedAsSandbox = false;
// ...
});
onItemRender
and onRender
To improve the readability of code there is a virtual method called Seemple.Array#onItemRender, which can be used instead of render
event. As second alternative, the onRender
can be used for a "model".
class MyModel extends Seemple.Object {
constructor(data) {
super(data);
},
onRender(evt) {
this.bindNode('isChecked', ':sandbox .my-checkbox');
this.bindNode('text', ':sandbox .text',
Seemple.binders.html());
}
}
class MyArray extends Seemple.Array {
get Model() { return MyModel; }
itemRenderer = '...';
constructor() {
//...
},
onItemRender(item, evt) {
//...
}
}
const app = new MyArray();
A template engine
If you have a look at the examples of using Seemple.Array and Seemple.Array#itemRenderer, you can notice that all logic which is responsible for the two-way and one-way data binding is contained in JavaScript code. But when you develop a very simple collection which does not include complicated logic, lots of bindings, etc, you would like to have a shorter variant of the binding declaration. A template including bindings (see Seemple#parseBindings) can be passed to itemRenderer
for that.
class MyArray extends Seemple.Array {
itemRenderer: `<label>
<input type="checkbox" checked="{{isChecked}}">{{text}}
</label>`
// ...
}
const app = new MyArray();
The cancellation of rendering
As is seen from above if the render
property of the child item is specified, Seemple.Array
will try to render it. In order to completely cancel rendering for an array, set a property renderIfPossible
of Seemple.Array
instance as false
.
class MyArray extends Seemple.Array {
renderIfPossible = false;
// ...
}
Moving an object across few collections
By default, when you add an object to an array the framework will try to render it using itemRenderer
. It gives a great advantage in cases when you have two or more lists which include the same object. The lists react instantly on any change in the object.
But sometimes you just need to move an object across few collections without re-rendering it again. If you need to move object to another collection including its sandbox use the moveSandbox
flag while adding the new item to another array.
this.push_(item, {
moveSandbox: true
})
itemRenderer
reassigning
When you reassign itemRenderer
a collection rerenders automatically.
this.itemRenderer = '<div class="new-renderer"></div>';
This ability allows to load renderer from a server.
fetch('templates/template.html')
.then(resp => resp.text())
.then(data => {
this.itemRenderer = data;
});
To render the only objects which aren't yet rendered use Seemple#set with forceRerender
flag set to false
.
this.set('itemRenderer', renderer, {
forceRerender: false
});
It's useful when you use server prerendering (look at the Seemple.Array#restore) and template needs to be loaded asynchronously.
class MyArray extends Seemple.Array {
constructor() {
super()
.bindNode('sandbox', '.some-node')
.restore();
fetch('templates/template.html')
.then(resp => resp.text())
.then(data => {
this.set('itemRenderer', data, {
forceRerender: false
});
});
}
}
Rendering of a collection that contains any kind of object
It's not obligatory to fill an array with only Seemple
instances, you can render any kind of object. Bindings for such objects can be defined using static method Seemple.bindNode.
class MyArray extends Seemple.Array {
// Model is not given
itemRenderer: ...
onItemRender(item) {
Seemple.bindNode(item, 'x', ':sandbox .some-node');
}
})
One more little example: the rendering of a simple list:
class MyArray extends Seemple.Array {
itemRenderer = '<li>{{value}}</li>';
constructor() {
super().bindNode('sandbox', '.my-list');
}
});
const arr = new MyArray();
arr.push({ value: 'Item 1' }, { value: 'Item 2' });
Links
Seemple.Array#renderIfPossible: boolean
The renderIfPossible
property cancels the array rendering if it equals false
Links
Examples
class MyArray extends Seemple.Array {
get renderIfPossible() {
return false;
}
// ...
});
<virtual>Seemple.Array#trackBy: string
trackBy
property indicates a key of unique IDs for collection items
In case if a client and a server activelly communicate with each other and collection items have unique IDs, then redrawing entire collection from scratch does not make sense. After the server respond with new collection, much better to check does the old collection contain objects which have the corresponding identifiers. If object ID from new collection matches object ID from old one, old object will be updated and used again. That means a new object (new instance of Seemple.Array#Model) will not be created and new DOM node will not be rendered.
trackBy
works only using Seemple.Array#recreate because recreate
is the only method which recreates collection from scratch.
In examples below _id
key is used as identifier (you can use any other key).
class MyArray extends Seemple.Array {
get trackBy() {
return '_id';
}
constructor() {
//...
}
});
const arr = new MyArray();
// adds 3 objects to the collection
arr.recreate([
{_id: 0, name: 'Foo'},
{_id: 1, name: 'Bar'},
{_id: 2, name: 'Baz'}
]);
// the next recreate call
// removes an object with _id: 0
// adds an object with _id: 3
// updates an object with _id: 3 (changes name from Bar to BarNew)
// updates an object with _id: 2 (changes name from Baz to BazNew)
// resorts collection
arr.recreate([
{_id: 1, name: 'BarNew'},
{_id: 3, name: 'Qux'},
{_id: 2, name: 'BazNew'}
]);
trackBy
can have "$index"
value, allowing to update objects by their index in collection.
class MyArray extends Seemple.Array {
get trackBy() {
return '$index';
}
constructor() {
//...
}
});
const arr = new MyArray();
// adds 3 objects to the collection
arr.recreate([
{name: 'Foo'},
{name: 'Bar'},
{name: 'Baz'}
]);
// the next recreate call
// updates all 3 items with new "name"
// adds an object with name "Qux"
arr.recreate([
{name: 'NewFoo'},
{name: 'NewBar'},
{name: 'NewBaz'},
{name: 'Qux'}
]);
Links
Seemple.Array.from(arrayLike, mapFn, thisArg)
→
seempleArray
The function creates a new Seemple.Array instance from array-like or iterable object
Returns seempleArray
Arguments
Name | Type | Details |
---|---|---|
arrayLike | object | Array-like or iterable object |
mapFn optional | function | The mapping function which is called for each element of an array |
thisArg optional | * | An object which is used as |
Links
Examples
const seempleArray = Seemple.Array.from([1, 2, 3, 4]);
const seempleArray = Seemple.Array.from([1, 2, 3, 4], item => item * 2);
Inheritance of the method
class MyClass extends Seemple.Array {
// ...
}
const myArray = MyClass.from([1, 2, 3, 4]);
console.log(myArray instanceof MyClass); // true
Seemple.Array.of() → seempleArray
The function creates a new Seemple.Array instance with a variable number of arguments, regardless of number or type of the arguments
Returns seempleArray
Links
Examples
const seempleArray = Seemple.Array.of(1, 2, 3, 4);
Inheritance of the method
class MyClass extends Seemple.Array {
// ...
}
const myArray = MyClass.of(1, 2, 3, 4);
console.log(myArray instanceof MyClass); // true
eventHandler: function
Event handler. Takes any arguments passed to Seemple#trigger
Arguments
Name | Type | Details |
---|---|---|
options | * | Any arguments |
Examples
const eventHandler = (...args) => {
console.log(args);
};
this.on('fyeah', eventHandler);
// logs 'foo', 'bar', 'baz'
this.trigger('fyeah', 'foo', 'bar', 'baz');
seemple: object
Seemple instance
Examples
const seemple = new Seemple();
obj.calc('a', 'b');
seempleObject: object
Seemple.Object instance
Examples
const obj = new Seemple.Object({ foo: 'x' });
obj.setData({ bar: 'y' });
seempleArray: object
Seemple.Array instance
Examples
const arr = new Seemple.Array(1, 2, 3);
arr.push(4);
eventNames: string
Event name or space-delimited list of event names.
Custom events.
this.on('myevent', () => {...});
this.trigger('myevent');
change:KEY
which is triggered every time when a property is changed.
this.on('change:x', evt => {...});
this.x = 42;
beforechange:KEY
which is triggered every time before a property is changed.
this.on('beforechange:x', evt => {...});
this.x = 42;
addevent:NAME
and addevent
which are triggered on event add.
// for any event
this.on('addevent', evt => {...});
// for "someevent" event
this.on('addevent:someevent', evt => {...});
// the line below fires "addevent" and "addevent:someevent"
this.on('someevent', evt => {...});
removeevent:NAME
and removeevent
which are triggered on event remove.
// for any event
this.on('removeevent', evt => {...});
// for "someevent" event
this.on('removeevent:someevent', evt => {...});
// the line below fires "removeevent" and "removeevent:someevent"
this.off('someevent', evt => {...});
DOM_EVENT::KEY
, where DOM_EVENT is a name of DOM event, KEY is a key. A handler is called when DOM_EVENT is triggered on a node which is bound to the KEY.
this.bindNode('x', '.my-div');
this.on('click::x', evt => {
alert('clicked ".my-div"');
});
DOM_EVENT::KEY(SELECTOR)
, where DOM_EVENT is a name of DOM event, KEY is a key, SELECTOR is a selector. A handler is called when DOM_EVENT is triggered on a node which matches the SELECTOR within a node bound to the KEY.
<div class="my-div">
<button class="my-button"></button>
</div>
this.bindNode('x', '.my-div');
this.on('click::x(.my-button)', evt => {
alert('clicked ".my-button"');
});
DOM_EVENT::(SELECTOR)
, where DOM_EVENT is a name of DOM event, SELECTOR is a selector. A handler is called when DOM_EVENT is triggered on a node which matches the SELECTOR within a sandbox.
this.bindNode('sandbox', '.my-div');
this.on('click::(.my-button)', evt => {
alert('clicked ".my-button"');
});
The same as:
this.bindNode('sandbox', '.my-div');
this.on('click::sandbox(.my-button)', evt => {
alert('clicked ".my-button"');
});
Delegated events: PATH@EVENT
, where PATH is a path to a target object whose events we want to listen, EVENT is an event name.
this.on('a@someevent', () => {...});
this.on('a.b.c@change:d', () => {...});
If you need to listen an event of every item of Seemple.Array or every data property of Seemple.Object, you can use an asterisk "*" instead of specific property name.
this.on('*@someevent', () => {...});
this.on('*.b.*.d@change:e', () => {...});
Any combinations. All events described above can be combined.
this.on('x.y.z@click::(.my-selector)', () => {...});
binder: object
binder
contains all information about how to synchronize instance property value with DOM node state. Every member of a binder uses HTML node as its context (this
)
Properties
Name | Type | Details |
---|---|---|
on optional | string function | DOM event (or space-delimited list of events) which tells when the node state is changed. Besides, it accepts a function as a value if you need to customize a listener definition |
getValue optional | function | A function which tells how to retrieve a value (state) from HTML node when DOM event is fired |
setValue optional | function | A function which tells how to change DOM node when the property value is changed |
initialize optional | function | A function which is called before binding is launched. For example it can initialize jQuery plugin or something else |
destroy optional | function | A function which is called when a binding is removed using |
Examples
const binder = {
on: 'click',
getValue(bindingOptions) {
return this.value;
},
setValue(v, bindingOptions) {
this.value = v;
},
initialize(bindingOptions) {
alert('A binding is initialized');
},
destroy(bindingOptions) {
alert('A binding is destroyed');
}
};
this.bindNode('a', '.my-checkbox', binder);
const binder = {
on(callback, bindingOptions) {
this.onclick = callback;
},
// ...
};
// ...
eventOptions: object
An object which can contain service flags or custom data which will be passed to an event handler
Examples
const eventOptions = { silent: true };
this.a = 1;
this.on('change:a', () => {
alert('a is changed');
});
this.set('a', 2, eventOptions); // no alert
const eventOptions = { f: 'yeah' };
this.a = 1;
this.on('change:a', eventOptions => {
alert(eventOptions.f);
});
this.set('a', 2, eventOptions); // alerts "yeah"
class: function
A class made using ECMAScript 2015 syntax or returned by Seemple.Class function
Examples
class MyClass {
method() { ... }
};
const MyClass = Seemple.Class({
method() { ... }
});
node
A DOM node
Examples
const node = document.querySelector('.foo');
$nodes
DOM nodes collection. For example, jQuery instance or NodeList.
Examples
let $nodes = $('.foo');
$nodes = document.querySelectorAll('.bar');
string
A string
Examples
const foo = 'bar';
boolean
A boolean
Examples
const bool = true;
number
A number
Examples
const num = 42;
object
An object
Examples
const obj = {
foo: 'x',
['bar']: 'y'
};
array
An array
Examples
const arr = ['foo', undefined, null, () => {}];
function
A function
Examples
function comeOnBarbieLetsGoParty() {
alert("I'm a Barbie girl, in a Barbie world");
}
null
null
Examples
const x = null;
*
Any type
Examples
let whatever = 'foo';
whatever = 42;
FAQ
How does Seemple work?
Seemple uses accessors, setters in particular, for implementing the two-way data binding and catching the events of property changing.
As an example of how the two-way data binding works (bindNode function in particular), have a look at this code:
function bindNode(object, key, node, binder) {
const value = object[key];
Object.defineProperty(object, key, {
get() {
return value;
},
set(v) {
binder.setValue.call(node, v);
}
});
node.addEventListener(binder.on, () => {
value = binder.getValue.call(node);
});
}
As you see, it’s real easy (for simplicity, the function doesn’t support many-to-many binding).
How to pre-render an application on a server
For the app pre-rendering Seemple.js can be used on Node.js (window
global object can be created via jsdom) or any template system you want on any server platform. The first case is fine for static HTML generation and the second for dynamic pages.
The task of client-side is to restore application state from HTML. Seemple#bindNode extracts element value and assigns it to a property and Seemple.Array#restore restores a state of a collection.
What is "debounce"
At this page you often can see the phrase "micropattern debounce". This is widely used pattern which enforces that a function not be called again until a certain amount of time has passed without it being called. More info can be found there.
How should look like a big application?
An application written with Seemple.js usually represents one nested JavaScript object where every branch is Seemple instance. The new branches are created using Seemple#instantiate which ensures the integrity of the application and allows to replace a state of the application using ordinary assignment.
How to render one object at few collections
The first thing: you need to set bindRenderedAsSandbox
as false
for an item class. It turns off automatic sandbox initialization for rendered object (two sandboxes for one object aren't allowed).
The second thing: when render
event is fired, check where the object is rendered. Parent array can be get using parentArray
property of the event object.
Example. You have User
class and two collections UsersA
and UsersB
(their Seemple.Array#itemRenderer can be different). For both collections User
is a model.
class User extends Seemple.Object {
constructor() {
super();
this.bindRenderedAsSandbox = false;
setInterval(() => {
// when "name" is changed
// both bound nodes also change
this.name = Math.random();
}, 5000);
}
onRender(evt) {
const { parentArray, node } = evt;
if(parentArray instanceof UsersA) {
this.bindNode({
// pseudo-sandbox creation for syntax sugar at selectors
// (not required)
nodeA: node,
name: ':bound(nodeA) .name',
email: ':bound(nodeA) .email',
});
} else if(parentArray instanceof UsersB) {
this.bindNode({
nodeB: node,
name: ':bound(nodeB) .user-name',
});
}
}
}
Actually there are many ways to solve this problem. For example bindings can be declared at classes of the collections (an array will listen to render
event of inserted objects)...