dom-render
v1.0.98
Published
html view template engine
Downloads
309
Maintainers
Readme
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/[email protected]/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/[email protected]/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
${}$ innerTEXT, #{}# innerHTML
<body id="app">
${this.name}$ <!-- outout: <i>my name is dom-render</i> -->
#{this.name}# <!-- outout text is italic: my name is dom-render -->
<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
<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
if element render
<body id="app">
<div dr-if="true">true</div> <!-- render -->
<div dr-if="this.gender === 'M'">gender: M</div> <!-- No Render -->
<script>
let data = {
gender: 'F'
};
data = DomRender.run(data , document.querySelector('#app'));
</script>
</body>
<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>
<h3>appender</h3>
<ul>
<li dr-appender="@[email protected]">
${#it#}$
</li>
</ul>
<button dr-event-click="@[email protected]()">appending</button>
<button dr-event-click="@[email protected](0)">idx 0 modify</button>
<button dr-event-click="@[email protected]()">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();
}
}
<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>
<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
- 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="@[email protected] = 'name' + new Date()">click</button> <br>
change: <input type="text" dr-event-change="@[email protected] = $target.value"> <br>
input: <input type="text" dr-event-input="@[email protected] = $target.value"> <br>
keyup: <input type="text" dr-event-keyup="@[email protected] = $target.value"> <br>
...
keydown: <input type="text" dr-event-keydown="@[email protected] = $target.value"><br>
submit: <form dr-event-submit="console.log($event); $event.preventDefault();"><input type="text"> <button type="submit">submit</button></form><br>
- ref: window
- variable: $target
window-event-popstate: <input type="text" dr-window-event-popstate="alert(@[email protected])"><br>
- other event
- ref: element
- variable: $params, $event
<input dr-event:bind='eventName1, eventName2' dr-event="console.log('event', $params, $event)" type="text">
this
class data {
dictionary = {
name: 'visualkhh'
}
}
<div dr-this="@[email protected]">
${@[email protected]}$
</div>
class data {
dictionary = {
name1: 'visualkhh1',
name2: 'visualkhh2',
name3: 'visualkhh3'
}
}
<home dr-this-property="@[email protected]" dr-on-init:arguments="[2,#this#]">
${this}$
</home>
value
- 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="@[email protected]"> <br>
dr-value-link: <input type="text" dr-value-link="@[email protected]"> <br>
other
<textarea dr-attr="{rows: @[email protected]/2, cols: @[email protected]}"></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>
<div dr-class="{big: @[email protected] > 50, red: @[email protected] > 50}"></div>
<div dr-class="'big yellow ' + (@[email protected] > 50 ? 'old' : 'young')"></div>
<div dr-class="['small', 'yellow']"></div>
<div dr-style="{fontSize: @[email protected] + 'px'}"> style </div>
<div dr-style="{'font-size': '20px'}"> style</div>
<div dr-style="'font-size: ' + @[email protected] +'px; margin: ' + @[email protected] + 'px'"> style </div>
<div dr-style="['font-size: ' + @[email protected] +'px', 'margin: ' + @[email protected] + 'px']"> style </div>
<div dr-strip="true"><span>hello</span></div> <!-- output html : <span>hello</span> -->
<div dr-before="console.log('process before')" dr-after="console.log('process after')"></div>
<select dr-value-link="@[email protected]" dr-event-change="@[email protected]($event)">
<option dr-for-of="@[email protected]" dr-value="#it#.key" dr-complete="@[email protected]='defaultValue'">${#it#.title}$</option>
</select>
dr-form
<body id="app">
<form dr-form="@[email protected]" dr-event-submit="@[email protected](); $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>
<!-- 💥 submit call -->
<!-- console: {name: 'name data', age: 'age data'} -->
<body id="app">
<form dr-form="@[email protected]" dr-event-submit="@[email protected](); $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
- config routerType: 'hash' | 'path' | 'none' (default: 'none')
// Config
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'; // 'hash' | 'path' | 'none';
const data = DomRender.run(new Data(), document.querySelector('#app')!, config);
<header>
<h1>examples header</h1>
<h2>${@[email protected]}$</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="@[email protected]()">${@[email protected]}$ 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);
}
}
// RouteData type
type RouteData = {
path: string;
url: string;
data?: any;
searchParams: URLSearchParams;
pathData?: any;
}
Messenger (Data transmission)
- publish, subscribe
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);
}
}
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'}
});
// messenger?.createChannel(this).subscribe((data) => {
// this.rcvData = data;
// return {data: 'good', action: 'actionGood'}
// });
}
}
Class
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();
const appender = new Appender<number>([1, 2]);
appender.push(3, 4)
for (const data of appender) {
console.log('----appender item--->', data);
}
Detect Get, Set
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
// frezz
{name : Object.freeze({...})}
// Shield Object type: {[k: string]: any}
{name : new Shield()}
// DomRenderProxy Final
{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
- examples lazy load (html, css)
<body id="app">
${@[email protected]}$
<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>${@[email protected]}$</b>
</profile>
</profile>
<h3>component data link and detect</h3>
<Profile dr-if="@[email protected]" dr-detect="$component.age = @[email protected]" dr-on-create:callback="$component.name='papa'; $component.age=58;">
<b>${@[email protected]}$</b>
</Profile>
<Profile dr-if="@[email protected]" dr-detect="$component.age = @[email protected]" dr-on-constructor:arguments="[1,2]">
<b>${@[email protected]}$</b>
</Profile>
<button dr-event-click="@[email protected] = new Date().toString();">change name</button>
<button dr-event-click="@[email protected] = Date.now();">change age</button>
<button dr-event-click="@[email protected] = !@[email protected];">change toggle</button>
<j1>component constructor, on-create, dr-on-create:callback</j1>
<home dr-constructor="[@[email protected], @[email protected], '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="@[email protected]">
link attribute
</button>
<h1>attrCallBack</h1>
<input id="callback" type="text" wow>
</body>
config.targetElements = [
DomRender.createComponent({type: Profile, template: ProfileTemplate}), // lazy loading format 'lazy://component/home.html'
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="${@[email protected]}$" wow="${@[email protected]}$">
${#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)