Als-component
Table of contents
About
Als-component allows you to manage dom content as components with JavaScript like you do it in React, but in more simple way.
You can use sane Component on backend and on frontend.
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
We are closer than ever to final release!
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:
Component.components
Component.ref(ref,add='')
Component.qs(selector,dom=document)
Component.qsa(selector,dom=document)
Component.vars
Component.actions
Component.dispatch(componentName,action,data,id)
Component == window.$C
let c = new Component(ComponentName,fn,data)
c.update(data,id,updateElement=true)
c.get(data,id)
c.element(id)
c.ref(ref,add)
c[id] = {update,get,data,element,id}
c.methods
c.methods.$
c.actions
c.actions.dispatch(action,data,id)
c.actions.$
c.vars
Parameters:
-
new Component(ComponentName,fn,data)
- ComponentName - Component's name
- Available as Component.ComponentName or as $C.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
or Component.componentName.ids.id.data
if there is id
-
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}
- updateElement - if false, update will return fn result only (without updating html element)
- true by default - updating data and html element
-
get(data,id)
- same as update(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 $C('counter',function() {
if(this.data <0) this.data = 0
return `<div>
<button onclick="$C.Counter.update($C.Counter.data+1)">+</button>
<span>${this.data}</span>
<button onclick="$C.Counter.update($C.Counter.data-1)">-</button>
</div>`
}).update(0)
</script>
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 $C('counter',function() {
if(this.data <0) this.data = 0
return `<div>
<button onclick="$C.Counter.update($C.Counter.data+1)">+</button>
<span>${this.data}</span>
<button onclick="$C.Counter.update($C.Counter.data-1)">-</button>
</div>`
})
$C.Counter.before = function(data,id) {
console.log('before',data)
}
Component.onDom = function(element) {
console.log('Component.onDom')
}
$C.Counter.onDom = function(element) {
console.log('Counter.onDom')
}
$C.Counter.onFirstMount = function(data,id) {
console.log('onFirstMount',data)
}
$C.Counter.onMount = function(data,id) {
console.log('onMount',data)
}
$C.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.$)
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 `<div>
${data.map(todo => $C.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 = `<hr>`
Todos.vars.isHr = false
}
return `<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 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 `
<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} = require('als-component')
global.Component = $C = Component
app.get('/',[
(req,res,next) => {
require('./app')
next()
},
(req,res) => {
let name = 'Alex'
return res.end (`<!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)}</script>
</html>
`)
}])
app.listen(3000)
app.js
new Component('app',function(data) {
return `
<div>Hello ${data.name}</div>
`
})
$C.App.onLoad = function() {
console.log('hello')
}
Example above, showing the way for using Component on frontent and on backend. Here are the highlights:
- Define global.Component before using the routes - now Component available anywhere and it's the same one anywhere.
- Use Export method for exporting global.Component to frontend.
- Use onLoad hook to add function which will runs then all content will be loaded
- Include components as middleware, if you need those components only for some routes. Otherwise, component will be available for all routes.