Our primary goals are
- view template engine
- Dom control and reorder
- all internal variables are managed by proxy. (DomRenderProxy)
🚀 Quick start
npm install dom-render
😃 examples
📄 Code description
initialized
<!doctype html><html lang="en"><body id="app"></body></html>
const target = document.querySelector('#app');
const data = DomRender.run({name: 'my name is dom-render'}, target);
data.name = 'modify name';
print and call
<div>${this.name}</div>
<div>${this.office.addr.first}, ${this.office.addr.last}, ${this.office.addr.street} (${this.office.name})</div>
<div dr="this.office.addr.street">${this.getOfficeFullAddr()}</div>
dr-if
<div dr-if="true">true</div>
<div dr-if="this.gender==='M'">gender: M</div>
dr-for, dr-for-of
<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-repeat
<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
<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, 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
- ref: window
- variable: $target
window-event-popstate: <input type="text" dr-window-event-popstate="alert(this.name)"><br>
dr-event
- other event
- ref: element
- variable: $params, $event
<input dr-event:bind='eventName1, eventName2' dr-event="console.log('event', $params, $event)" type="text">
dr-value, value-link
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>
dr-attr
<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
<div dr-class="{big: this.age > 50, red: this.age > 50}">
<div dr-class="'big yellow ' + (this.age > 50 ? 'old' : 'young')">
<div dr-class="['small', 'yellow']">
dr-style
<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
as-is
<div dr-strip="true"><span>hello</span></div>
to-be
<span>hello</span>
dr-on-init
<input dr-on-init="this.onInitElement">
dr-before, dr-after
<div dr-before="console.log('process before')" dr-after="console.log('process after')"></div>
dr-complete
<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>
LifeCycle
* OnInitRender
- onInitRender(): init render 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
export namespace Profile {
export const templat = '<div>aaaaa${this.name}aaaaa </div>';
export const styles = ['p {color: red}', 'div {margin: ${this.margin} + \'px\' }'];
export class Component {
public name = 'default name';
public margin = 5;
public say() {
console.log('say!~')
}
}
}
new DomRender.run(obj, target, {
targetElements: [
RawSet.createComponentTargetElement('my-element', (e, o, r) => new Profile.Component(), Profile.templat, Profile.styles, scripts)
],
});
using component
<my-element dr-on-init="$component.say();"></my-element>
- attribute
- dr-on-init: component created init call script
- $component: component instance
- $element: element instance
- $attribute: element attribute object
- $innerHTML: element innerHTML string
dr-form
- event: change
- modify change: dr-form:event="input"
class User {
form = {};
submit() {
const form = (this.form as any)
console.log('submit->', form, form.name, form.age, form.addr);
}
}
<form dr-form="this.form" dr-event-submit="this.submit(); $event.preventDefault();">
name: <input name="name">
age: <input name="age">
addr: <input dr-form:event="input" name="addr">
<button type="submit">submit</button>
</form>
validator
class PageValidator extends Validator {
required = new RequiredValidator();
notEmpty = new NotEmptyValidator();
empty = new EmptyValidator();
regexp = new RegExpTestValidator(/[0-9]/);
mix = new MultipleValidator([new RequiredValidator(), new NotEmptyValidator()]);
all = new ValidValidatorArray((v, t, e) => {
return !((v ?? []).filter(it => !it.checked).length > 0);
});
gender = new ValidValidatorArray((v, t, e) => {
return ((v ?? []).filter(it => it.checked).length > 0);
});
valid(): boolean {
return super.childValids();
}
}
class User {
form = new PageValidator();
submit() {
console.log('submit valid->', this.form.valid());
}
}
<form dr-form="this.form" dr-event-submit="this.submit(); $event.preventDefault();">
<h2>validator</h2>
required: <input name="required"> <br>
notEmpty: <input name="notEmpty"> <br>
empty: <input name="empty"> <br>
regexp: /[0-9]/ <input name="regexp"> <br>
<h2>mix validator</h2>
required, notEmpty: <input name="mix"> <br>
<h2>all check required</h2>
<input name="all" type="checkbox" value="a">a <br>
<input name="all" type="checkbox" value="b">b <br>
<h2>gender chose one</h2>
<input name="gender" type="radio" value="male"> Male <br>
<input name="gender" type="radio" value="female"> Female<br>
<button type="submit">check valid</button>
</form>
validator
- Validator (abstract)
- ValidatorArray (abstract)
- ValidValidator
- ValidMultipleValidator
- ValidValidatorArray
- BlockValidator
- CheckedValidator
- EmptyValidator
- MultipleValidator
- NotEmptyValidator
- RegExpTestValidator
- RequiredValidator
- SkipValidator
Detect Get, Set
OnBeforeReturnSet
export interface OnBeforeReturnSet {
onBeforeReturnSet(name: string, value: any, fullPath?: string[]): void;
}
OnBeforeReturnGet
export interface OnBeforeReturnGet {
onBeforeReturnGet(name: string, value: any, fullPath?: string[]): void;
}
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']
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({...})}
Config
export type TargetElement = {
_name: string,
template?: string,
styles?: string[],
callBack: (target: Element, obj: any, rawSet: RawSet) => DocumentFragment,
complete?: (target: Element, obj: any, rawSet: RawSet) => void
};
export type TargetAttr = {
name: string,
callBack: (target: Element, attrValue: string, obj: any, rawSet: RawSet) => DocumentFragment,
complete?: (target: Element, attrValue: string, obj: any, rawSet: RawSet) => void
};
export interface Config {
targetElements?: TargetElement[];
targetAttrs?: TargetAttr[];
onElementInit?: (name: string, obj: any, rawSet: RawSet) => void;
onAttrInit?: (name: string, attrValue: string, obj: any, rawSet: RawSet) => void;
proxyExcludeTyps?: ConstructorType<any>[];
proxyExcludeOnBeforeReturnSets?: string[];
proxyExcludeOnBeforeReturnGets?: string[];
scripts?: { [n: string]: any };
applyEvents?: { attrName: string, callBack: (elements: Element, attrValue: string, obj: any) => void }[];
}
License