DOM-RENDER
- view template engine
- Dom control and reorder and render
- all internal variables are managed by proxy. (DomRenderProxy)
🚀 Quick start
<script src="https://cdn.jsdelivr.net/npm/dom-render@1.0.91/dist/bundle.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body id="app">
${this.name}$
<script src="https://cdn.jsdelivr.net/npm/dom-render@1.0.86/dist/bundle.js"></script>
<script>
let data = {
name: 'my name is dom-render'
};
data = DomRender.run(data , document.querySelector('#app'));
</script>
</body>
</html>
😃 examples
Expression
${...}$, #{...}# 🔻(click)
${}$ innerTEXT, #{}# innerHTML
<body id="app">
${this.name}$
#{this.name}#
<script>
let data = {
name: '<i>my name is dom-render</i>'
};
data = DomRender.run(data , document.querySelector('#app'));
</script>
</body>
dom-render attributes
attribute change, bind
attribute🔻(click)
attribute
<body id="app">
<input type="text" value="${this.name}$" style="${'color: '+this.color}$">
<button dr-event-click="this.changeData();">change</button>
</body>
class Data {
name = 'my name is dom-render';
color = '#ff0000';
changeData() {
this.name = RandomUtils.getRandomString(10);
this.color = RandomUtils.getRandomColor();
}
}
const data = DomRender.run(new Data(), document.querySelector('#app')!);
control, print Statement
dr-if🔻(click)
if element render
<body id="app">
<div dr-if="true">true</div>
<div dr-if="this.gender === 'M'">gender: M</div>
<script>
let data = {
gender: 'F'
};
data = DomRender.run(data , document.querySelector('#app'));
</script>
</body>
dr-for, dr-for-of🔻(click)
<div dr-for="var i = 0; i < this.friends.length; i++"> friend</div>
<div dr-for-of="this.friends"> ${#it#.name}$</div>
<div dr-for-of="$range(10, 20)"><div>${#it#}$</div><div>
<div dr-for="var i = 1 ; i <= 9 ; i++" dr-it="i">
${#it#}$ *
<scope dr-for="var y = 1 ; y <= 9 ; y++" dr-it="y" dr-var="superIt=#it#" dr-strip="true">
#it# = ${var.superIt * #it#}$
</scope>
</div>
dr-appender🔻(click)
<h3>appender</h3>
<ul>
<li dr-appender="@this@.appender">
${#it#}$
</li>
</ul>
<button dr-event-click="@this@.append()">appending</button>
<button dr-event-click="@this@.modifyAppender(0)">idx 0 modify</button>
<button dr-event-click="@this@.clearAppend()">appender clear</button>
class Data {
appender = new Appender();
constructor() {
this.appender.push('init' + RandomUtils.uuid(), 'init' + RandomUtils.uuid());
}
append() {
this.appender.push(RandomUtils.uuid(), RandomUtils.uuid());
}
clearAppend() {
this.appender.clear()
}
modifyAppender(idx: number) {
this.appender[idx][0] = RandomUtils.uuid();
}
}
dr-repeat🔻(click)
<div dr-repeat="10"><div>#it#</div></div>
<div dr-repeat="$range(10, 20)"><div>#it#</div></div>
<div dr-repeat="$range(10, 20, 5)"><div>#it#</div></div>
<div dr-repeat="$range('10..5, 2')"><div>#it#</div></div>
dr-inner-text, dr-inner-html🔻(click)
<div dr-inner-text="'<b>aa</b> <button dr-event-click=\'alert(1)\'>aa</button>'"> friend</div>
<div dr-inner-html="'<b>aa</b> <button dr-event-click=\'alert(1)\'>aa</button>'"> friend</div>
event
dr-event-(name)🔻(click)
- click, mousedown, mouseup, dblclick, mouseover, mouseout, mousemove, mouseenter, mouseleave, contextmenu, keyup, keydown, keypress, change, input, submit, resize, focus, blur
- ref: element
- variable: $event, $target
click: <button dr-event-click="@this@.name = 'name' + new Date()">click</button> <br>
change: <input type="text" dr-event-change="@this@.name = $target.value"> <br>
input: <input type="text" dr-event-input="@this@.name = $target.value"> <br>
keyup: <input type="text" dr-event-keyup="@this@.name = $target.value"> <br>
...
keydown: <input type="text" dr-event-keydown="@this@.name = $target.value"><br>
submit: <form dr-event-submit="console.log($event); $event.preventDefault();"><input type="text"> <button type="submit">submit</button></form><br>
dr-window-event-popstate🔻(click)
- ref: window
- variable: $target
window-event-popstate: <input type="text" dr-window-event-popstate="alert(@this@.name)"><br>
dr-event🔻(click)
- other event
- ref: element
- variable: $params, $event
<input dr-event:bind='eventName1, eventName2' dr-event="console.log('event', $params, $event)" type="text">
this
dr-this🔻(click)
* dr-this
* object mapping
class data {
dictionary = {
name: 'visualkhh'
}
}
<div dr-this="@this@.dictionary">
${@this@.name}$
</div>
dr-this-property🔻(click)
* dr-property
* property forEach in object
* dynamic add, delete
class data {
dictionary = {
name1: 'visualkhh1',
name2: 'visualkhh2',
name3: 'visualkhh3'
}
}
<home dr-this-property="@this@.dictionary" dr-on-init:arguments="[2,#this#]">
${this}$
</home>
value
dr-value, value-link🔻(click)
- dr-value
- The value is assigned the first time.
- dr-value-link
- Value and variable values are referencing each other. It affects each other when changing. (Immediate reflection event: input)
dr-value: <input type="text" dr-value="@this@.office.name"> <br>
dr-value-link: <input type="text" dr-value-link="@this@.office.addr.street"> <br>
other
dr-attr🔻(click)
<textarea dr-attr="{rows: @this@.age/2, cols: @this@.age}"></textarea>
<div dr-attr="{wow: '123', good: 123444}"></div>
<div dr-attr="['wow=123', 'good=123444']"></div>
<div dr-attr="'wow=123, good=123444'"></div>
dr-class🔻(click)
<div dr-class="{big: @this@.age > 50, red: @this@.age > 50}"></div>
<div dr-class="'big yellow ' + (@this@.age > 50 ? 'old' : 'young')"></div>
<div dr-class="['small', 'yellow']"></div>
dr-style🔻(click)
<div dr-style="{fontSize: @this@.age + 'px'}"> style </div>
<div dr-style="{'font-size': '20px'}"> style</div>
<div dr-style="'font-size: ' + @this@.age +'px; margin: ' + @this@.age + 'px'"> style </div>
<div dr-style="['font-size: ' + @this@.age +'px', 'margin: ' + @this@.age + 'px']"> style </div>
dr-strip🔻(click)
<div dr-strip="true"><span>hello</span></div>
dr-before, dr-after🔻(click)
<div dr-before="console.log('process before')" dr-after="console.log('process after')"></div>
dr-complete🔻(click)
<select dr-value-link="@this@.currentContry" dr-event-change="@this@.contryChange($event)">
<option dr-for-of="@this@.languages" dr-value="#it#.key" dr-complete="@this@.currentContry='defaultValue'">${#it#.title}$</option>
</select>
dr-form
form🔻(click)
* event: change
* modify change: dr-form:event="input"
<body id="app">
<form dr-form="@this@.form" dr-event-submit="@this@.submit(); $event.preventDefault();">
name: <input name="name">
age: <input name="age">
<button type="submit">submit</button>
</form>
<script>
let data = {
form: {},
submit() {
console.log(this.form);
}
};
data = DomRender.run(data , document.querySelector('#app'));
</script>
</body>
validator🔻(click)
<body id="app">
<form dr-form="@this@.form" dr-event-submit="@this@.submit(); $event.preventDefault();">
name: <input name="name">
age: <input name="age">
<button type="submit">submit</button>
</form>
<script>
const form = new FormValidator();
form.name = new NotEmptyValidator();
form.age = new NotEmptyValidator();
let data = {
form,
submit() {
if (this.form.valid()){
console.log('valid');
} else {
console.log('inValid');
}
}
};
data = DomRender.run(data , document.querySelector('#app'));
</script>
</body>
validator
- Validator (abstract)
- ValidatorArray (abstract)
- AllCheckedValidatorArray
- AllUnCheckedValidatorArray
- CheckedValidator
- CountEqualsCheckedValidatorArray
- CountEqualsUnCheckedValidatorArray
- CountGreaterThanCheckedValidatorArray
- CountGreaterThanEqualsCheckedValidatorArray
- CountGreaterThanEqualsUnCheckedValidatorArray
- CountGreaterThanUnCheckedValidatorArray
- CountLessThanCheckedValidatorArray
- CountLessThanEqualsCheckedValidatorArray
- CountLessThanEqualsUnCheckedValidatorArray
- CountLessThanUnCheckedValidatorArray
- CountUnCheckedValidatorArray
- EmptyValidator
- ExcludeCheckedValidatorArray
- FormValidator
- IncludeCheckedValidatorArray
- MultipleValidator
- NonPassValidator
- NotEmptyValidator
- NotRegExpTestValidator
- PassValidator
- RegExpTestValidator
- RequiredValidator
- UnCheckedValidator
- ValidMultipleValidator
- ValidValidator
- ValidValidatorArray
- ValueEqualsValidator
- ValueNotEqualsValidator
Route
Route🔻(click)
- config routerType: 'hash' | 'path' | 'none' (default: 'none')
const config: Config = {
window
};
config.targetElements = [
DomRender.createComponent({type: Main, tagName: 'page-main', template: MainTemplate}),
DomRender.createComponent({type: Second, tagName: 'page-second', template: SecondTemplate}),
DomRender.createComponent({type: Detail, tagName: 'page-detail', template: DetailTemplate})
]
config.routerType = 'hash';
const data = DomRender.run(new Data(), document.querySelector('#app')!, config);
<header>
<h1>examples header</h1>
<h2>${@this@.name}$</h2>
<div>
<div><button dr-event-click="$router.go('/')">main</button></div>
<div>
<button dr-event-click="$router.go('/second', {secondata: 555})">second</button>
<button dr-event-click="$router.go('/second/5')">second/1</button>
<button dr-event-click="$router.go('/second/wow')">second/2</button>
</div>
<div><button dr-event-click="$router.go('/detail/25?name=zzz')">detail</button></div>
</div>
</header>
<hr>
<main>
<page-main dr-if="$router.test('/')"></page-main>
<page-second dr-if="$router.test('/second')">1</page-second>
<page-second dr-if="$router.testRegexp('/second/[0-9]?$')">2</page-second>
<page-second dr-if="$router.testRegexp('/second/wow$')">wow</page-second>
<page-detail url='/detail/{id:[0-9]+}' dr-if="$router.test($attribute.url)" dr-on-create:callback="$component.routerData($router.getRouteData($attribute.url))" ></page-detail>
<div>
<button dr-event-click="@this@.plusCount()">${@this@.count}$ count pluse++</button>
</div>
</main>
<hr>
<footer>footer</footer>
export class Second implements OnCreateRender {
name = 'Second'
onCreateRender(data: CreatorMetaData): void {
console.log('----->', data.router)
}
}
import {RouteData} from 'dom-render/routers/Router';
import {OnCreateRender} from 'dom-render/lifecycle/OnCreateRender';
export class Detail implements OnCreateRender {
name = 'Detail';
onCreateRender(data: CreatorMetaData) {
console.log('routeData->', data);
}
routerData(routeData: RouteData) {
console.log('--------', routeData);
}
}
type RouteData = {
path: string;
url: string;
data?: any;
searchParams: URLSearchParams;
pathData?: any;
}
Messenger (Data transmission)
publish🔻(click)
export class Home implements OnProxyDomRender {
private channel?: Channel;
sendIndexMessage() {
const rtn = this.channel?.publish(Index, {
name: this.name,
age: this.age,
title: this.title
});
console.log('sendIndexMessage return value: ', rtn);
}
onProxyDomRender({messenger}: Config): void {
this.channel = messenger?.createChannel(Home);
}
}
subscribe🔻(click)
class Index implements OnProxyDomRender {
onProxyDomRender({messenger}: Config): void {
messenger?.createChannel(this).filter((data) => (data.age ?? 0) > 5).subscribe((data) => {
this.rcvData = data;
return {data: 'good', action: 'actionGood'}
});
}
}
Class
Range🔻(click)
const range = new Range(100,55, 10);
for (let data of new Range(100,55, 10)) {
console.log(data);
}
const rangeArray = new Range(100,55, 10).toArray();
Appender🔻(click)
const appender = new Appender<number>([1, 2]);
appender.push(3, 4)
for (const data of appender) {
console.log('----appender item--->', data);
}
Detect Get, Set
Method Proxy🔻(click)
using detect
{
name: 'dom-render'
onBeforeReturnSet: (name: string, value: any, fullpath: string[]) => {
console.log('set name-->', name, value, fullpath);
}
onBeforeReturnGet: (name: string, value: any, fullpath: string[]) => {
console.log('get name-->', name, value, fullpath);
}
}
exclude detect property: Config
- proxyExcludeOnBeforeReturnGets: ['propertyName']
- proxyExcludeOnBeforeReturnSets: ['propertyName']
OnBeforeReturnSet
export interface OnBeforeReturnSet {
onBeforeReturnSet(name: string, value: any, fullPath?: string[]): void;
}
OnBeforeReturnGet
export interface OnBeforeReturnGet {
onBeforeReturnGet(name: string, value: any, fullPath?: string[]): void;
}
Proxy
all internal variables are managed by proxy. (DomRenderProxy)
exclude proxy (situation: Maximum call stack error)
exclude detect property: Config
- proxyExcludeTyps: [Class...]
Code base
{name : Object.freeze({...})}
{name : new Shield()}
{name : DomRenderProxy.final({...})}
LifeCycle
- OnCreateRender
- onCreateRender(): created call
- OnInitRender
- onInitRender(): init render call
- OnDestroyRender
- onDestroyRender(): component Destroy call
Script
new DomRender.run(obj, target, {
scripts: {
concat: function (head: string, tail: string) {
return head + tail;
}
}
});
using script
const data = config.scripts.concat('head', 'tail')
<div>${$scripts.concat('head', 'tail')}</div>
<div dr-if="$scripts.concat('wow', 'good') === 'wowgood'"> is wowgood</div>
Component, Attribute, AttributeCallBack
😃 examples
<body id="app">
${@this@.name}$
<h1>component</h1>
<profile dr-on-create:callback="$component.name='jhone'; $component.age=55;"><b>${#component#.details}$</b></profile>
<profile dr-on-create:callback="$component.name='cal'; $component.age=56;"><b>detail-2</b></profile>
<profile dr-on-create:callback="$component.name='rose'; $component.age=57;">
<profile dr-on-create:callback="$component.name='rose-sub'; $component.age=156;">
<b>${@this@.name}$</b>
</profile>
</profile>
<h3>component data link and detect</h3>
<Profile dr-if="@this@.toggle" dr-detect="$component.age = @this@.age" dr-on-create:callback="$component.name='papa'; $component.age=58;">
<b>${@this@.name}$</b>
</Profile>
<Profile dr-if="@this@.toggle" dr-detect="$component.age = @this@.age" dr-on-constructor:arguments="[1,2]">
<b>${@this@.name}$</b>
</Profile>
<button dr-event-click="@this@.name = new Date().toString();">change name</button>
<button dr-event-click="@this@.age = Date.now();">change age</button>
<button dr-event-click="@this@.toggle = !@this@.toggle;">change toggle</button>
<j1>component constructor, on-create, dr-on-create:callback</j1>
<home dr-constructor="[@this@.name, @this@.age, 'home welcom']" dr-on-create-arguments="{type: 'onCreate', data: 'datadata'}" dr-on-create:callback="$component.onInit('data')"></home>
<h1>scripts</h1>
<div>
${$scripts.concat('hello', 'tail')}$
</div>
<h1>attr</h1>
<button link="@this@.link">
link attribute
</button>
<h1>attrCallBack</h1>
<input id="callback" type="text" wow>
</body>
config.targetElements = [
DomRender.createComponent({type: Profile, template: ProfileTemplate}),
DomRender.createComponent({type: Home, template: HomeTemplate, styles: HomeStyle})
]
config.targetAttrs = [
DomRender.createAttribute('link',
(element: Element, attrValue: string, obj: any, rawSet: RawSet) => {
return obj;
},
(element: Element, attrValue: string, obj: any, rawSet: RawSet) => {
const fag = window.document.createDocumentFragment();
if (attrValue) {
const n = element.cloneNode(true) as Element;
attrValue = ScriptUtils.eval(`return ${attrValue}`, obj)
n.addEventListener('click', () => {
location.href = attrValue;
});
fag.append(n);
}
return fag;
}
)
]
config.applyEvents = [
{
attrName: 'wow',
callBack: (e, a, o) => {
e.addEventListener('click', (event) => {
alert((event.target as any).value);
})
}
}
]
const data = DomRender.run(new Data(), document.querySelector('#app')!, config);
using component
<my-element dr-on-create:callback="$component.say();"></my-element>
<home value="${@this@.name}$" wow="${@this@.color}$">
${#component#.homeName}$
<home value="${#component#.homeName}$" wow="${#component#.homeColor}$" dr-component-name="sub_component" dr-component-inner-html-name="innerHTML">
${#sub_component#.homeName}$
</home>
</home>
lazy loading rollup config
copy({
targets: [
{
src: ['**/*.html', '**/*.css', '!node_modules/**/*.html', '!node_modules/**/*.css'], dest: 'dist',
rename: (name, extension, fullPath) => `${fullPath}`
},
{ src: 'assets', dest: 'dist' }
]
})
- attribute
- dr-on-create:callback: component created init callback script
- dr-on-create:arguments: component onCreatedRender arguments
- dr-on-init:arguments: component onInitRender arguments
- dr-on-constructor:arguments: component counstructor arguments
- $component: component instance
- $element: element instance
- $attribute: element attribute object
- $innerHTML: element innerHTML string
- $creatorMetaData: metaData
- #component#: component instance
- #innerHTML#: element innerHTML
- dr-component-name: renaming component variable name (default: component)
- dr-inner-html-name: renaming innerHTML variable name (default: innerHTML)
License