dom-render
TypeScript icon, indicating that this package has built-in type declarations

1.0.97 • Public • Published

DOM-RENDER

typescript license

  • 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}$  <!-- 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🔻(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>  <!-- 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>
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> <!-- output html : <span>hello</span> -->
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>
<!-- 💥 submit call --> 
<!-- console: {name: 'name data', age: 'age data'}  -->
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')
// 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>${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);
  }
}
// RouteData type
type RouteData = {
  path: string;
  url: string;
  data?: any;
  searchParams: URLSearchParams;
  pathData?: any;
}

Messenger (Data transmission)

  • publish, subscribe
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'}
      });
      // messenger?.createChannel(this).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

// 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

<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}),  // 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="${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

Package Sidebar

Install

npm i dom-render

Weekly Downloads

20

Version

1.0.97

License

MIT

Unpacked Size

735 kB

Total Files

195

Last publish

Collaborators

  • visualkhh