Litedom is an elegant Web Component library.
At ~3.5kb gzip, it allows you to create Web Component/Custom Element easily. Litedom can effortlessy be added into exitsing HTML page, without the need to bring in the bloat of big frameworks.
With Litedom, you can create your own custom tag element, to be reused throughout the application.
Components created with Litedom are reactive. Litedom provides an internal state manager, a simple progressive templating language by leveraging Javascript Template Literals, provides a one way data flow, has two-way data biding and events handling, lifecycle, directives, stylemaps. It has no dependecies, no virtual DOM, no JSX, No build tool.
Litedom follows the Web Component V1 specs, which allows you to have Shadow Dom Spec, Custom Element Spec, HTML Template Spec and ES Module Spec. It is compatible with all modern browsers that support ES2015 (ES6), ESM (ES Module), Proxy, etc.
Litedom is set to be easy, simple and straight forward.
Features: Web Components, Custom Element, Template Literals, Reactive, Data Binding, One Way Data Flow, Two-way data binding, Event Handling, Props, Lifecycle, State Management, Computed Properties, Directives and more.
Litedom turns the template into template string literal and doesn't have a virtual DOM, therefor it doesn't keep a DOM tree in memory. Instead it relies on the real DOM, and only mutates it in place whenever there is change. This tends to be memory efficient, and also reduces GC activities
Litedom aims to be simple, easy to use and helps you do much more.
Custom Element create reusable element by specifying a tagName
(custom tag).
<script type="module">
import Litedom from '//unpkg.com/litedom';
const template = `
Counting {this.count}
`;
Litedom({
template,
tagName: `my-counter`,
data: {
count: 0
},
created() {
this.data.count = this.prop.start || 0;
setInterval(_=> {
this.data.count++;
}, 1000)
}
})
</script>
<!-- HTML -->
<!-- this will start at 5 -->
<my-counter start=5></my-counter>
<!-- this will start at 13 -->
<my-counter start=13></my-counter>
Inline element gets created if a tagName
was not provided, and the el
is refering to the element on the page.
<script type="module">
import Litedom from '//unpkg.com/litedom';
Litedom({
el: `#root`,
data: {
count: 0
},
created() {
setInterval(_=> {
this.data.count++;
}, 1000)
}
})
</script>
<!-- HTML -->
<!-- this will be relifted and shown in place -->
<div id="root">
Hello I'm inline and counting: {this.count}
</div>
Expression are placed within {...}
and are updated whenever the data
values are changed, making data
reactive.
<script type="module">
import Litedom from '//unpkg.com/litedom';
Litedom({
el: `#root`,
data: {
name: 'Litedom',
license: 'MIT',
timestamp: Date.now()
}
})
</script>
<!-- HTML -->
<div id="root">
<div>Library: {this.name}</div>
<div>License: {this.license}</div>
<div>Timestamp: {this.timestamp}</div>
<div>Template literal evaluation {1 + 1}</div>
<!-- real template literal, can do everything -->
<div>Library Upper: {this.name.toUpperCase()}</div>
<!-- with HTML data attribute -->
<div data-license="{this.license}">{this.license.toUpperCase()}</div>
</div>
For conditional use :if
and :else
<script type="module">
import Litedom from '//unpkg.com/litedom';
Litedom({
el: `#root`,
data: {
count: 0
},
created() {
setInterval(_=> {
this.data.count++;
}, 1000)
}
})
</script>
<!-- HTML -->
<div id="root">
Hello I'm inline and counting: {this.count}
<span :if="this.count % 2 === 0">This Even</span>
<span :else>This Odd</span>
</div>
For For-loop use :for
<script type="module">
import Litedom from '//unpkg.com/litedom';
Litedom({
el: `#root`,
data: {
items: [
'bread',
'butter',
'sugar',
'drink',
'cake'
]
}
})
</script>
<!-- HTML -->
<div id="root">
<h2>This is the list</h2>
<ul>
<li :for="item in this.items">I want {item}</li>
</ul>
</div>
To create an event listener, use @$event-name
as an attribute in the element.
<script type="module">
import Litedom from '//unpkg.com/litedom';
Litedom({
el: `#root`,
sayHello(event) {
console.log('Hello World!')
}
})
</script>
<!-- HTML -->
<div id="root">
<a @click="sayHello" href="#">Say Hello!</a>
</div>
Two-way data binding is set on form elements, with @bind
pointing to the data to be updated.
<script type="module">
import Litedom from '//unpkg.com/litedom';
Litedom({
el: `#root`,
data: {
name: ''
}
})
</script>
<!-- HTML -->
<div id="root">
<div>Name: {this.name}</div>
<div>Enter name: <input type="text" @bind="name"></div>
</div>
Lifecycle put some hooks on the component and get executed based on what happens
<script type="module">
import Litedom from '//unpkg.com/litedom';
const template = `
Counting {this.count || 'no count'}
`;
Litedom({
template,
tagName: `my-counter`,
created() {
// runs once, when the element is added
},
updated() {
// run each time the dom is updated from the data
},
removed() {
// when the element is removed from the page
}
})
</script>
Litedom is written in ES2015 and distributed as standard JavaScript modules (ESM). Modules are increasingly supported in JavaScript environments and have shipped in Chrome, Firefox, Edge, Safari, and Opera.
The recommended way to import Litedom is via ESM javascript, where we specify the type module
in the script tag, and we import it from unpkg.com
Make sure type="module"
exists in the script tag (<script type="module">
).
<script type="module">
import Litedom from '//unpkg.com/litedom';
...
</script>
The JavaScript import statement only works inside module scripts (<script type="module">
), which can be inline scripts (as shown above) or external scripts:
<script type="module" src="$PATH/script.esm.js"></script>
Or by installing it in your project
npm install litedom
import Litedom from 'litedom';
Litedom is a modern library for moden browsers that support ES2015 (ES6), Template Literals, Proxy, and all the fun stuff.
The library is written in ES2015, and will be delivered to you as such. To keep it small Litedom doesn't have any polyfills nor extra code to make new ES20xx features available in non modern browsers, therefor it will not work with browsers that don't support ES6, Template Literals, Proxy, etc.
https://caniuse.com/#feat=es6
https://caniuse.com/#search=proxy
Litedom turns your application into smaller composable fully compliant Web Component (Custom Element + Shadow DOM), which can be used as In-Place elements or Custom Elements with Custom Tags to be reused.
In-Place Elements is set in place by using the current DOM element section to turn it into reactive. An in-place element is not intended to be reused. It also requires the el
to be set, and tagName
to be omitted.
<script type="module">
import Litedom from '//unpkg.com/litedom';
Litedom({
el: '#root',
data: {
world: 'World'
}
})
</script>
<div id="root">
Hello {this.world}
</div>
Custom Element is set using a Custom Tag, which can be reused in multiple places. And also, as Custom Element, it allows you to place your component in an external JS file.
Unlike In-Place element, Custom Element requires a tagName
and a template
.
<script type="module">
import Litedom from '//unpkg.com/litedom';
Litedom({
tagName: 'hello-world',
template: `Hello {this.world} {this.prop.name}!`,
data: {
world: 'World'
}
})
</script>
<!-- usage -->
<hello-world name='Mardix'></hello-world>
<hello-world name='Sebastien'></hello-world>
<hello-world name='Samien'></hello-world>
The recommended way to import Litedom is via ESM javascript, where we specify the type module
in the script tag, and we import it from unpkg.com
Make sure type="module"
exists in the script tag (<script type="module">
).
<script type="module">
import Litedom from '//unpkg.com/litedom';
Litedom(options=object|array)
</script>
or
<script type="module" src="$PATH/script.esm.js"></script>
Litedom
function accepts one argument which can be of:
Object: as a plain object, it contains the config to create and initialize the element.
Litedom({
tagName: 'component-x',
template: '...'
})
Array: as an array, it accepts an array of configs, to create and initialize multiple elements at once.
const componentA = {
template: '...',
tagName: 'component-a'
};
const componentB = {
template: '',
tagName: 'component-b'
};
Litedom([componentA, componentB]);
el
:[string|HTMLElement]
To be used mainly when creating In-Place Elements.
This is where the view instance will be created and rendered. It will use thee innerHTML of the element as template.
This can be html selector , ie #someId
, [some-data-attribute]
. Or a query selector document.querySelector('#myId')
.
tagName
:[string]
Name for the new custom element. Note that custom element names must contain a hyphen. my-counter
will be used as <my-counter></my-counter>
By having a tagName it will automatically turn the component into a Custom Element.
data
:[object]
Is the application state. All data in here are reactive. Whenever a property is added, updated or removed it will trigger the update of the DOM (if necessary).
Values are expected to be the type string, number, plain object, array, boolean, null, undefined or function.
In the case of a function, it will become a computed data.
created
[function]
This is a lifecycle hook method. It runs once the component is added on the page.
updated
[function]
This is a lifecycle hook method. It runs each time the data or the store update the component's state.
removed
[function]
This is a lifecycle hook method. It runs once the component is removed from the page.
template
[string]
A string/text for the body of the element. It contains all the markup to be displayed. When creating Custom Element.
shadowDOM
:[boolean:false]
By default elements are created as normal Custom Element. To set the web component as ShadowDOM, set shadowDOM
to true
.
$store
:[state management interface]
Unlike data
store is where to hook a shared store manager, ie: reStated, Redux. The store instance must have the methods getState()
and subscribe(callback:function)
.
Along the lifecycle methods created
, updated
and mounted
, you have the ability to define your own methods.
The defined methods are set with the rest of the options.
WARNING:
When creating methods don't use arrow functions such as created: () => this.sayHello(),
. Since arrow function doesn't have a this
, this
will be treated as any other variable and will often result in error such as Uncaught TypeError: Cannot read property of undefined
or Uncaught TypeError: this.myMethod is not a function
<script type="module">
import Litedom from '//unpkg.com/litedom';
Litedom({
el: '#root',
data: {},
created() {
this.sayHello('Litedom');
},
sayHello(name) {
console.log(`Hello ${name}`)
},
})
</script>
<div id="root"></div>
Inside of the lifecycle and defined methods, you have access to the following properties:
this.el
Is the instance root element. It allows you to safely query, manipulate the instance's DOM elements.
Litedom({
// will run each time there is a re-render
updated() {
const allLis = this.el.querySelectorAll('li');
console.log(allList.length);
}
})
this.data
Gives you access to the reactive data
. You can get, set and delete properties.
Whenever a data
is updated it will trigger re-render (if necessary). You don't have to pre define a property in data
to make it reactive.
Litedom({
data: {
name: ''
},
methodA() {
this.data.name = 'Mardix'; // setter
console.log(this.data.location) // getter
this.data.myArray = [];
this.data.myArray.push(1);
console.log(this.data.myArray.length);
}
})
this.prop
Props are the attributes that were set during initialization
<script>
Litedom({
tagName: `my-counter`,
template: `Counting: {this.count}`
data: {
count: 0
},
created() {
this.data.count = this.prop.start || 0;
setTimeout(_=> { this.data.count++; }, 1000)
}
})
</script>
<my-counter start=5></my-counter>
...this.$defined-methods
The other methods you have defined
Litedom({
methodA() {
this.methodB();
},
methodB() {
this.methodC();
}
methodC() {
console.log(`I'm method C :)`)
}
})
For every instance that gets created, Litedom provides two lifecycle methods that get added during the initialization.
All lifecycle methods have:
*All methods have access to the following instance's properties:
this.el
: Is the instance root element. It allows you to safely query, manipulate the instance's DOM elements. ie: this.el.querySelector('ul')
this.data
: Gives you access to the reactive data. You can get, set and delete properties. Whenever a data is updated it will trigger re-render (if necessary), ie: console.log(this.data.name)
this.prop
: Give you access to the properties that were set as attributes in the custom element.
...this.defined-methods
all of the defined methods, ie: this.my-defined-method()
created
runs once when the Custom Element is added to the page. At the time of running, the DOM is ready, you can query elements.
It is also the place to initialize some async call, ajax etc.
Litedom({
created() {
//... code here
}
})
Litedom({
el: '#root',
data: {
loading: false,
loaded: false,
results: []
},
async created() {
// Could be used on the page to show spinner
this.loading = true;
this.loaded = false;
const data = await fetch('some-url');
const result = await data.json();
this.data.results = results;
// Tell the page everything is good to go
this.loading = false;
this.loaded = true;
}
})
updated
runs only each time the state updates the DOM. This is a place to do any computations after an update.
Litedom({
updated() {
//... code
}
})
Litedom({
data: {
totalLis: 0
},
updated() {
const lis = this.el.querySelectorAll('li');
this.data.totalLis = lis.length;
}
})
removed
runs once when the Custom Element is removed from the page.
It is also the place to do some cleanup, remove intervals etc.
Litedom({
removed() {
//... code here
}
})
Litedom uses an HTML-based template syntax that allows you to declaratively bind the rendered DOM to the instance’s data. All Litedom templates are valid HTML that can be parsed by spec-compliant browsers and HTML parsers.
To interpolate, use the single brace {...}
without the the dollar-sign $
or to use it as template literals with ${...}
in it.
In the template you have access to data via this.#data-property-name
, where '#data-property-name' is the property name to access.
<script type="module">
Litedom({
el: '#root',
data: {
name: 'Litedom'
},
created() {
// Dynamically added
this.data.todaysDate = new Date().toLocaleString();
}
})
</script>
<div id="root">
<p>Hello {this.name}</p>
<p>Date: {this.todaysDate}
</div>
this
this
in your template indicate the root context of the data. By not putting this
, the variable will fall under the global object, which is the window
in the browser. With this
we keep the data in scope.
<div id="root">
<!-- use from data -->
{this.firstName}
<!-- fall under the global object/window -->
{new Date().toLocaleString()}
</div>
Directives are special attribute that start with :
(colon) that you place in HTML elements as a normal data attribute, ie: <span :if="this.x === y">show</span>
. They serve as shorthands to convert to template literals stuff that could be too challenging to write.
// directive
<span :if="this.index === 5">Show me</span>
// The code above will be converted to
${this.index === 5 ? `<span>Show me</span>` : ``}
// Here's how to iterate over a list of items
<ul>
<li :for="item in this.items">{item}</li>
</ul>
Values can be of any javascript conditional. Values should not be placed in ${...}
or {...}
inside of the directive. It should be written as normal string.
DO THIS: <span :if="this.index === 5">show me</span>
DON'T DO: <span :if="${this.index === 5}">show me</span>
:if
can be used to conditionally add or remove the elements.The same way you would write your conditional in javascript.
:else
can also be used to indicate an "else block" for :if
. The element must immediately follow the :if
, or it will not be recognized.
<div id="root">
<div :if="this.count !== 5">The count is not {this.count}</div>
<div :if="this.isTrue">Show me</div>
<div :else> Show me ELSE</div>
</div>
<script type="module">
Litedom({
el: '#root',
data: {
isTrue: true,
count: 5
}
})
</script>
:for
can be used to iterate over a list of items. Underneath it will turn it into map
.
The :for
directive requires a special syntax in the form of item in items
, where items
is the source data Array and item
is an alias for the Array element being iterated on.
You can also have item, index in items
, where index
is tracking the number.
It is recommended to provide an :key
directive or id
attribute with :for
whenever possible, because Litedom patches the element in place. For the key, use either string or a number, or a combination of both. You may also use the index of the loop to set it as id.
<div id="root">
<ul>
<li :for="location in this.locations">{location.name}</li>
</ul>
</div>
<script type="module">
Litedom({
el: '#root',
data: {
locations: [
{
name: 'Charlotte'
},
{
name: 'Atlanta'
},
{
name: 'Concord'
}
]
}
})
</script>
<div id="root">
<ul>
<li :for="state in this.states">
{state.name}
<ul>
<li>Cities</li>
<li :for="city in state.cities">{city}</li>
</ul>
</li>
</ul>
</div>
<script type="module">
Litedom({
el: '#root',
data: {
states: [
{
name: 'NC',
cities: [
'Concord',
'Charlotte',
'Raleigh'
]
},
{
name: 'Florida',
cities: [
'Tampa',
'Miami',
'Jacksonville'
]
},
{
name: 'South Carolina',
cities: [
'Columbia',
'Greenville'
]
}
]
}
})
</script>
<div :for="i in [...Array(5).keys()]">I'm {i}</div>
<div :for="i in [...Array(5).keys()]" :key="my-div-{i}">I'm {i}</div>
:class
allows to conditionally toggle class names. Separates each class condition with a semi-colon, in the following format className: conditionToBeTrue;
=> :class="classA: this.x === y; classB: this.z > 5"
<style>
.somAClass {
color: blue
}
.myClassB {
color: red
}
</style>
<script>
Litedom({
data: {
count: 0
}
})
</script>
<div :class="someClassA: this.count === 7; myClassB: this.count === 10">
Will have .someClassA if count is 7,
will then have .myClassB when count is 10
</div>
:style
help sets inline style dynamically in the element. The data passed, must be the type of plain object which CSS style.
<script>
Litedom({
data: {
myStyle: {
backgroundColor: 'red',
display: 'none',
'font-size': '12px;'
}
}
})
</script>
<div :style="this.myStyle"></div>
// will become
<div style="background-color: red; display: none; font-size: 12px"></div>
Data is at the core of the instance's reactivity. Whenever data is changed, it will trigger a re-render (if necessary).
Data is usally set during the instance's setup, under the data
options.
data
[object]
Is the application state. All data in here are reactive. Whenever a property is added, updated or removed it will trigger the update of the DOM (if necessary).
Values are expected to be the type string, number, plain object, boolean, null, undefined or function.
In the case of a function, it will become a computed data.
Litedom({
data: {
firstName: 'Mardix',
lastName: 'M.',
fullName: (state) => `${state.firstName} ${state.lastName}`
}
})
Data in Litedom is:
this.data.aNumber = 1;
or this.data.someArray.pop();
this.data
is updated it will trigger a re-render (if necessary)Props are simply attributes that were passed in the Custom Element. They can be retrived in the methods via this.prop
or in the template {this.prop}
<script type="module">
const template = `counting: {this.count}`;
Litedom({
template,
tagName: 'my-counter',
data: {
count: 0
},
created() {
this.data.count = this.prop.start || 0;
setInterval(() => {
this.data.count++;
}, 1000)
}
})
</script>
<my-counter start=5></my-counter>
Local state is the data that the instance will use. It is set in the data
. Whenever it is updated, it will trigger a re-render (if necessary).
In the template you have access to it via {this.#data-property-name}
and in your methods it's via this.data
;
The state/data is mutable only in the methods of your instance, which means you can directly update the properties. No need for this.set(key, value) or this.get(key).
You can do this:
Litedom({
el: '#root',
data: {
name: 'Litedom',
count: 0
},
sayHello() {
console.log(this.data.name);
},
changeName(name) {
this.data.name = name;
},
runCounter(){
setInterval(() => {
this.data.count++;
}, 1000)
},
created() {
this.runCounter();
}
})
Computed state are data that will be created based on some other input, usually from the reactive data
. Whenever the state is updated, the computed data will also be updated. Which makes computed data reactive.
Computed data are set as function that returns a value, which will be assigned to the name of the function in the data
object.
data: {
firstName: 'Mardix',
lastName: 'M.',
// computed data, will be accessed via '{this.fullName}' or 'this.data.fullName'
fullName: (state) => `${state.firstName} ${state.lastName}`,
// computed data, will be accessed via '{this.totalChars}' or 'this.data.totalChars'
totalChars: (state) => state.fullName.length
}
In the example above, we now can access as properties: this.data.fullName
and this.data.totalChars
. In the template, {this.fullName}
and {this.totalChars}
NOTE 1: You can't access the computed data as functions in your code.
NOTE 2: You can't mutate the state in the computed data funcion, nor access an instance's method in the computed data function.
Computed data function accept the current state as the only argument, and must return a value. The value will be assigned in the data
with the function name. The data provided in the computed data is not mutable.
<script type="module">
Litedom({
el: '#root',
data: {
firstName: 'Mardix',
lastName: 'M.',
fullName: (state) => `${state.firstName} ${state.lastName}`
}
})
</script>
<div id="root">
<p>Hello {this.fullName}</p>
</div>
You can use the @bind
directive to create two-way data bindings on form input, textarea, and select elements. It automatically picks the correct way to update the element based on the input type. @bind
is essentially syntax sugar for updating data on user input events.
<script type="module">
import Litedom from '//unpkg.com/litedom';
Litedom({
el: `#root`,
data: {
name: '',
salutation: ''
}
})
</script>
<!-- HTML -->
<div id="root">
<div>Hello {this.salutation} {this.name}</div>
<!---- Form ---->
<form>
<div>Enter name: <input type="text" @bind="name"></div>
<div>Salutation:
<input type="radio" name="salutation" @bind="salutation" value="Mr."> Mr. -
<input type="radio" name="salutation" @bind="salutation" value="Mrs."> Mrs.
</div>
</form>
</div>
The example below illustrate how we can make async call and at the same time setting the state to make it reactive.
<div id="root">
<div $if="this.loadingStatus === 'loading'">Loading...</div>
<div $if="this.loadingStatus === 'done'">
<p>Data loading successfully!</p>
<ul>
<li $for="item in this.myData">{item}</li>
</ul>
</div>
</div>
<script type="module">
Litedom({
el: '#root',
data: {
loadingStatus: null,
myData: []
},
async loadData() {
this.loadingStatus = 'loading';
const resp = await fetch('some-url');
this.data.myData = await resp.json();
this.loadingStatus = 'done';
}
})
</script>
To share state with multiple instances, please refer to the SHARED STATE section in this guide.
You can define your own methods in the instance.
Method can be used to be accessed by other methods via this.$method-name(...args)
, or can be used as events methods in the instance of @click="$method-name"
*All methods have access to the following instance's properties:
this.el
: Is the instance root element. It allows you to safely query, manipulate the instance's DOM elements. ie: this.el.querySelector('ul')
this.data
: Gives you access to the reactive data. You can get, set and delete properties. Whenever a data is updated it will trigger re-render (if necessary), ie: console.log(this.data.name)
this.prop
: Give you access to the properties that were set as attributes in the custom element.
...this.defined-methods
all of the defined methods, ie: this.my-defined-method()
The example below showcases how methods can be used.
<div id="root">
<a @click="sayHello" href="#">Say Hello!</a>
<input
type="text"
name="color"
@call="changeColor"
$value="this.defaultColor"
>
</div>
<script type="module">
Litedom({
el: '#root',
data: {
defaultColor: '#FFFFFF'
},
sayHello(event) {
console.log('Hello World!');
},
changeColor(event) {
const color = event.target.value;
this.setBgColor(color);
},
setBgColor(color) {
this.el.style.background = color;
},
})
</script>
You can also setup Async methods with the async/await
.
Litedom({
el: '#root',
async loadData() {
this.data.status = 'loading...';
const data = await fetch('url');
const data = await resp.data;
this.data.status = 'loading completed!';
},
async created(event) {
await this.loadData();
},
})
You can add event listener to elements by adding the @
+ the $event-name
as attribute, and assign it the name of the method to bind it to: <a @click="sayHello" href="#">Say Hello!</a>
The $event-name must be the name of the event without on
, ie: @click
is VALID but @onclick
is INVALID.
The method must be in the context of the instace that's created.
When an event is invoked, the Event
object is passed to the method as the first and only argument. The Event
object can be used to retrieve data attribute of the element, etc.
<div id="root">
<a @click="sayHello" href="#">Say Hello!</a>
</div>
<script type="module">
Litedom({
el: '#root',
data: {},
sayHello(event) {
console.log('Hello World!')
}
})
</script>
When the button is clicked it will 'Hello World' will be displayed on the console.
To pass values from the element to the event, we can use html attribute and retrieve the data from there. We can't pass object directly to the method. It has to be done via data attribute. With the data attribute, we can use it to retrieve some more data from some other sources.
<div id="root">
<button @click="sayHello" data-name="Mardix">Say Hello!</button>
</div>
<script type="module">
Litedom({
el: '#root',
data: {},
sayHello(event) {
const name = event.target.getAttribute('data-name');
console.log(`Hello ${name}`)
}
})
</script>
Will now show Hello Mardix
@call
is a shorthand key that will assign the right event based on the element type.
By default all @call
will result into @click
, except for the scenarios below:
HTMLAnchorElement
AHREF @call
=> @click
<a @call="something">x</a> to
<a href="javascript:void(0);" @click="something"></a>
HTMLInputElement & HTMLTextAreaElement
FORMS: Input & Textarea @call
=> @input + @paste
<input type="text" @call="something"> to
<input type="text" @input="something" @paste="something">
HTMLSelectElement
FORMS: Select @call
=> @change
<select @call="something"><options...></select>
<select @change="something"><options...></select>
HTMLFormElement
FORMS: Form @call
=> @submit
<form @call="something"></form>
<form @submit="something"></form>
Here is the list of all the events accepted by Litedom
@call
@click
@submit
@change
@input
@select
@focus
@blur
@hover
@reset
@keydown
@keypress
@keyup
@dblclick
@mouseenter
@mouseleave
@mousedown
@mousemove
@mouseout
@mouseover
@mouseup
@contextmenu
@drag
@dragend
@dragenter
@dragstart
@dragleave
@drop
@cut
@copy
@paste
To share state with multiple instances, it's recommended to have a state manager such as *RESTATED, Redux, or look through this List of State Managers
For the store to be hooked into Litedom, it must have the following methods:
getState()
: To return the full state of the store.
subscribe(callback:function)
: A subscription method that will execute each the state is updated.
If the state manager doesn't provide these methods by default, you can extend it yourself.
const myStateManager = new somethingSomething()
// Now the store contains getState() and subscribe(callback)
const store = {
getState() {
return myStateManager.state;
},
subscribe(callback) {
return myStateManager.onChange(callback);
},
...myStateManager
}
Litedom({
el: '#root',
data: {},
$store: STORE_INSTANCE
})
The store is exposed in the methods by this.$store
, which is the object that was passed. Therefor you can access anything from it.
Litedom({
el: '#root',
data: {},
$store: STORE_INSTANCE,
doSomething() {
this.$store.doSomething();
}
})
To access properties from the store, this.$store
is exposed and contain the values from $store.getState()
.
<div id="root">
{this.$store.fullName}
</div>
reStated
An ambitiously tiny flux-like library to manage your state.
Inspired by Redux and Vuex, reStated removes the boilerplate and keep it simple and flat.
Unlike Redux, you don't need to return a new immutable object. You can mutate the state in place, and you definitely don't need to define a reducer. The action mutator is both your action and your reducer "at the same damn time" (Future's song)
Unlike Vuex, you don't need to have actions and mutations. You can only mutate the state via your actions mutators which are just function that pass as first argument the current state to be mutated.
Learn more about RESTATED
This is how we can use shared state with reStated.
<script type="module">
import Litedom from '//unpkg.com/litedom';
import reStated from '//unpkg.com/restatedjs';
const store = reStated({
state: {
name: '',
lastName: '',
fullName: (state) => `${state.name} ${state.lastName}`,
accountDetails: []
},
changeName(state, name) {
state.name = name;
},
changeLastName(state, lastName) {
state.lastName = lastName;
},
async loadAccount(state) {
state.status = 'loading';
const resp = await fetch(url);
const data = await resp.json();
// will be shared as this.$store.accountDetails
state.accountDetails = data;
state.status = 'done';
}
});
Litedom([
{
el: '#rootA',
$store: store,
loadAccount() {
this.$store.doSomething();
}
},
{
el: '#rootB',
$store: store
}
]);
</script>
<div id="rootA">
Hello {this.$store.fullName}!
<button @call="loadAccount">Load Account</button>
</div>
<div id="rootB">
<ul>
<li :for="item in this.$store.accountDetails">{accountName}</li>
</ul>
</div>