@dooboostore/dom-render 1.0.108 → 1.0.109
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.MD +27 -1451
- package/dist/cjs/DomRender.js +41 -26
- package/dist/cjs/DomRender.js.map +2 -2
- package/dist/cjs/DomRenderProxy.js +12 -5
- package/dist/cjs/DomRenderProxy.js.map +2 -2
- package/dist/cjs/bundle-entry.js +32 -21
- package/dist/cjs/bundle-entry.js.map +2 -2
- package/dist/cjs/components/ComponentBase.js +20 -2
- package/dist/cjs/components/ComponentBase.js.map +2 -2
- package/dist/cjs/components/ComponentRouterBase.js +11 -1
- package/dist/cjs/components/ComponentRouterBase.js.map +2 -2
- package/dist/cjs/components/choose/Choose.js +4 -0
- package/dist/cjs/components/choose/Choose.js.map +2 -2
- package/dist/cjs/components/router/RouterOutlet.js +15 -1
- package/dist/cjs/components/router/RouterOutlet.js.map +2 -2
- package/dist/cjs/components/select/Select.js +20 -6
- package/dist/cjs/components/select/Select.js.map +2 -2
- package/dist/cjs/components/timer/Timer.js +14 -11
- package/dist/cjs/components/timer/Timer.js.map +2 -2
- package/dist/cjs/decorators/DomRenderNoProxy.js +2 -1
- package/dist/cjs/decorators/DomRenderNoProxy.js.map +2 -2
- package/dist/cjs/lifecycle/OnRawSetRendered.js +26 -0
- package/dist/cjs/lifecycle/OnRawSetRendered.js.map +7 -0
- package/dist/cjs/lifecycle/index.js +1 -0
- package/dist/cjs/lifecycle/index.js.map +2 -2
- package/dist/cjs/operators/DrTargetElement.js +2 -0
- package/dist/cjs/operators/DrTargetElement.js.map +2 -2
- package/dist/cjs/operators/OperatorExecuter.js +1 -2
- package/dist/cjs/operators/OperatorExecuter.js.map +2 -2
- package/dist/cjs/package.json +5 -0
- package/dist/cjs/rawsets/RawSet.js +154 -74
- package/dist/cjs/rawsets/RawSet.js.map +3 -3
- package/dist/cjs/rawsets/Render.js.map +1 -1
- package/dist/cjs/routers/Router.js +31 -15
- package/dist/cjs/routers/Router.js.map +3 -3
- package/dist/esm/DomRender.js +41 -26
- package/dist/esm/DomRender.js.map +2 -2
- package/dist/esm/DomRenderProxy.js +12 -5
- package/dist/esm/DomRenderProxy.js.map +2 -2
- package/dist/esm/bundle-entry.js +22 -21
- package/dist/esm/bundle-entry.js.map +2 -2
- package/dist/esm/components/ComponentBase.js +20 -2
- package/dist/esm/components/ComponentBase.js.map +2 -2
- package/dist/esm/components/ComponentRouterBase.js +11 -1
- package/dist/esm/components/ComponentRouterBase.js.map +2 -2
- package/dist/esm/components/choose/Choose.js +4 -0
- package/dist/esm/components/choose/Choose.js.map +2 -2
- package/dist/esm/components/router/RouterOutlet.js +16 -2
- package/dist/esm/components/router/RouterOutlet.js.map +2 -2
- package/dist/esm/components/select/Select.js +20 -6
- package/dist/esm/components/select/Select.js.map +2 -2
- package/dist/esm/components/timer/Timer.js +14 -11
- package/dist/esm/components/timer/Timer.js.map +2 -2
- package/dist/esm/decorators/DomRenderNoProxy.js +2 -1
- package/dist/esm/decorators/DomRenderNoProxy.js.map +2 -2
- package/dist/esm/lifecycle/OnRawSetRendered.js +7 -0
- package/dist/esm/lifecycle/OnRawSetRendered.js.map +7 -0
- package/dist/esm/lifecycle/index.js +1 -0
- package/dist/esm/lifecycle/index.js.map +2 -2
- package/dist/esm/operators/DrTargetElement.js +2 -0
- package/dist/esm/operators/DrTargetElement.js.map +2 -2
- package/dist/esm/operators/OperatorExecuter.js +1 -2
- package/dist/esm/operators/OperatorExecuter.js.map +2 -2
- package/dist/esm/rawsets/RawSet.js +154 -74
- package/dist/esm/rawsets/RawSet.js.map +3 -3
- package/dist/esm/routers/Router.js +31 -15
- package/dist/esm/routers/Router.js.map +3 -3
- package/dist/esm-bundle/dooboostore-dom-render.esm.js +12586 -6496
- package/dist/esm-bundle/dooboostore-dom-render.esm.js.map +4 -4
- package/dist/types/DomRender.d.ts +11 -5
- package/dist/types/DomRender.d.ts.map +1 -1
- package/dist/types/DomRenderProxy.d.ts.map +1 -1
- package/dist/types/bundle-entry.d.ts +5 -2
- package/dist/types/bundle-entry.d.ts.map +1 -1
- package/dist/types/components/ComponentBase.d.ts +7 -1
- package/dist/types/components/ComponentBase.d.ts.map +1 -1
- package/dist/types/components/ComponentRouterBase.d.ts +8 -0
- package/dist/types/components/ComponentRouterBase.d.ts.map +1 -1
- package/dist/types/components/choose/Choose.d.ts.map +1 -1
- package/dist/types/components/index.d.ts +2 -1
- package/dist/types/components/index.d.ts.map +1 -1
- package/dist/types/components/router/RouterOutlet.d.ts +5 -1
- package/dist/types/components/router/RouterOutlet.d.ts.map +1 -1
- package/dist/types/components/select/Select.d.ts +2 -2
- package/dist/types/components/select/Select.d.ts.map +1 -1
- package/dist/types/components/timer/Timer.d.ts +5 -3
- package/dist/types/components/timer/Timer.d.ts.map +1 -1
- package/dist/types/decorators/DomRenderNoProxy.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/lifecycle/OnRawSetRendered.d.ts +10 -0
- package/dist/types/lifecycle/OnRawSetRendered.d.ts.map +1 -0
- package/dist/types/lifecycle/index.d.ts +1 -0
- package/dist/types/lifecycle/index.d.ts.map +1 -1
- package/dist/types/operators/DrTargetElement.d.ts.map +1 -1
- package/dist/types/operators/OperatorExecuter.d.ts.map +1 -1
- package/dist/types/rawsets/RawSet.d.ts +11 -4
- package/dist/types/rawsets/RawSet.d.ts.map +1 -1
- package/dist/types/rawsets/Render.d.ts +1 -0
- package/dist/types/rawsets/Render.d.ts.map +1 -1
- package/dist/types/routers/Router.d.ts +2 -3
- package/dist/types/routers/Router.d.ts.map +1 -1
- package/dist/umd-bundle/dooboostore-dom-render.umd.js +12600 -6529
- package/dist/umd-bundle/dooboostore-dom-render.umd.js.map +4 -4
- package/package.json +6 -5
- package/src/DomRender.ts +62 -34
- package/src/DomRenderProxy.ts +20 -8
- package/src/bundle-entry.ts +5 -3
- package/src/components/ComponentBase.ts +29 -7
- package/src/components/ComponentRouterBase.ts +17 -2
- package/src/components/choose/Choose.ts +4 -0
- package/src/components/router/RouterOutlet.ts +25 -5
- package/src/components/select/Select.ts +40 -26
- package/src/components/timer/Timer.ts +19 -51
- package/src/decorators/DomRenderNoProxy.ts +3 -1
- package/src/lifecycle/OnRawSetRendered.ts +15 -0
- package/src/lifecycle/index.ts +1 -0
- package/src/operators/DrTargetElement.ts +14 -0
- package/src/operators/OperatorExecuter.ts +1 -2
- package/src/rawsets/RawSet.ts +572 -257
- package/src/rawsets/Render.ts +1 -0
- package/src/routers/Router.ts +40 -23
- package/types.d.ts +68 -20
package/README.MD
CHANGED
|
@@ -1,1467 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
===
|
|
3
|
-
[](https://www.npmjs.com/package/@dooboostore/dom-render) [](LICENSE.md)
|
|
4
|
-
* view template engine
|
|
5
|
-
* Dom control and reorder and render
|
|
6
|
-
* all internal variables are managed by proxy. (DomRenderProxy)
|
|
1
|
+
# @dooboostore/dom-render
|
|
7
2
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@dooboostore/dom-render)
|
|
4
|
+
[](https://github.com/dooboostore-develop/packages/actions/workflows/main.yaml)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
name: string = 'my name is dom-render';
|
|
14
|
-
}
|
|
7
|
+
**Full Documentation:** [https://dooboostore-develop.github.io/packages/dom-render](https://dooboostore-develop.github.io/packages/dom-render)
|
|
15
8
|
|
|
16
|
-
|
|
17
|
-
DomRender.run(data, document.querySelector('#app')!);
|
|
18
|
-
```
|
|
19
|
-
```html
|
|
20
|
-
<!DOCTYPE html>
|
|
21
|
-
<html lang="en">
|
|
22
|
-
<head>
|
|
23
|
-
<meta charset="UTF-8">
|
|
24
|
-
<title>Title</title>
|
|
25
|
-
</head>
|
|
26
|
-
<body id="app">
|
|
27
|
-
${this.name}$
|
|
28
|
-
</body>
|
|
29
|
-
</html>
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
# 😃 examples
|
|
33
|
-
- [examples](./examples)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
## Expression
|
|
37
|
-
<details>
|
|
38
|
-
<summary>${...}$, #{...}# <strong>🔻(click)</strong></summary>
|
|
39
|
-
|
|
40
|
-
### ${}$ innerTEXT, #{}# innerHTML
|
|
41
|
-
```html
|
|
42
|
-
<body id="app">
|
|
43
|
-
${this.name}$ <!-- outout: <i>my name is dom-render</i> -->
|
|
44
|
-
#{this.name}# <!-- outout text is italic: my name is dom-render -->
|
|
45
|
-
</body>
|
|
46
|
-
```
|
|
47
|
-
```typescript
|
|
48
|
-
import { DomRender } from '@dooboostore/dom-render';
|
|
49
|
-
|
|
50
|
-
class AppData {
|
|
51
|
-
name: string = '<i>my name is dom-render</i>';
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const data = new AppData();
|
|
55
|
-
DomRender.run(data, document.querySelector('#app')!);
|
|
56
|
-
```
|
|
57
|
-
</details>
|
|
58
|
-
|
|
59
|
-
# dom-render attributes
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
## attribute change, bind
|
|
63
|
-
<details>
|
|
64
|
-
<summary>attribute<strong>🔻(click)</strong></summary>
|
|
65
|
-
|
|
66
|
-
### attribute
|
|
67
|
-
```html
|
|
68
|
-
<body id="app">
|
|
69
|
-
<input type="text" value="${this.name}$" style="${'color: '+this.color}$">
|
|
70
|
-
<button dr-event-click="this.changeData();">change</button>
|
|
71
|
-
</body>
|
|
72
|
-
```
|
|
73
|
-
```typescript
|
|
74
|
-
class Data {
|
|
75
|
-
name = 'my name is dom-render';
|
|
76
|
-
color = '#ff0000';
|
|
77
|
-
|
|
78
|
-
changeData() {
|
|
79
|
-
this.name = RandomUtils.getRandomString(10);
|
|
80
|
-
this.color = RandomUtils.getRandomColor();
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const data = DomRender.run(new Data(), document.querySelector('#app')!);
|
|
84
|
-
```
|
|
85
|
-
</details>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
## style css
|
|
89
|
-
```typescript
|
|
90
|
-
export class IndexComponent implements OnInitRender, OnDestroyRender, OnInit, OnSimCreate, OnDrThisBind, OnDrThisUnBind, OnCreateRender, OnCreateRenderData, OnCreatedThisChild {
|
|
91
|
-
private canvas: HTMLCanvasElement | null = null;
|
|
92
|
-
private ctx: CanvasRenderingContext2D | null = null;
|
|
93
|
-
|
|
94
|
-
name = 'IndexComponent';
|
|
95
|
-
age = 10;
|
|
96
|
-
color = 'red';
|
|
97
|
-
img = 'url(\'https://pbs.twimg.com/media/F1FHR-saMAAMTKm?format=jpg&name=360x360\')';
|
|
98
|
-
//...
|
|
99
|
-
}
|
|
100
|
-
```
|
|
101
|
-
```css
|
|
102
|
-
.sign-container{
|
|
103
|
-
cursor: pointer;
|
|
104
|
-
width: 32px;
|
|
105
|
-
height: 28px;
|
|
106
|
-
background-image: ${@this@.img}$;
|
|
107
|
-
background-position: center; /* 정가운데 */
|
|
108
|
-
background-size: contain; /* 이미지 전체 표시 */
|
|
109
|
-
background-repeat: no-repeat; /* 반복 방지 */
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
```css
|
|
113
|
-
.#uuid# {
|
|
114
|
-
color:${@this@.color}$ ;
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
```css
|
|
118
|
-
@media (width >= /*$@this@.ss$*/) {
|
|
119
|
-
#profileDialog {
|
|
120
|
-
max-width: ${@this@.ss}$; /* 너비 조정 */
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
### Release the style that applies only to the inside of the component. Please write (1line) online
|
|
125
|
-
```css
|
|
126
|
-
## .wow {...}
|
|
127
|
-
## body::-webkit-scrollbar {display: none;}
|
|
128
|
-
```
|
|
129
|
-
```html
|
|
130
|
-
<button dr-event-click="@this@.ss = '50rem'">aa</button>
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
selector expression: /*$@this@.ss$*/
|
|
134
|
-
uuid: rawSetUUID
|
|
135
|
-
##: escape scope
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
## control, print Statement
|
|
140
|
-
<details>
|
|
141
|
-
<summary>dr-if<strong>🔻(click)</strong></summary>
|
|
142
|
-
|
|
143
|
-
### if element render
|
|
144
|
-
```html
|
|
145
|
-
<body id="app">
|
|
146
|
-
<div dr-if="true">true</div> <!-- render -->
|
|
147
|
-
<div dr-if="this.gender === 'M'">gender: M</div> <!-- No Render -->
|
|
148
|
-
</body>
|
|
149
|
-
```
|
|
150
|
-
```typescript
|
|
151
|
-
import { DomRender } from '@dooboostore/dom-render';
|
|
152
|
-
|
|
153
|
-
class AppData {
|
|
154
|
-
gender: string = 'F';
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const data = new AppData();
|
|
158
|
-
DomRender.run(data, document.querySelector('#app')!);
|
|
159
|
-
```
|
|
160
|
-
</details>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
<details>
|
|
167
|
-
<summary>dr-for, dr-for-of<strong>🔻(click)</strong></summary>
|
|
168
|
-
|
|
169
|
-
```html
|
|
170
|
-
<div dr-for="var i = 0; i < this.friends.length; i++"> friend</div>
|
|
171
|
-
<div dr-for-of="this.friends"> ${#it#.name}$</div>
|
|
172
|
-
<div dr-for-of="$range(10, 20)"><div>${#it#}$</div><div>
|
|
173
|
-
<div dr-for="var i = 1 ; i <= 9 ; i++" dr-it="i">
|
|
174
|
-
${#it#}$ *
|
|
175
|
-
<scope dr-for="var y = 1 ; y <= 9 ; y++" dr-it="y" dr-var="superIt=#it#" dr-option-strip="true">
|
|
176
|
-
#it# = ${$var.superIt * #it#}$
|
|
177
|
-
</scope>
|
|
178
|
-
</div>
|
|
179
|
-
```
|
|
180
|
-
</details>
|
|
181
|
-
|
|
182
|
-
<details>
|
|
183
|
-
<summary>dr-appender<strong>🔻(click)</strong></summary>
|
|
184
|
-
|
|
185
|
-
```html
|
|
186
|
-
<h3>appender</h3>
|
|
187
|
-
<ul>
|
|
188
|
-
<li dr-appender="@this@.appender">
|
|
189
|
-
${#it#}$
|
|
190
|
-
</li>
|
|
191
|
-
</ul>
|
|
192
|
-
<button dr-event-click="@this@.append()">appending</button>
|
|
193
|
-
<button dr-event-click="@this@.modifyAppender(0)">idx 0 modify</button>
|
|
194
|
-
<button dr-event-click="@this@.clearAppend()">appender clear</button>
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
class Data {
|
|
199
|
-
appender = new Appender();
|
|
200
|
-
|
|
201
|
-
constructor() {
|
|
202
|
-
this.appender.push('init' + RandomUtils.uuid(), 'init' + RandomUtils.uuid());
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
append() {
|
|
206
|
-
this.appender.push(RandomUtils.uuid(), RandomUtils.uuid());
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
clearAppend() {
|
|
210
|
-
this.appender.clear()
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
modifyAppender(idx: number) {
|
|
214
|
-
this.appender[idx][0] = RandomUtils.uuid();
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
```
|
|
219
|
-
</details>
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
### dr-strip
|
|
223
|
-
```html
|
|
224
|
-
<div dr-strip="true">aa</div>
|
|
225
|
-
```
|
|
226
|
-
only "aa" result strip element
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
<details>
|
|
231
|
-
<summary>dr-repeat<strong>🔻(click)</strong></summary>
|
|
232
|
-
|
|
233
|
-
```html
|
|
234
|
-
<div dr-repeat="10"><div>#it#</div></div>
|
|
235
|
-
<div dr-repeat="$range(10, 20)"><div>#it#</div></div>
|
|
236
|
-
<div dr-repeat="$range(10, 20, 5)"><div>#it#</div></div>
|
|
237
|
-
<div dr-repeat="$range('10..5, 2')"><div>#it#</div></div>
|
|
238
|
-
```
|
|
239
|
-
</details>
|
|
240
|
-
|
|
241
|
-
<details>
|
|
242
|
-
<summary>dr-inner-text, dr-inner-html<strong>🔻(click)</strong></summary>
|
|
243
|
-
|
|
244
|
-
```html
|
|
245
|
-
<div dr-inner-text="'<b>aa</b> <button dr-event-click=\'alert(1)\'>aa</button>'"> friend</div>
|
|
246
|
-
<div dr-inner-html="'<b>aa</b> <button dr-event-click=\'alert(1)\'>aa</button>'"> friend</div>
|
|
247
|
-
```
|
|
248
|
-
</details>
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
## event
|
|
252
|
-
<details>
|
|
253
|
-
<summary>dr-event-(name)<strong>🔻(click)</strong></summary>
|
|
254
|
-
|
|
255
|
-
* click, mousedown, mouseup, dblclick, mouseover, mouseout, mousemove, mouseenter, mouseleave, contextmenu, keyup, keydown, keypress, change, input, submit, resize, focus, blur
|
|
256
|
-
* ref: element
|
|
257
|
-
* variable: $event, $target
|
|
258
|
-
```html
|
|
259
|
-
click: <button dr-event-click="@this@.name = 'name' + new Date()">click</button> <br>
|
|
260
|
-
change: <input type="text" dr-event-change="@this@.name = $target.value"> <br>
|
|
261
|
-
input: <input type="text" dr-event-input="@this@.name = $target.value"> <br>
|
|
262
|
-
keyup: <input type="text" dr-event-keyup="@this@.name = $target.value"> <br>
|
|
263
|
-
...
|
|
264
|
-
keydown: <input type="text" dr-event-keydown="@this@.name = $target.value"><br>
|
|
265
|
-
submit: <form dr-event-submit="console.log($event); $event.preventDefault();"><input type="text"> <button type="submit">submit</button></form><br>
|
|
266
|
-
```
|
|
267
|
-
</details>
|
|
268
|
-
|
|
269
|
-
<details>
|
|
270
|
-
<summary>dr-window-event-popstate<strong>🔻(click)</strong></summary>
|
|
271
|
-
|
|
272
|
-
* ref: window
|
|
273
|
-
* variable: $target
|
|
274
|
-
|
|
275
|
-
```html
|
|
276
|
-
window-event-popstate: <input type="text" dr-window-event-popstate="alert(@this@.name)"><br>
|
|
277
|
-
```
|
|
278
|
-
</details>
|
|
279
|
-
|
|
280
|
-
<details>
|
|
281
|
-
<summary>dr-event<strong>🔻(click)</strong></summary>
|
|
282
|
-
|
|
283
|
-
* other event
|
|
284
|
-
* ref: element
|
|
285
|
-
* variable: $params, $event
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
```html
|
|
289
|
-
<input dr-event:bind='eventName1, eventName2' dr-event="console.log('event', $params, $event)" type="text">
|
|
290
|
-
```
|
|
291
|
-
</details>
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
## this
|
|
295
|
-
<details>
|
|
296
|
-
<summary>dr-this<strong>🔻(click)</strong></summary>
|
|
297
|
-
* dr-this
|
|
298
|
-
* object mapping
|
|
299
|
-
|
|
300
|
-
```typescript
|
|
301
|
-
class data {
|
|
302
|
-
dictionary = {
|
|
303
|
-
name: 'visualkhh'
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
```html
|
|
308
|
-
<div dr-this="@this@.dictionary">
|
|
309
|
-
${@this@.name}$
|
|
310
|
-
</div>
|
|
311
|
-
```
|
|
312
|
-
</details>
|
|
313
|
-
|
|
314
|
-
<details>
|
|
315
|
-
<summary>dr-this-property<strong>🔻(click)</strong></summary>
|
|
316
|
-
* dr-property
|
|
317
|
-
* property forEach in object
|
|
318
|
-
* dynamic add, delete
|
|
319
|
-
|
|
320
|
-
```typescript
|
|
321
|
-
class data {
|
|
322
|
-
dictionary = {
|
|
323
|
-
name1: 'visualkhh1',
|
|
324
|
-
name2: 'visualkhh2',
|
|
325
|
-
name3: 'visualkhh3'
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
```
|
|
329
|
-
```html
|
|
330
|
-
<home dr-this-property="@this@.dictionary" dr-on-init:arguments="[2,#this#]">
|
|
331
|
-
${this}$
|
|
332
|
-
</home>
|
|
333
|
-
```
|
|
334
|
-
</details>
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
## Component Property binding with `@attribute` decorator
|
|
338
|
-
<details>
|
|
339
|
-
<summary>@attribute<strong>🔻(click)</strong></summary>
|
|
340
|
-
|
|
341
|
-
You can bind component properties directly to HTML attributes using the `@attribute` decorator. When the attribute on the custom element changes, the corresponding property in the component class instance is automatically updated.
|
|
342
|
-
|
|
343
|
-
This requires your component to extend `ComponentBase`.
|
|
344
|
-
|
|
345
|
-
### Usage
|
|
346
|
-
|
|
347
|
-
The `@attribute` decorator options:
|
|
348
|
-
|
|
349
|
-
* `name: string`: Binds the property to a specifically named attribute. If not provided, the property name is used.
|
|
350
|
-
* `converter: (value: string | null) => any`: A function to convert the string attribute value to another type.
|
|
351
|
-
|
|
352
|
-
### Example
|
|
353
|
-
|
|
354
|
-
**Component (`MyComponent.ts`)**
|
|
355
|
-
```typescript
|
|
356
|
-
import { ComponentBase, attribute } from '@dooboostore/dom-render';
|
|
357
|
-
|
|
358
|
-
// Assuming MyComponent is registered with the tag 'my-component'
|
|
359
|
-
export class MyComponent extends ComponentBase<{ 'title': string, 'show-icon': string, 'created-at': string }> {
|
|
360
|
-
@attribute
|
|
361
|
-
title: string = 'Default Title';
|
|
362
|
-
|
|
363
|
-
@attribute({
|
|
364
|
-
name: 'show-icon',
|
|
365
|
-
converter: (val) => val === 'true'
|
|
366
|
-
})
|
|
367
|
-
showIcon: boolean; // The converter handles string -> boolean
|
|
368
|
-
|
|
369
|
-
@attribute({
|
|
370
|
-
name: 'created-at',
|
|
371
|
-
converter: (val) => val ? new Date(val) : null
|
|
372
|
-
})
|
|
373
|
-
createdAt: Date | null;
|
|
374
|
-
}
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
**Template (`index.html`)**
|
|
378
|
-
```html
|
|
379
|
-
<my-component
|
|
380
|
-
title="Hello World!"
|
|
381
|
-
show-icon="true"
|
|
382
|
-
created-at="2025-08-30T12:00:00Z">
|
|
383
|
-
</my-component>
|
|
384
|
-
|
|
385
|
-
<script>
|
|
386
|
-
// After some time, change the attribute
|
|
387
|
-
setTimeout(() => {
|
|
388
|
-
const comp = document.querySelector('my-component');
|
|
389
|
-
comp.setAttribute('title', 'New Title!');
|
|
390
|
-
comp.setAttribute('show-icon', 'false');
|
|
391
|
-
comp.setAttribute('created-at', new Date().toISOString());
|
|
392
|
-
}, 2000);
|
|
393
|
-
</script>
|
|
394
|
-
```
|
|
395
|
-
When attributes change, the `converter` function will be executed before the value is assigned to the property. For `showIcon`, the string `"true"` or `"false"` will be converted to a boolean. For `createdAt`, the date string will be converted to a `Date` object.
|
|
396
|
-
|
|
397
|
-
### Binding to Methods
|
|
398
|
-
|
|
399
|
-
The `@attribute` decorator can also be applied to methods. When the corresponding HTML attribute changes, the decorated method will be invoked with the new attribute value as its argument. This allows you to execute custom logic directly in response to attribute changes.
|
|
400
|
-
|
|
401
|
-
**Component (`MyComponent.ts`) - Method Example**
|
|
402
|
-
```typescript
|
|
403
|
-
import { ComponentBase, attribute } from '@dooboostore/dom-render';
|
|
404
|
-
|
|
405
|
-
export class MyComponent extends ComponentBase<{ 'data-value': string }> {
|
|
406
|
-
public receivedData: string = '';
|
|
407
|
-
|
|
408
|
-
@attribute({ name: 'data-value' })
|
|
409
|
-
onDataValueChange(newValue: string): void {
|
|
410
|
-
this.receivedData = `Method received: ${newValue}`;
|
|
411
|
-
console.log(this.receivedData);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
**Template (`index.html`) - Method Example**
|
|
417
|
-
```html
|
|
418
|
-
<my-component data-value="initial"></my-component>
|
|
419
|
-
|
|
420
|
-
<p>${@this@.receivedData}$</p>
|
|
421
|
-
|
|
422
|
-
<script>
|
|
423
|
-
setTimeout(() => {
|
|
424
|
-
const comp = document.querySelector('my-component');
|
|
425
|
-
comp.setAttribute('data-value', 'updated value');
|
|
426
|
-
}, 1000);
|
|
427
|
-
</script>
|
|
428
|
-
```
|
|
429
|
-
When `data-value` attribute changes, the `onDataValueChange` method will be called with the new value.
|
|
430
|
-
|
|
431
|
-
</details>
|
|
432
|
-
|
|
433
|
-
## Element Query with `@query` decorator
|
|
434
|
-
<details>
|
|
435
|
-
<summary>@query<strong>🔻(click)</strong></summary>
|
|
436
|
-
|
|
437
|
-
You can get a reference to DOM elements within your component's template using the `@query` decorator. It takes a CSS selector as an argument.
|
|
438
|
-
|
|
439
|
-
- If the decorated property is **not an array type**, it returns the **first** matching element (like `querySelector`).
|
|
440
|
-
- If the decorated property **is an array type** (e.g., `HTMLElement[]`), it returns **all** matching elements as an array (like `querySelectorAll`).
|
|
441
|
-
- When applied to a **method**, the method will be called with the queried element(s) as arguments after the component has been rendered. If the method expects a single element, the first match is passed. If it expects an array, all matches are passed.
|
|
442
|
-
|
|
443
|
-
This is useful for directly accessing and manipulating elements you need to interact with from your component's logic.
|
|
444
|
-
|
|
445
|
-
**Note:** This feature relies on TypeScript's decorator metadata (`emitDecoratorMetadata: true` in `tsconfig.json`).
|
|
446
|
-
|
|
447
|
-
### Usage Example
|
|
448
|
-
|
|
449
|
-
```typescript
|
|
450
|
-
import { ComponentBase, query } from '@dooboostore/dom-render';
|
|
451
|
-
|
|
452
|
-
// Assuming MyComponent is rendered with the template below
|
|
453
|
-
export class MyComponent extends ComponentBase<{}> {
|
|
454
|
-
// Gets the first element matching the selector
|
|
455
|
-
@query('h3.title')
|
|
456
|
-
private titleEl: HTMLHeadingElement;
|
|
457
|
-
|
|
458
|
-
// Gets all elements matching the selector as an array
|
|
459
|
-
@query('ul.item-list > li')
|
|
460
|
-
private items: HTMLLIElement[];
|
|
461
|
-
|
|
462
|
-
onInitRender() {
|
|
463
|
-
// The elements are available after the component has been rendered.
|
|
464
|
-
if (this.titleEl) {
|
|
465
|
-
this.titleEl.style.color = 'blue';
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if (this.items && this.items.length > 0) {
|
|
469
|
-
this.items.forEach((item, index) => {
|
|
470
|
-
item.textContent = `Item ${index + 1} (updated)`;
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// Method query: This method will be called with the first element matching '.my-button'
|
|
476
|
-
@query('.my-button')
|
|
477
|
-
private setupButton(button: HTMLButtonElement) {
|
|
478
|
-
if (button) {
|
|
479
|
-
button.addEventListener('click', () => alert('Button clicked from method query!'));
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// Method query: This method will be called with an array of all elements matching 'span.item'
|
|
484
|
-
@query('span.item')
|
|
485
|
-
private processItems(spans: HTMLSpanElement[]) {
|
|
486
|
-
spans.forEach(span => {
|
|
487
|
-
span.style.fontWeight = 'bold';
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
### Template
|
|
494
|
-
```html
|
|
495
|
-
<!-- Template for MyComponent -->
|
|
496
|
-
<div>
|
|
497
|
-
<h3 class="title">Original Title</h3>
|
|
498
|
-
<ul class="item-list">
|
|
499
|
-
<li>Item 1</li>
|
|
500
|
-
<li>Item 2</li>
|
|
501
|
-
<li>Item 3</li>
|
|
502
|
-
</ul>
|
|
503
|
-
<button class="my-button">Click Me</button>
|
|
504
|
-
<p><span class="item">First Span</span> <span class="item">Second Span</span></p>
|
|
505
|
-
</div>
|
|
506
|
-
```
|
|
507
|
-
The `@query` decorator will assign the corresponding element(s) to the decorated property or pass them to the decorated method. The property will be `null`/`undefined` (for single query) or an empty array (for multiple query) if no elements are found or before the component is rendered. For methods, they will only be called if matching elements are found.
|
|
508
|
-
|
|
509
|
-
</details>
|
|
510
|
-
|
|
511
|
-
</details>
|
|
512
|
-
|
|
513
|
-
## DOM Event Binding with `@event` decorator
|
|
514
|
-
<details>
|
|
515
|
-
<summary>@event<strong>🔻(click)</strong></summary>
|
|
516
|
-
|
|
517
|
-
You can bind component methods directly to DOM events on elements within your component's template using the `@event` decorator.
|
|
518
|
-
|
|
519
|
-
The decorator takes an options object with two properties:
|
|
520
|
-
- `query: string`: A CSS selector to find the target element(s).
|
|
521
|
-
- `name: string`: The name of the DOM event to listen for (e.g., `'click'`, `'input'`).
|
|
522
|
-
|
|
523
|
-
The framework will automatically add the event listener when the component is initialized and remove it when the component is destroyed, preventing memory leaks.
|
|
524
|
-
|
|
525
|
-
### Usage Example
|
|
526
|
-
|
|
527
|
-
```typescript
|
|
528
|
-
import { ComponentBase, event } from '@dooboostore/dom-render';
|
|
529
|
-
|
|
530
|
-
// Assuming MyComponent is rendered with the template below
|
|
531
|
-
export class MyComponent extends ComponentBase<{}> {
|
|
532
|
-
|
|
533
|
-
// This method will be called when any element matching 'button.action' is clicked.
|
|
534
|
-
@event({ query: 'button.action', name: 'click' })
|
|
535
|
-
onActionClick(e: MouseEvent) {
|
|
536
|
-
console.log('Action button clicked!', e.target);
|
|
537
|
-
alert('Action!');
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// This method will be called for the 'input' event on all text inputs.
|
|
541
|
-
@event({ query: 'input[type="text"]', name: 'input' })
|
|
542
|
-
onTextInput(e: Event) {
|
|
543
|
-
const target = e.target as HTMLInputElement;
|
|
544
|
-
console.log(`Text changed in ${target.id}: ${target.value}`);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
### Template
|
|
550
|
-
```html
|
|
551
|
-
<!-- Template for MyComponent -->
|
|
552
|
-
<div>
|
|
553
|
-
<input type="text" id="firstname" placeholder="First Name">
|
|
554
|
-
<input type="text" id="lastname" placeholder="Last Name">
|
|
555
|
-
<button class="action">Submit</button>
|
|
556
|
-
</div>
|
|
557
|
-
```
|
|
558
|
-
In this example, when the "Submit" button is clicked, the `onActionClick` method is executed. When the user types in either of the text inputs, the `onTextInput` method is executed for each keystroke.
|
|
559
|
-
|
|
560
|
-
</details>
|
|
561
|
-
|
|
562
|
-
## nearThis
|
|
563
|
-
```html
|
|
564
|
-
<div dr-this="@this@.dictionary"> (${@nearThis@.name}$) ${$nearThis.name}$ </div>
|
|
565
|
-
```
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
## dr-on-init
|
|
569
|
-
- element on-init event
|
|
570
|
-
```html
|
|
571
|
-
<canvas id="gameCanvas" dr-on-init="console.log('--@@@@@@@---', $element)"></canvas>sssss
|
|
572
|
-
```
|
|
573
|
-
## value
|
|
574
|
-
<details>
|
|
575
|
-
<summary>dr-value, value-link<strong>🔻(click)</strong></summary>
|
|
576
|
-
|
|
577
|
-
* dr-value
|
|
578
|
-
* The value is assigned the first time.
|
|
579
|
-
* dr-value-link
|
|
580
|
-
* Value and variable values are referencing each other. It affects each other when changing. (Immediate reflection event: input)
|
|
581
|
-
* instance variable first
|
|
582
|
-
* dr-checked-link
|
|
583
|
-
* Value and variable values are referencing each other. It affects each other when changing. (Immediate reflection event: input)
|
|
584
|
-
* instance variable first
|
|
585
|
-
* dr-disabled-link
|
|
586
|
-
* Value and variable values are referencing each other. It affects each other when changing. (Immediate reflection event: input)
|
|
587
|
-
* instance variable first
|
|
588
|
-
|
|
589
|
-
```html
|
|
590
|
-
dr-value: <input type="text" dr-value="@this@.office.name"> <br>
|
|
591
|
-
dr-value-link: <input type="text" dr-value-link="@this@.office.addr.street"> <br>
|
|
592
|
-
```
|
|
593
|
-
</details>
|
|
594
|
-
|
|
595
|
-
## Element State Attributes
|
|
596
|
-
<details>
|
|
597
|
-
<summary>dr-checked, dr-selected, dr-readonly, dr-disabled<strong>🔻(click)</strong></summary>
|
|
598
|
-
|
|
599
|
-
These attributes allow you to bind boolean values directly to the `checked`, `selected`, `readonly`, and `disabled` properties of HTML elements.
|
|
600
|
-
|
|
601
|
-
* `dr-checked`: Binds the `checked` attribute of an input (checkbox/radio) to a boolean value.
|
|
602
|
-
* `dr-selected`: Binds the `selected` attribute of an option to a boolean value.
|
|
603
|
-
* `dr-readonly`: Binds the `readonly` attribute of an input to a boolean value.
|
|
604
|
-
* `dr-disabled`: Binds the `disabled` attribute of an input/button/etc. to a boolean value.
|
|
605
|
-
|
|
606
|
-
### Example
|
|
607
|
-
|
|
608
|
-
```html
|
|
609
|
-
<!-- Example for dr-checked -->
|
|
610
|
-
<input type="checkbox" dr-checked="@this@.isChecked">
|
|
611
|
-
<p>Checkbox is: ${@this@.isChecked}$</p>
|
|
612
|
-
|
|
613
|
-
<!-- Example for dr-selected -->
|
|
614
|
-
<select>
|
|
615
|
-
<option value="apple" dr-selected="@this@.isAppleSelected">Apple</option>
|
|
616
|
-
<option value="banana" dr-selected="@this@.isBananaSelected">Banana</option>
|
|
617
|
-
</select>
|
|
618
|
-
|
|
619
|
-
<!-- Example for dr-readonly -->
|
|
620
|
-
<input type="text" dr-readonly="@this@.isReadOnly" value="Some text">
|
|
621
|
-
|
|
622
|
-
<!-- Example for dr-disabled -->
|
|
623
|
-
<button dr-disabled="@this@.isDisabled">Click Me</button>
|
|
624
|
-
<input type="text" dr-disabled="@this@.isDisabled" value="Disabled input">
|
|
625
|
-
```
|
|
626
|
-
```typescript
|
|
627
|
-
class Data {
|
|
628
|
-
isChecked = true;
|
|
629
|
-
isAppleSelected = false;
|
|
630
|
-
isBananaSelected = true;
|
|
631
|
-
isReadOnly = false;
|
|
632
|
-
isDisabled = true;
|
|
633
|
-
}
|
|
634
|
-
// ... DomRender.run(new Data(), ...)
|
|
635
|
-
```
|
|
636
|
-
</details>
|
|
637
|
-
|
|
638
|
-
## dr-on-rendered-init
|
|
639
|
-
- rendered call
|
|
640
|
-
|
|
641
|
-
## other
|
|
642
|
-
<details>
|
|
643
|
-
<summary>dr-attr<strong>🔻(click)</strong></summary>
|
|
644
|
-
|
|
645
|
-
```html
|
|
646
|
-
<textarea dr-attr="{rows: @this@.age/2, cols: @this@.age}"></textarea>
|
|
647
|
-
<div dr-attr="{wow: '123', good: 123444}"></div>
|
|
648
|
-
<div dr-attr="['wow=123', 'good=123444']"></div>
|
|
649
|
-
<div dr-attr="'wow=123, good=123444'"></div>
|
|
650
|
-
```
|
|
651
|
-
</details>
|
|
652
|
-
|
|
653
|
-
<details>
|
|
654
|
-
<summary>dr-class<strong>🔻(click)</strong></summary>
|
|
655
|
-
|
|
656
|
-
```html
|
|
657
|
-
<div dr-class="{big: @this@.age > 50, red: @this@.age > 50}"></div>
|
|
658
|
-
<div dr-class="'big yellow ' + (@this@.age > 50 ? 'old' : 'young')"></div>
|
|
659
|
-
<div dr-class="['small', 'yellow']"></div>
|
|
660
|
-
```
|
|
661
|
-
</details>
|
|
662
|
-
|
|
663
|
-
<details>
|
|
664
|
-
<summary>dr-style<strong>🔻(click)</strong></summary>
|
|
665
|
-
|
|
666
|
-
```html
|
|
667
|
-
<div dr-style="{'background-image': 'url(\' '+@this@.my?.image+'\')'}" >aa</div>
|
|
668
|
-
<div dr-style="{fontSize: @this@.age + 'px'}"> style </div>
|
|
669
|
-
<div dr-style="{'font-size': '20px'}"> style</div>
|
|
670
|
-
<div dr-style="'font-size: ' + @this@.age +'px; margin: ' + @this@.age + 'px'"> style </div>
|
|
671
|
-
<div dr-style="['font-size: ' + @this@.age +'px', 'margin: ' + @this@.age + 'px']"> style </div>
|
|
672
|
-
```
|
|
673
|
-
</details>
|
|
674
|
-
|
|
675
|
-
<details>
|
|
676
|
-
<summary>dr-strip<strong>🔻(click)</strong></summary>
|
|
677
|
-
|
|
678
|
-
```html
|
|
679
|
-
<div dr-strip="true"><span>hello</span></div> <!-- output html : <span>hello</span> -->
|
|
680
|
-
```
|
|
681
|
-
</details>
|
|
682
|
-
|
|
683
|
-
<details>
|
|
684
|
-
<summary>dr-before, dr-after<strong>🔻(click)</strong></summary>
|
|
685
|
-
|
|
686
|
-
```html
|
|
687
|
-
<div dr-before="console.log('process before')" dr-after="console.log('process after')"></div>
|
|
688
|
-
```
|
|
689
|
-
</details>
|
|
690
|
-
|
|
691
|
-
<details>
|
|
692
|
-
<summary>dr-complete<strong>🔻(click)</strong></summary>
|
|
693
|
-
|
|
694
|
-
```html
|
|
695
|
-
<select dr-value-link="@this@.currentContry" dr-event-change="@this@.contryChange($event)">
|
|
696
|
-
<option dr-for-of="@this@.languages" dr-value="#it#.key" dr-option-complete="@this@.currentContry='defaultValue'">${#it#.title}$</option>
|
|
697
|
-
</select>
|
|
698
|
-
|
|
699
|
-
<select id="object-type" name="type" class="select-input" dr-value-link="@this@.selectedObjetActionType">
|
|
700
|
-
<option dr-for-of="Object.values(@this@.objetActionType)" dr-detect-option-attr="{value: #it#, selected: @this@.selectedObjetActionType === #it# ? 'selected' : null}" >${@this@.objetActionMetaData[#it#].enName}$</option>
|
|
701
|
-
</select>
|
|
702
|
-
```
|
|
703
|
-
#nearForOfIndex#
|
|
704
|
-
```html
|
|
705
|
-
<select dr-value-link="@this@.selectedStationIndex" dr-event-change="@this@.onChangeStation($event)">
|
|
706
|
-
<option dr-for-of="@this@.stations" dr-value="#nearForOfIndex#">
|
|
707
|
-
${#it#.name}$
|
|
708
|
-
</option>
|
|
709
|
-
</select>
|
|
710
|
-
```
|
|
711
|
-
|
|
712
|
-
#it#
|
|
713
|
-
```html
|
|
714
|
-
<input type="text" list="search-details-body-input-datalist" dr-on-init="@this@.onInitSearchElement($element);"> <button class="fa-solid fa-magnifying-glass"></button>
|
|
715
|
-
<datalist id="search-details-body-input-datalist">
|
|
716
|
-
<option dr-for-of="@this@.searchUsers" dr-value="#it#.name">
|
|
717
|
-
</datalist>
|
|
718
|
-
|
|
719
|
-
<div id="search-details-body-result-container">
|
|
720
|
-
<ul>
|
|
721
|
-
<li dr-for-of="@this@.searchUsers">
|
|
722
|
-
${#it#.name}$
|
|
723
|
-
</li>
|
|
724
|
-
</ul>
|
|
725
|
-
</div>
|
|
726
|
-
```
|
|
727
|
-
</details>
|
|
728
|
-
|
|
729
|
-
## dr-form
|
|
730
|
-
<details>
|
|
731
|
-
<summary>form<strong>🔻(click)</strong></summary>
|
|
732
|
-
* event: change
|
|
733
|
-
* modify change: dr-form:event="input"
|
|
734
|
-
|
|
735
|
-
- [examples](./examples/forms)
|
|
736
|
-
|
|
737
|
-
```html
|
|
738
|
-
<body id="app">
|
|
739
|
-
<form dr-form="@this@.form" dr-event-submit="@this@.submit(); $event.preventDefault();">
|
|
740
|
-
name: <input name="name">
|
|
741
|
-
age: <input name="age">
|
|
742
|
-
<button type="submit">submit</button>
|
|
743
|
-
</form>
|
|
744
|
-
</body>
|
|
745
|
-
```
|
|
746
|
-
```typescript
|
|
747
|
-
import { DomRender } from '@dooboostore/dom-render';
|
|
748
|
-
|
|
749
|
-
class FormData {
|
|
750
|
-
form: any = {};
|
|
751
|
-
|
|
752
|
-
submit() {
|
|
753
|
-
console.log(this.form);
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
|
|
757
|
-
const data = new FormData();
|
|
758
|
-
DomRender.run(data, document.querySelector('#app')!);
|
|
759
|
-
```
|
|
760
|
-
<!-- 💥 submit call -->
|
|
761
|
-
<!-- console: {name: 'name data', age: 'age data'} -->
|
|
762
|
-
</details>
|
|
763
|
-
|
|
764
|
-
<details>
|
|
765
|
-
<summary>validator<strong>🔻(click)</strong></summary>
|
|
766
|
-
|
|
767
|
-
```html
|
|
768
|
-
<body id="app">
|
|
769
|
-
<form dr-form="@this@.form" dr-event-submit="@this@.submit(); $event.preventDefault();">
|
|
770
|
-
name: <input name="name">
|
|
771
|
-
age: <input name="age">
|
|
772
|
-
<button type="submit">submit</button>
|
|
773
|
-
</form>
|
|
774
|
-
</body>
|
|
775
|
-
```
|
|
776
|
-
```typescript
|
|
777
|
-
import { DomRender } from '@dooboostore/dom-render';
|
|
778
|
-
import { FormValidator, NotEmptyValidator } from '@dooboostore/dom-render/validator';
|
|
779
|
-
|
|
780
|
-
class FormDataWithValidator {
|
|
781
|
-
form: FormValidator = new FormValidator();
|
|
782
|
-
|
|
783
|
-
constructor() {
|
|
784
|
-
this.form.name = new NotEmptyValidator();
|
|
785
|
-
this.form.age = new NotEmptyValidator();
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
submit() {
|
|
789
|
-
if (this.form.valid()){
|
|
790
|
-
console.log('valid');
|
|
791
|
-
} else {
|
|
792
|
-
console.log('inValid');
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
const data = new FormDataWithValidator();
|
|
798
|
-
DomRender.run(data, document.querySelector('#app')!);
|
|
799
|
-
```
|
|
800
|
-
validator
|
|
801
|
-
- Validator (abstract)
|
|
802
|
-
- ValidatorArray (abstract)
|
|
803
|
-
- AllCheckedValidatorArray
|
|
804
|
-
- AllUnCheckedValidatorArray
|
|
805
|
-
- CheckedValidator
|
|
806
|
-
- CountEqualsCheckedValidatorArray
|
|
807
|
-
- CountEqualsUnCheckedValidatorArray
|
|
808
|
-
- CountGreaterThanCheckedValidatorArray
|
|
809
|
-
- CountGreaterThanEqualsCheckedValidatorArray
|
|
810
|
-
- CountGreaterThanEqualsUnCheckedValidatorArray
|
|
811
|
-
- CountGreaterThanUnCheckedValidatorArray
|
|
812
|
-
- CountLessThanCheckedValidatorArray
|
|
813
|
-
- CountLessThanEqualsCheckedValidatorArray
|
|
814
|
-
- CountLessThanEqualsUnCheckedValidatorArray
|
|
815
|
-
- CountLessThanUnCheckedValidatorArray
|
|
816
|
-
- CountUnCheckedValidatorArray
|
|
817
|
-
- EmptyValidator
|
|
818
|
-
- ExcludeCheckedValidatorArray
|
|
819
|
-
- FormValidator
|
|
820
|
-
- IncludeCheckedValidatorArray
|
|
821
|
-
- MultipleValidator
|
|
822
|
-
- NonPassValidator
|
|
823
|
-
- NotEmptyValidator
|
|
824
|
-
- NotRegExpTestValidator
|
|
825
|
-
- PassValidator
|
|
826
|
-
- RegExpTestValidator
|
|
827
|
-
- RequiredValidator
|
|
828
|
-
- UnCheckedValidator
|
|
829
|
-
- ValidMultipleValidator
|
|
830
|
-
- ValidValidator
|
|
831
|
-
- ValidValidatorArray
|
|
832
|
-
- ValueEqualsValidator
|
|
833
|
-
- ValueNotEqualsValidator
|
|
834
|
-
</details>
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
## Route
|
|
839
|
-
<details>
|
|
840
|
-
<summary><strong>Route (click)</strong></summary>
|
|
841
|
-
|
|
842
|
-
Declarative routing is supported using the `dr-route-match` and `dr-route-regexp` components. This allows you to conditionally render parts of your UI based on the current URL path.
|
|
843
|
-
|
|
844
|
-
- [Example](./examples/route)
|
|
845
|
-
|
|
846
|
-
### 1. Configuration
|
|
847
|
-
|
|
848
|
-
To enable routing, you must set the `routerType` in your `DomRender` configuration.
|
|
849
|
-
|
|
850
|
-
- `routerType`: Can be `'hash'`, `'path'`, or `'none'` (default).
|
|
851
|
-
|
|
852
|
-
```typescript
|
|
853
|
-
// In your main setup file (e.g., index.ts)
|
|
854
|
-
import { DomRender, DomRenderRunConfig } from '@dooboostore/dom-render';
|
|
855
|
-
|
|
856
|
-
// ... your root object class (e.g., Index)
|
|
857
|
-
|
|
858
|
-
const config: DomRenderRunConfig<Index> = {
|
|
859
|
-
window: window,
|
|
860
|
-
routerType: 'path' // or 'hash'
|
|
861
|
-
};
|
|
862
|
-
|
|
863
|
-
const domRender = new DomRender({
|
|
864
|
-
rootObject: new Index(),
|
|
865
|
-
target: document.querySelector('#app')!,
|
|
866
|
-
config: config
|
|
867
|
-
}, {autoGo: '/'}); // autoGo navigates to the initial path
|
|
868
|
-
```
|
|
869
|
-
|
|
870
|
-
### 2. Template Usage
|
|
871
|
-
|
|
872
|
-
Use the `<dr-route-match>` and `<dr-route-regexp>` components in your HTML template to define which content appears for which route.
|
|
873
|
-
|
|
874
|
-
- **`<dr-route-match>`**: Renders its content when the path matches the `value` attribute. It supports simple paths and path parameters (e.g., `/{id}`).
|
|
875
|
-
- **`<dr-route-regexp>`**: Renders its content when the path matches the regular expression in the `value` attribute.
|
|
876
|
-
|
|
877
|
-
```html
|
|
878
|
-
<!-- index.html -->
|
|
879
|
-
<header>
|
|
880
|
-
<!-- Navigation buttons -->
|
|
881
|
-
<button dr-event-click="$router?.go({path:'/'})">Main</button>
|
|
882
|
-
<button dr-event-click="$router?.go({path:'/second'})">Second</button>
|
|
883
|
-
<button dr-event-click="$router?.go({path:'/detail/42'})">Detail 42</button>
|
|
884
|
-
</header>
|
|
885
|
-
|
|
886
|
-
<main>
|
|
887
|
-
<!-- Content for the root path -->
|
|
888
|
-
<dr-route-match value="/">
|
|
889
|
-
<h1>Main Page</h1>
|
|
890
|
-
</dr-route-match>
|
|
891
|
-
|
|
892
|
-
<!-- Content for the /second path -->
|
|
893
|
-
<dr-route-match value="/second">
|
|
894
|
-
<h1>Second Page</h1>
|
|
895
|
-
</dr-route-match>
|
|
896
|
-
|
|
897
|
-
<!-- Content for a path with a numeric parameter -->
|
|
898
|
-
<dr-route-match value="/detail/{id:[0-9]+}">
|
|
899
|
-
<h1>Detail Page</h1>
|
|
900
|
-
<p>The router's pathData will contain the matched 'id'.</p>
|
|
901
|
-
</dr-route-match>
|
|
902
|
-
|
|
903
|
-
<!-- Content using a regular expression -->
|
|
904
|
-
<dr-route-regexp value="/second/[0-9]?$">
|
|
905
|
-
<p>This content appears for paths like /second/1, /second/2, etc.</p>
|
|
906
|
-
</dr-route-regexp>
|
|
907
|
-
</main>
|
|
908
|
-
```
|
|
909
|
-
|
|
910
|
-
The `$router` object is automatically available in your templates for navigation (`$router.go(...)`) and testing (`$router.test(...)`).
|
|
911
|
-
|
|
912
|
-
</details>
|
|
913
|
-
|
|
914
|
-
## Accessing the Root Object Proxy
|
|
915
|
-
|
|
916
|
-
After initializing `DomRender`, you can get a proxied reference to your `rootObject`. Any changes made to this proxy will be automatically detected and reflected in the DOM. This is useful for updating state from outside your component's methods, such as in `setTimeout` or in response to external events.
|
|
917
|
-
|
|
918
|
-
```typescript
|
|
919
|
-
import { DomRender } from '@dooboostore/dom-render';
|
|
920
|
-
|
|
921
|
-
class App {
|
|
922
|
-
message = 'Hello, World!';
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
// 1. Initialize DomRender
|
|
926
|
-
const domRender = new DomRender({
|
|
927
|
-
rootObject: new App(),
|
|
928
|
-
target: document.querySelector('#app')!
|
|
929
|
-
});
|
|
930
|
-
|
|
931
|
-
// 2. Get the proxy to the rootObject
|
|
932
|
-
const data = domRender.rootObject;
|
|
933
|
-
|
|
934
|
-
// 3. Now you can modify the state from anywhere
|
|
935
|
-
setTimeout(() => {
|
|
936
|
-
// This change will automatically update the UI
|
|
937
|
-
data.message = 'Hello, DomRender!';
|
|
938
|
-
}, 2000);
|
|
939
|
-
```
|
|
940
|
-
|
|
941
|
-
```html
|
|
942
|
-
<!-- index.html -->
|
|
943
|
-
<div id="app">
|
|
944
|
-
${@this@.message}$
|
|
945
|
-
</div>
|
|
946
|
-
```
|
|
947
|
-
|
|
948
|
-
## Core Components
|
|
949
|
-
This section describes the built-in components provided by `dom-render`.
|
|
950
|
-
|
|
951
|
-
<details>
|
|
952
|
-
<summary><strong>ComponentBase & Decorators (<code>@attribute</code>, <code>@query</code>, <code>@event</code>)</strong>🔻(click)</summary>
|
|
953
|
-
|
|
954
|
-
`ComponentBase` is the foundation for creating custom components that can interact with the `dom-render` lifecycle and attributes. When you extend `ComponentBase`, you gain access to powerful decorators for linking your class to the template.
|
|
955
|
-
|
|
956
|
-
- **`@attribute`**: Binds a class property to an HTML attribute. It can automatically convert values and even bind to methods.
|
|
957
|
-
- **`@query`**: Injects DOM elements from your component's template into class properties or method arguments.
|
|
958
|
-
- **`@event`**: Binds a class method to a DOM event on an element within the template, handling listener cleanup automatically.
|
|
959
|
-
|
|
960
|
-
*For detailed examples of these decorators, please see their dedicated sections above in the documentation.*
|
|
961
|
-
|
|
962
|
-
</details>
|
|
963
|
-
|
|
964
|
-
<details>
|
|
965
|
-
<summary><strong>ComponentSet</strong>🔻(click)</summary>
|
|
966
|
-
|
|
967
|
-
`ComponentSet` is a wrapper class that pairs a component instance with its own template and styles. It's the primary mechanism for dynamically rendering and swapping components within a `dr-this` block.
|
|
968
|
-
|
|
969
|
-
### Usage
|
|
970
|
-
```typescript
|
|
971
|
-
import { ComponentSet } from '@dooboostore/dom-render';
|
|
972
|
-
|
|
973
|
-
// Define two simple components
|
|
974
|
-
class ComponentA { message = 'This is Component A'; }
|
|
975
|
-
class ComponentB { message = 'Now you see Component B'; }
|
|
976
|
-
|
|
977
|
-
class App {
|
|
978
|
-
// Create ComponentSet instances for each component
|
|
979
|
-
componentA = new ComponentSet(new ComponentA(), { template: '<h1>${@this@.message}$</h1>' });
|
|
980
|
-
componentB = new ComponentSet(new ComponentB(), { template: '<h2>${@this@.message}$</h2>' });
|
|
981
|
-
|
|
982
|
-
// This property will hold the currently active component
|
|
983
|
-
activeComponent: ComponentSet;
|
|
984
|
-
|
|
985
|
-
constructor() {
|
|
986
|
-
this.activeComponent = this.componentA;
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
showA() { this.activeComponent = this.componentA; }
|
|
990
|
-
showB() { this.activeComponent = this.componentB; }
|
|
991
|
-
}
|
|
992
|
-
```
|
|
993
|
-
|
|
994
|
-
```html
|
|
995
|
-
<!-- This div will dynamically render the component held by 'activeComponent' -->
|
|
996
|
-
<div dr-this="@this@.activeComponent"></div>
|
|
997
|
-
|
|
998
|
-
<button dr-event-click="@this@.showA()">Show Component A</button>
|
|
999
|
-
<button dr-event-click="@this@.showB()">Show Component B</button>
|
|
1000
|
-
```
|
|
1001
|
-
</details>
|
|
1002
|
-
|
|
1003
|
-
<details>
|
|
1004
|
-
<summary><strong><code>dr-this</code></strong>🔻(click)</summary>
|
|
1005
|
-
|
|
1006
|
-
The `dr-this` component is the target for rendering a `ComponentSet`. It dynamically renders the component instance and template defined in the `ComponentSet` object it's bound to.
|
|
1007
|
-
|
|
1008
|
-
See the `ComponentSet` example above for a practical demonstration.
|
|
1009
|
-
|
|
1010
|
-
</details>
|
|
1011
|
-
|
|
1012
|
-
<details>
|
|
1013
|
-
<summary><strong><code>dr-choose</code> / <code>dr-choose-when</code> / <code>dr-choose-other-wise</code></strong>🔻(click)</summary>
|
|
1014
|
-
|
|
1015
|
-
Provides a "switch-case" control flow for rendering blocks of HTML. It evaluates a data object and renders the first `dr-choose-when` block whose `test` condition returns true. If no `when` blocks match, the `dr-choose-other-wise` block is rendered.
|
|
1016
|
-
|
|
1017
|
-
### Usage
|
|
1018
|
-
```typescript
|
|
1019
|
-
class App {
|
|
1020
|
-
status: 'loading' | 'success' | 'error' = 'loading';
|
|
1021
|
-
}
|
|
1022
|
-
```
|
|
1023
|
-
```html
|
|
1024
|
-
<dr-choose data="${@this@.status}$">
|
|
1025
|
-
<dr-choose-when test="(status) => status === 'loading'">
|
|
1026
|
-
<p>Loading...</p>
|
|
1027
|
-
</dr-choose-when>
|
|
1028
|
-
<dr-choose-when test="(status) => status === 'success'">
|
|
1029
|
-
<p>Data loaded successfully!</p>
|
|
1030
|
-
</dr-choose-when>
|
|
1031
|
-
<dr-choose-other-wise>
|
|
1032
|
-
<p>An unknown error occurred.</p>
|
|
1033
|
-
</dr-choose-other-wise>
|
|
1034
|
-
</dr-choose>
|
|
1035
|
-
```
|
|
1036
|
-
</details>
|
|
1037
|
-
|
|
1038
|
-
<details>
|
|
1039
|
-
<summary><strong><code>dr-promise-switch</code></strong>🔻(click)</summary>
|
|
1040
|
-
|
|
1041
|
-
Declaratively handles the different states of a JavaScript `Promise`. It provides child components to render content for `pending`, `fulfilled`, and `rejected` states.
|
|
1042
|
-
|
|
1043
|
-
### Child Components
|
|
1044
|
-
- `<dr-promise-switch-pending>`: Shown while the promise is in flight.
|
|
1045
|
-
- `<dr-promise-switch-fulfilled>`: Shown when the promise resolves successfully. The resolved data is available inside via `#component#.data`.
|
|
1046
|
-
- `<dr-promise-switch-rejected>`: Shown when the promise is rejected. The error is available inside via `#component#.data`.
|
|
1047
|
-
- `<dr-promise-switch-default>`: Shown before the promise is triggered.
|
|
1048
|
-
|
|
1049
|
-
### Usage
|
|
1050
|
-
```typescript
|
|
1051
|
-
class App {
|
|
1052
|
-
userPromise = null;
|
|
1053
|
-
|
|
1054
|
-
fetchUser() {
|
|
1055
|
-
this.userPromise = fetch('/api/user').then(res => res.json());
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
```
|
|
1059
|
-
```html
|
|
1060
|
-
<button dr-event-click="@this@.fetchUser()">Fetch User</button>
|
|
1061
|
-
|
|
1062
|
-
<dr-promise-switch data="${@this@.userPromise}$">
|
|
1063
|
-
<dr-promise-switch-pending>
|
|
1064
|
-
<p>Fetching user data...</p>
|
|
1065
|
-
</dr-promise-switch-pending>
|
|
1066
|
-
<dr-promise-switch-fulfilled>
|
|
1067
|
-
<p>Welcome, ${#component#.data.name}$!</p>
|
|
1068
|
-
</dr-promise-switch-fulfilled>
|
|
1069
|
-
<dr-promise-switch-rejected>
|
|
1070
|
-
<p>Failed to fetch user: ${#component#.data.message}$</p>
|
|
1071
|
-
</dr-promise-switch-rejected>
|
|
1072
|
-
</dr-promise-switch>
|
|
1073
|
-
```
|
|
1074
|
-
</details>
|
|
1075
|
-
|
|
1076
|
-
<details>
|
|
1077
|
-
<summary><strong><code>dr-checkbox</code></strong>🔻(click)</summary>
|
|
1078
|
-
|
|
1079
|
-
A wrapper for creating a custom, stylable checkbox. It uses child components to define the appearance for checked and unchecked states.
|
|
1080
|
-
|
|
1081
|
-
### Child Components
|
|
1082
|
-
- `<dr-checkbox-checked>`: Content to show when the box is checked.
|
|
1083
|
-
- `<dr-checkbox-unchecked>`: Content to show when the box is unchecked.
|
|
1084
|
-
|
|
1085
|
-
### Usage
|
|
1086
|
-
```typescript
|
|
1087
|
-
class App {
|
|
1088
|
-
agreed = false;
|
|
1089
|
-
}
|
|
1090
|
-
```
|
|
1091
|
-
```html
|
|
1092
|
-
<dr-checkbox name="terms" checked="${@this@.agreed}$" change="${(checked) => @this@.agreed = checked}$">
|
|
1093
|
-
<dr-checkbox-checked>
|
|
1094
|
-
<!-- Using FontAwesome icons as an example -->
|
|
1095
|
-
<i class="fas fa-check-square"></i> I agree to the terms.
|
|
1096
|
-
</dr-checkbox-checked>
|
|
1097
|
-
<dr-checkbox-unchecked>
|
|
1098
|
-
<i class="far fa-square"></i> I agree to the terms.
|
|
1099
|
-
</dr-checkbox-unchecked>
|
|
1100
|
-
</dr-checkbox>
|
|
1101
|
-
|
|
1102
|
-
<p>Agreed: ${@this@.agreed}$</p>
|
|
1103
|
-
```
|
|
1104
|
-
</details>
|
|
1105
|
-
|
|
1106
|
-
<details>
|
|
1107
|
-
<summary><strong><code>dr-details</code></strong>🔻(click)</summary>
|
|
1108
|
-
|
|
1109
|
-
An enhanced version of the native `<details>` element. It provides child components for the summary and body, and includes extra functionality like closing when the user clicks outside of it.
|
|
1110
|
-
|
|
1111
|
-
### Child Components
|
|
1112
|
-
- `<dr-details-summary>`: The clickable header of the details element.
|
|
1113
|
-
- `<dr-details-body>`: The collapsible content.
|
|
1114
|
-
- `<dr-details-form>`: A special body that is a form, with features like automatic reset on toggle.
|
|
1115
|
-
|
|
1116
|
-
### Usage
|
|
1117
|
-
```html
|
|
1118
|
-
<dr-details>
|
|
1119
|
-
<dr-details-summary>
|
|
1120
|
-
<h3>More Information</h3>
|
|
1121
|
-
</dr-details-summary>
|
|
1122
|
-
<dr-details-body>
|
|
1123
|
-
<p>Here is the hidden content that appears on click.</p>
|
|
1124
|
-
</dr-details-body>
|
|
1125
|
-
</dr-details>
|
|
1126
|
-
```
|
|
1127
|
-
</details>
|
|
1128
|
-
|
|
1129
|
-
<details>
|
|
1130
|
-
<summary><strong><code>dr-input</code></strong>🔻(click)</summary>
|
|
1131
|
-
|
|
1132
|
-
A custom input component that enhances a standard `<input>` with powerful features like debouncing and distinct value change detection. This is useful for performance-intensive tasks like live search.
|
|
1133
|
-
|
|
1134
|
-
### Attributes
|
|
1135
|
-
- `debounceTime`: (Optional) The time in milliseconds to wait after the user stops typing before firing the `input` event.
|
|
1136
|
-
- `distinct`: (Optional) If set to `true`, the `input` event will only fire if the new value is different from the previous one.
|
|
1137
|
-
- `input`: A callback function that receives the debounced/distinct value.
|
|
1138
|
-
|
|
1139
|
-
### Usage
|
|
1140
|
-
```typescript
|
|
1141
|
-
class App {
|
|
1142
|
-
searchQuery = '';
|
|
1143
|
-
|
|
1144
|
-
handleSearch(query: string) {
|
|
1145
|
-
console.log('Searching for:', query);
|
|
1146
|
-
this.searchQuery = query;
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
```
|
|
1150
|
-
```html
|
|
1151
|
-
<label>Search:</label>
|
|
1152
|
-
<dr-input
|
|
1153
|
-
type="text"
|
|
1154
|
-
class="search-input"
|
|
1155
|
-
debounceTime="300"
|
|
1156
|
-
distinct="true"
|
|
1157
|
-
input="${(value) => @this@.handleSearch(value)}$"
|
|
1158
|
-
></dr-input>
|
|
1159
|
-
|
|
1160
|
-
<p>Current query: ${@this@.searchQuery}$</p>
|
|
1161
|
-
```
|
|
1162
|
-
</details>
|
|
1163
|
-
|
|
1164
|
-
## Messenger (Data transmission)
|
|
1165
|
-
* publish, subscribe
|
|
1166
|
-
<details>
|
|
1167
|
-
<summary>publish<strong>🔻(click)</strong></summary>
|
|
1168
|
-
|
|
1169
|
-
```typescript
|
|
1170
|
-
export class Home implements OnProxyDomRender {
|
|
1171
|
-
private channel?: Channel;
|
|
1172
|
-
|
|
1173
|
-
sendIndexMessage() {
|
|
1174
|
-
const rtn = this.channel?.publish(Index, {
|
|
1175
|
-
name: this.name,
|
|
1176
|
-
age: this.age,
|
|
1177
|
-
title: this.title
|
|
1178
|
-
});
|
|
1179
|
-
console.log('sendIndexMessage return value: ', rtn);
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
onProxyDomRender({messenger}: Config): void {
|
|
1183
|
-
this.channel = messenger?.createChannel(Home);
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
```
|
|
1187
|
-
</details>
|
|
1188
|
-
<details>
|
|
1189
|
-
<summary>subscribe<strong>🔻(click)</strong></summary>
|
|
1190
|
-
|
|
1191
|
-
```typescript
|
|
1192
|
-
class Index implements OnProxyDomRender {
|
|
1193
|
-
onProxyDomRender({messenger}: Config): void {
|
|
1194
|
-
messenger?.createChannel(this).filter((data) => (data.age ?? 0) > 5).subscribe((data) => {
|
|
1195
|
-
this.rcvData = data;
|
|
1196
|
-
return {data: 'good', action: 'actionGood'}
|
|
1197
|
-
});
|
|
1198
|
-
// messenger?.createChannel(this).subscribe((data) => {
|
|
1199
|
-
// this.rcvData = data;
|
|
1200
|
-
// return {data: 'good', action: 'actionGood'}
|
|
1201
|
-
// });
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
````
|
|
1205
|
-
</details>
|
|
1206
|
-
|
|
1207
|
-
## Class
|
|
1208
|
-
<details>
|
|
1209
|
-
<summary>Range<strong>🔻(click)</strong></summary>
|
|
1210
|
-
|
|
1211
|
-
```javascript
|
|
1212
|
-
const range = new Range(100,55, 10);
|
|
1213
|
-
for (let data of new Range(100,55, 10)) {
|
|
1214
|
-
console.log(data);
|
|
1215
|
-
}
|
|
1216
|
-
const rangeArray = new Range(100,55, 10).toArray();
|
|
1217
|
-
```
|
|
1218
|
-
</details>
|
|
1219
|
-
<details>
|
|
1220
|
-
<summary>Appender<strong>🔻(click)</strong></summary>
|
|
1221
|
-
|
|
1222
|
-
```javascript
|
|
1223
|
-
const appender = new Appender<number>([1, 2]);
|
|
1224
|
-
appender.push(3, 4)
|
|
1225
|
-
for (const data of appender) {
|
|
1226
|
-
console.log('----appender item---', data);
|
|
1227
|
-
}
|
|
1228
|
-
```
|
|
1229
|
-
</details>
|
|
1230
|
-
|
|
1231
|
-
## Detect Get, Set
|
|
1232
|
-
<details>
|
|
1233
|
-
<summary>Method Proxy<strong>🔻(click)</strong></summary>
|
|
1234
|
-
|
|
1235
|
-
### using detect
|
|
1236
|
-
```typescript
|
|
1237
|
-
{
|
|
1238
|
-
name: 'dom-render'
|
|
1239
|
-
onBeforeReturnSet: (name: string, value: any, fullpath: string[]) => {
|
|
1240
|
-
console.log('set name--', name, value, fullpath);
|
|
1241
|
-
}
|
|
1242
|
-
onBeforeReturnGet: (name: string, value: any, fullpath: string[]) => {
|
|
1243
|
-
console.log('get name--', name, value, fullpath);
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
```
|
|
1247
|
-
exclude detect property: Config
|
|
1248
|
-
- proxyExcludeOnBeforeReturnGets: ['propertyName']
|
|
1249
|
-
- proxyExcludeOnBeforeReturnSets: ['propertyName']
|
|
1250
|
-
---
|
|
1251
|
-
|
|
1252
|
-
### OnBeforeReturnSet
|
|
1253
|
-
```typescript
|
|
1254
|
-
export interface OnBeforeReturnSet {
|
|
1255
|
-
onBeforeReturnSet(name: string, value: any, fullPath?: string[]): void;
|
|
1256
|
-
}
|
|
1257
|
-
```
|
|
1258
|
-
### OnBeforeReturnGet
|
|
1259
|
-
```typescript
|
|
1260
|
-
export interface OnBeforeReturnGet {
|
|
1261
|
-
onBeforeReturnGet(name: string, value: any, fullPath?: string[]): void;
|
|
1262
|
-
}
|
|
1263
|
-
```
|
|
1264
|
-
</details>
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
## Proxy
|
|
1269
|
-
all internal variables are managed by proxy. (DomRenderProxy)
|
|
1270
|
-
### exclude proxy (situation: Maximum call stack error)
|
|
1271
|
-
exclude detect property: Config
|
|
1272
|
-
- proxyExcludeTyps: [Class...]
|
|
1273
|
-
|
|
1274
|
-
Code base
|
|
1275
|
-
```typescript
|
|
1276
|
-
// frezz
|
|
1277
|
-
{name : Object.freeze({...})}
|
|
1278
|
-
|
|
1279
|
-
// Shield Object type: {[k: string]: any}
|
|
1280
|
-
{name : new Shield()}
|
|
1281
|
-
|
|
1282
|
-
// DomRenderProxy Final
|
|
1283
|
-
{name : DomRenderProxy.final({...})}
|
|
1284
|
-
```
|
|
1285
|
-
---
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
## LifeCycle
|
|
1289
|
-
* OnCreateRender
|
|
1290
|
-
* onCreateRender(): created call
|
|
1291
|
-
* OnInitRender
|
|
1292
|
-
* onInitRender(): init render call
|
|
1293
|
-
* OnDestroyRender
|
|
1294
|
-
* onDestroyRender(): component Destroy call
|
|
1295
|
-
|
|
1296
|
-
## Script
|
|
1297
|
-
```typescript
|
|
1298
|
-
new DomRender.run(obj, target, {
|
|
1299
|
-
scripts: {
|
|
1300
|
-
concat: function (head: string, tail: string) {
|
|
1301
|
-
return head + tail;
|
|
1302
|
-
}
|
|
1303
|
-
}
|
|
1304
|
-
});
|
|
1305
|
-
```
|
|
1306
|
-
using script
|
|
1307
|
-
```typescript
|
|
1308
|
-
const data = config.scripts.concat('head', 'tail')
|
|
1309
|
-
```
|
|
1310
|
-
```html
|
|
1311
|
-
<div>${$scripts.concat('head', 'tail')}</div>
|
|
1312
|
-
<div dr-if="$scripts.concat('wow', 'good') === 'wowgood'"> is wowgood</div>
|
|
1313
|
-
```
|
|
9
|
+
A powerful and reactive view template engine for fine-grained DOM control and rendering. All internal state is managed through a proxy system for automatic UI updates.
|
|
1314
10
|
|
|
1315
11
|
---
|
|
1316
|
-
|
|
1317
|
-
# 😃 examples
|
|
1318
|
-
- [examples](./examples/component_script_attr) lazy load (html, css)
|
|
1319
|
-
```html
|
|
1320
|
-
<body id="app">
|
|
1321
|
-
${@this@.name}$
|
|
1322
|
-
<h1>component</h1>
|
|
1323
|
-
<profile dr-on-create:callback="$component.name='jhone'; $component.age=55;"><b>${#component#.details}$</b></profile>
|
|
1324
|
-
<profile dr-on-create:callback="$component.name='cal'; $component.age=56;"><b>detail-2</b></profile>
|
|
1325
|
-
<profile dr-on-create:callback="$component.name='rose'; $component.age=57;">
|
|
1326
|
-
<profile dr-on-create:callback="$component.name='rose-sub'; $component.age=156;">
|
|
1327
|
-
<b>${@this@.name}$</b>
|
|
1328
|
-
</profile>
|
|
1329
|
-
</profile>
|
|
1330
|
-
<h3>component data link and detect</h3>
|
|
1331
|
-
<Profile dr-if="@this@.toggle" dr-detect="$component.age = @this@.age" dr-on-create:callback="$component.name='papa'; $component.age=58;">
|
|
1332
|
-
<b>${@this@.name}$</b>
|
|
1333
|
-
</Profile>
|
|
1334
|
-
<Profile dr-if="@this@.toggle" dr-detect="$component.age = @this@.age" dr-on-constructor:arguments="[1,2]">
|
|
1335
|
-
<b>${@this@.name}$</b>
|
|
1336
|
-
</Profile>
|
|
1337
|
-
|
|
1338
|
-
<button dr-event-click="@this@.name = new Date().toString();">change name</button>
|
|
1339
|
-
<button dr-event-click="@this@.age = Date.now();">change age</button>
|
|
1340
|
-
<button dr-event-click="@this@.toggle = !@this@.toggle;">change toggle</button>
|
|
12
|
+
## Features
|
|
1341
13
|
|
|
1342
|
-
|
|
1343
|
-
|
|
14
|
+
- **Reactive Templating**: Use simple expressions (`${...}$` and `#{...}#`) directly in your HTML to bind to your data.
|
|
15
|
+
- **Attribute & Style Binding**: Dynamically change attributes, classes, and styles with `dr-attr`, `dr-class`, and `dr-style`.
|
|
16
|
+
- **Control Flow**: Full control over rendering logic with directives like `dr-if`, `dr-for`, and `dr-choose`.
|
|
17
|
+
- **Component-Based Architecture**: Build encapsulated components with `ComponentBase` and use powerful decorators like `@attribute`, `@query`, and `@event` to connect your logic to the template.
|
|
18
|
+
- **Declarative Routing**: Simple and effective routing with `<dr-route-match>` to build single-page applications.
|
|
19
|
+
- **Effortless Form Handling**: Bind forms to your objects with `dr-form` and use a rich set of built-in validators.
|
|
20
|
+
- **Asynchronous Rendering**: Handle promises declaratively in your template with `<dr-promise-switch>`.
|
|
1344
21
|
|
|
22
|
+
## Installation
|
|
1345
23
|
|
|
24
|
+
```bash
|
|
25
|
+
# pnpm
|
|
26
|
+
pnpm add @dooboostore/dom-render
|
|
1346
27
|
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
${$scripts.concat('hello', 'tail')}$
|
|
1350
|
-
</div>
|
|
28
|
+
# npm
|
|
29
|
+
npm install @dooboostore/dom-render
|
|
1351
30
|
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
link attribute
|
|
1355
|
-
</button>
|
|
1356
|
-
<h1>attrCallBack</h1>
|
|
1357
|
-
<input id="callback" type="text" wow>
|
|
1358
|
-
</body>
|
|
31
|
+
# yarn
|
|
32
|
+
yarn add @dooboostore/dom-render
|
|
1359
33
|
```
|
|
1360
|
-
```typescript
|
|
1361
|
-
config.targetElements = [
|
|
1362
|
-
DomRender.createComponent({type: Profile, template: ProfileTemplate}), // lazy loading format 'lazy://component/home.html'
|
|
1363
|
-
DomRender.createComponent({type: Home, template: HomeTemplate, styles: HomeStyle})
|
|
1364
|
-
]
|
|
1365
34
|
|
|
1366
|
-
|
|
1367
|
-
DomRender.createAttribute('link',
|
|
1368
|
-
(element: Element, attrValue: string, obj: any, rawSet: RawSet) => {
|
|
1369
|
-
return obj;
|
|
1370
|
-
},
|
|
1371
|
-
(element: Element, attrValue: string, obj: any, rawSet: RawSet) => {
|
|
1372
|
-
const fag = window.document.createDocumentFragment();
|
|
1373
|
-
if (attrValue) {
|
|
1374
|
-
const n = element.cloneNode(true) as Element;
|
|
1375
|
-
attrValue = ScriptUtils.eval(`return ${attrValue}`, obj)
|
|
1376
|
-
n.addEventListener('click', () => {
|
|
1377
|
-
location.href = attrValue;
|
|
1378
|
-
});
|
|
1379
|
-
fag.append(n);
|
|
1380
|
-
}
|
|
1381
|
-
return fag;
|
|
1382
|
-
}
|
|
1383
|
-
)
|
|
1384
|
-
]
|
|
35
|
+
## Learn More
|
|
1385
36
|
|
|
1386
|
-
|
|
1387
|
-
{
|
|
1388
|
-
attrName: 'wow',
|
|
1389
|
-
callBack: (e, a, o) => {
|
|
1390
|
-
e.addEventListener('click', (event) => {
|
|
1391
|
-
alert((event.target as any).value);
|
|
1392
|
-
})
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
]
|
|
1396
|
-
const data = DomRender.run(new Data(), document.querySelector('#app')!, config);
|
|
1397
|
-
```
|
|
1398
|
-
using component
|
|
1399
|
-
```html
|
|
1400
|
-
<my-element dr-on-create:callback="$component.say();"></my-element>
|
|
37
|
+
The detailed API documentation, including all directives, components, and usage examples, is available on our documentation website.
|
|
1401
38
|
|
|
1402
|
-
|
|
1403
|
-
${#component#.homeName}$
|
|
1404
|
-
<home value="${#component#.homeName}$" wow="${#component#.homeColor}$" dr-component-name="sub_component" dr-component-inner-html-name="innerHTML">
|
|
1405
|
-
${#sub_component#.homeName}$
|
|
1406
|
-
</home>
|
|
1407
|
-
</home>
|
|
1408
|
-
```
|
|
1409
|
-
lazy loading rollup config
|
|
1410
|
-
```javascript
|
|
1411
|
-
copy({
|
|
1412
|
-
targets: [
|
|
1413
|
-
{
|
|
1414
|
-
src: ['**/*.html', '**/*.css', '!node_modules/**/*.html', '!node_modules/**/*.css'], dest: 'dist',
|
|
1415
|
-
rename: (name, extension, fullPath) => `${fullPath}`
|
|
1416
|
-
},
|
|
1417
|
-
{ src: 'assets', dest: 'dist' }
|
|
1418
|
-
]
|
|
1419
|
-
})
|
|
1420
|
-
```
|
|
39
|
+
**[Explore the full @dooboostore/dom-render documentation »](https://dooboostore-develop.github.io/packages/dom-render)**
|
|
1421
40
|
|
|
1422
|
-
##
|
|
1423
|
-
- dr-option-component-inner-html-name
|
|
1424
|
-
- dr-option-component-name
|
|
1425
|
-
|
|
1426
|
-
- callback
|
|
1427
|
-
- onChangeAttrRender
|
|
1428
|
-
- onCreateRenderData<T = any>(data: OnCreateRenderDataParams<T>): void
|
|
1429
|
-
- type OnCreateRenderDataParams<T = any> = { parent: T, render: Render };
|
|
1430
|
-
- OnCreateThisChild
|
|
1431
|
-
- thisChild created callback
|
|
1432
|
-
- OnChildRenderedByProperty
|
|
1433
|
-
- onChildRenderedByProperty(key: string, value: any, config: {rawSets:RawSet[]}): void;
|
|
1434
|
-
- event
|
|
1435
|
-
- dr-event-...:filter event filter!!
|
|
1436
|
-
- ex) dr-event-change:filter="false"
|
|
1437
|
-
- dr-event-...:dispatch
|
|
1438
|
-
- ```html
|
|
1439
|
-
<input name="${@this@.data.attribute.name}$"
|
|
1440
|
-
dr-event-change="@this@.change($element.checked)"
|
|
1441
|
-
dr-event-change:dispatch
|
|
1442
|
-
value="good"
|
|
1443
|
-
type="checkbox"
|
|
1444
|
-
checked
|
|
1445
|
-
/>
|
|
1446
|
-
```
|
|
1447
|
-
- attribute
|
|
1448
|
-
- dr-on-constructor:arguments: component constructor arguments
|
|
1449
|
-
- dr-on-create:callback: component created init callback script
|
|
1450
|
-
- dr-on-create:arguments: component onCreatedRender arguments
|
|
1451
|
-
- dr-on-init:arguments: component onInitRender arguments
|
|
1452
|
-
- dr-on-rendered-init: rendered
|
|
1453
|
-
- $component: component instance
|
|
1454
|
-
- $element: element instance
|
|
1455
|
-
- $attribute: element attribute object
|
|
1456
|
-
- $innerHTML: element innerHTML string
|
|
1457
|
-
- $creatorMetaData: metaData
|
|
1458
|
-
- #component#: component instance
|
|
1459
|
-
- #innerHTML#: element innerHTML
|
|
1460
|
-
- dr-option-this-name: renaming component variable name (default: component)
|
|
1461
|
-
- dr-option-component-name: renaming component variable name (default: component)
|
|
1462
|
-
- dr-option-inner-html-name: renaming innerHTML variable name (default: innerHTML)
|
|
1463
|
-
---
|
|
41
|
+
## License
|
|
1464
42
|
|
|
1465
|
-
|
|
1466
|
-
* MIT
|
|
1467
|
-
* visualkhh@gmail.com
|
|
43
|
+
This package is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|