Als-component
being tested
Table of contents
About
Dynamicly manage dom content as components and states on frontend and backend. You can do it with Vite like you do it in React, on Express as middleware or simply in frontend.
We are closer than ever to final release!
New in 0.9.82
- Use
$
instead$C
New in 0.9.8
- Component = require('als-component').get() - get Component after clearing cache
- Component.export - changed and fixed. Now it's return errors.
- Component.create() - checks if Component not exists
New in 0.9.7
- if component doesnt return enything, no error, the result 'undefined'
- inside onLoad function available this (component it self)
New in 0.9.6
- Export for Component.actions fixed
New in 0.9.5
- import is separated to
als-front-import
- Component.components
- Component.vars
- Component.methods
- Component.methods.$
- Component.actions
- Component.dispatch
- Component.ref(ref,add='')
- Component.qs
- Component.qsa
- component.vars
- component.methods
- component.methods.$
- component.actions.$
- component.ids.id = {id,data,get:element,update}
- component.onLoad() - for backend
- $C = Component (and not new Component())
- no $CComponentName and $cComponentName
Get started
Instalation and Cdn
- Install the package with
npm i als-component
- Add
component.js
to your head inside html
<head>
<script src="node_modules/als-component/component.js"></script>
</head>
- Use
Component class
Basic syntax
The component.js has class Component which do all the magic behind the scenes. You need to create new component and then get it result or update it to existing html element.
The syntax:
// Static methods
Component.components // includes all components
Component.ref(ref,add='') // select dom element with ref="ref" and add (additional selector)
Component.qs(selector,dom=document) // dom.querySelector(selector)
Component.qsa(selector,dom=document) // [...dom.querySelectorAll(selector)]
Component.vars // empty object for variables
Component.actions // empty object for actions and $ method
Component.dispatch(componentName,action,data,id)
Component == window.$ // true - by default $ available as reference to Component
let c = new Component(ComponentName,fn,data)
c.update(data,id,updateElement=true) // return created dom html element
c.get(data,id) // return string html
c.element(id) // return component's element, if id - with specific id
c.ref(ref,add) // return element inside component's element with selector [ref=ref]add
c.ids[id] = {update,get,data,element,id} // data and elements are getters
c.methods // object for methods
c.methods.$ // getter for component object - available as this.$ in method
c.actions // object for action functions
c.actions.dispatch(action,data,id)
c.actions.$ // getter for component object - available as this.$ in actions
c.vars // object for variables
Parameters:
-
new Component(ComponentName,fn,data)
-
ComponentName - Component's name
- Available as Component.ComponentName or as $.ComponentName
-
fn - function(data,id) which has to return html string code
- id parameter necessary only then you use more then one component
-
data - "the state" for component
- available as
Component.componentName.data
orComponent.componentName.ids.id.data
if there is id
- available as
-
ComponentName - Component's name
-
update(data,id,updateElement=true)
-
data - updates the "state".
- If data undefined, will be used data from component object (the state)
- data has to be new object
-
id - specify which data to use and which element to update
- will create object componet.id= {id,data,element,update,get}
- element is a getter
- will create object componet.id= {id,data,element,update,get}
-
updateElement - if false, update will return fn result only (without updating html element)
- true by default - updating data and html element
-
data - updates the "state".
-
get(data,id)
- same asupdate(data,id,false)
-
element(id)
- if id undefined return component main element (with component="componentname")
- if id not undefined return component element with given id
-
ref(ref,add='')
- return dom element with selector ``[component={componentName}] [ref="{ref}"]add`
Example
<div component="counter"></div>
<script>
new $('counter',function() {
if(this.data <0) this.data = 0
return /*html*/`<div>
<button onclick="$.Counter.update($.Counter.data+1)">+</button>
<span>${this.data}</span>
<button onclick="$.Counter.update($.Counter.data-1)">-</button>
</div>`
}).update(0)
</script>
Component.create
There is static method Component.create()
which equivalent to new Component()
. The method, checking if component allready exists and create new one if not. Return the component obj.
Syntax:
Component.create(ComponentName,fn,data):object instance of Component
Life cycle for update/get
You can add functions for executing on different stages of binding process.
Life cycle for update/get
- this.before(data,id)
- this.fn(data,id)
- Component.onDom(element,parentTemplate)
- this.onDom(element,parentTemplate)
- this.onFirstMount(data,id)
- this.onMount(data,id)
Here example for hooks:
<div component="counter"></div>
<script>
new $('counter',function() {
if(this.data <0) this.data = 0
return /*html*/`<div>
<button onclick="$.Counter.update($.Counter.data+1)">+</button>
<span>${this.data}</span>
<button onclick="$.Counter.update($.Counter.data-1)">-</button>
</div>`
})
// Before running fn
$.Counter.before = function(data,id) {
console.log('before',data)
}
// Then dom element is ready, but not mounted yet
// Runing in all components
Component.onDom = function(element) {
console.log('Component.onDom')
}
// Then dom element is ready, but not mounted yet
$.Counter.onDom = function(element) {
console.log('Counter.onDom')
}
// Then dom mounted first time - runing only once
$.Counter.onFirstMount = function(data,id) {
console.log('onFirstMount',data)
}
// Runing every time after dom has mounted
$.Counter.onMount = function(data,id) {
console.log('onMount',data)
}
$.Counter.update(0)
</script>
On example above, on first run youll get on console:
before 0
Component.onDom
Counter.onDom
onFirstMount 0
onMount 0
After clicking on minus:
before -1
Component.onDom
Counter.onDom
onMount 0
Simple redux system
Each Component object has actions
object.
You can add to this objects your actions for modifying data. The action has to return new data for update the component.
- For using actions you need to run
dispatch
method which update component with data that action return - Inside every action you can use
this.$
which reference to Component's object
Also you have Component.actions
object and Component.dispatch(componentName,action,data,id)
which update component with componentName with data that action will return.
Syntax
let Test = new Component('test',function() {})
Test.actions.testing = function(data,id) {
console.log(this.$) // Test component
return data
}
Test.dispatch('testing',{})
Let's see Todos app example that manage todos with localstorage and containes 3 files:
- index.html
- Todos component
- Todo component
Part of index.html
<script src="/node_modules/als-component/component.js"></script>
<input type="text" placeholder="add todo"
onchange="Todos.dispatch('add',this)">
<div component="todos"></div>
<script src="todo.js"></script>
<script src="todos.js"></script>
todos.js
let Todos = new Component('todos',function(data) {
this.vars.isHr = true
return /*html*/`<div>
${data.map(todo => $.Todo.get(todo,todo.id)).join('')}
</div>`
})
Todos.before = function(data,id) {
if(data == undefined) {
data = localStorage.getItem('todos') || {}
data = JSON.parse(data)
}
data.sort((a, b) => a.completed > b.completed ? 1 : -1);
localStorage.setItem('todos',JSON.stringify(data))
this.data = data
}
Todos.actions.add = function(element) {
let newTodo = {
name:element.value,
completed:false,
id:(Math.random() + 1).toString(36).substring(7)
}
element.value = ''
return [...this.$.data,newTodo]
}
Todos.actions.remove = function(id) {
return this.$.data.filter(obj => obj.id !== id)
}
Todos.actions.completed = function(id) {
return this.$.data.map(obj => {
if(obj.id == id) obj.completed = !obj.completed
return obj
})
}
Todos.update()
todo.js
new Component('todo',function({name,id,completed,hr=''}){
if(completed && Todos.vars.isHr) {
hr = /*html*/`<hr>`
Todos.vars.isHr = false
}
return /*html*/`<div>
${hr}
<button onclick="Todos.dispatch('remove','${id}')">×</button>
<span onclick="Todos.dispatch('completed','${id}')"
${completed ? 'style="text-decoration: line-through;"' : ''}
>${name}</span>
</div>`
})
Quick snippets and highlighted html
To show html highlighted code inside string in js, use es6-string-html plugin
in vsCode and
/*html*/`html code`
-
Install plugin:
es6-string-html plugin
. -
File > Preferences > Configure user snippets > javascript
-
Add:
"html": {
"prefix": "html",
"body": ["/*html*/`$1`"],
"description": "colored html"
},
"rhtml": {
"prefix": "rhtml",
"body": ["return /*html*/`$1`"],
"description": "return colored html"
},
Build with Vite
You can use Vite for building projects as you do in React.
Just do the folowing:
- Install Vite with
npm create vite@latest
(choose vanila) - install all packages for Vite (npm install) and then Component
npm i als-component
- Update main.js with the folowing:
import {Component} from 'als-component'
new Component('app',function() {
return /*html*/`
<div>Hello From component App</div>
`
}).update()
That's all!
Export in express
You can use components inside your Express and then export them to frontend. Each time you add new Component, you need to export the updated Component class as shown on example below.
server.js
let express = require('express')
let app = express()
let {Component,Export,get} = require('als-component')
let consoleLogErrors = true
let get$ = (req,res,next) => {
req.Component = get()
next()
}
app.get('/:name',[get$,
(req,res) => {
let {Component} = req
new Component('App',function(data) {
return /*html*/`
<div>Hello ${data.name}</div>
`
})
$.App.onLoad = function() {
console.log('hello '+this.data.name)
}
let name = req.params.name
return res.end (/*html*/`<!DOCTYPE html>
<html lang="en">
<head>
<title>Component export</title>
</head>
<body>
<input type="text" value="${name}"
oninput="Component.App.update({name:this.value})">
${Component.App.get({name})}
</body>
<script>${Export(Component,consoleLogErrors)}</script>
</html>
`)
}])
app.listen(3000)
Example above, showing the way for using Component on frontent and on backend. Here are the highlights:
- Define req.Component widht als-component.get method as middleware on route you want to use it.
- Use Export method for exporting Component to frontend.
- Use onLoad hook to add function which will runs then all content will be loaded
Use get() method to require component and use it as middleware, to include on each route only it's relevant components and methods.