@gogocat/data-bind 1.11.0 → 2.0.0
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/.editorconfig +14 -14
- package/.vscode/launch.json +12 -12
- package/CONFIGURATION.md +294 -0
- package/REACTIVE_MODE.md +553 -0
- package/README.md +266 -829
- package/babel.config.json +30 -0
- package/dist/js/_escape.d.ts +14 -0
- package/dist/js/_escape.d.ts.map +1 -0
- package/dist/js/applyBinding.d.ts +11 -0
- package/dist/js/applyBinding.d.ts.map +1 -0
- package/dist/js/attrBinding.d.ts +12 -0
- package/dist/js/attrBinding.d.ts.map +1 -0
- package/dist/js/binder.d.ts +67 -0
- package/dist/js/binder.d.ts.map +1 -0
- package/dist/js/changeBinding.d.ts +19 -0
- package/dist/js/changeBinding.d.ts.map +1 -0
- package/dist/js/commentWrapper.d.ts +39 -0
- package/dist/js/commentWrapper.d.ts.map +1 -0
- package/dist/js/config.d.ts +55 -0
- package/dist/js/config.d.ts.map +1 -0
- package/dist/js/createBindingOption.d.ts +32 -0
- package/dist/js/createBindingOption.d.ts.map +1 -0
- package/dist/js/createEventBinding.d.ts +10 -0
- package/dist/js/createEventBinding.d.ts.map +1 -0
- package/dist/js/cssBinding.d.ts +15 -0
- package/dist/js/cssBinding.d.ts.map +1 -0
- package/dist/js/dataBind.js +2772 -2519
- package/dist/js/dataBind.min.js +8 -1
- package/dist/js/dataBind.min.js.map +1 -1
- package/dist/js/domWalker.d.ts +9 -0
- package/dist/js/domWalker.d.ts.map +1 -0
- package/dist/js/forOfBinding.d.ts +12 -0
- package/dist/js/forOfBinding.d.ts.map +1 -0
- package/dist/js/hoverBinding.d.ts +13 -0
- package/dist/js/hoverBinding.d.ts.map +1 -0
- package/dist/js/ifBinding.d.ts +12 -0
- package/dist/js/ifBinding.d.ts.map +1 -0
- package/dist/js/index.d.ts +10 -0
- package/dist/js/index.d.ts.map +1 -0
- package/dist/js/modelBinding.d.ts +12 -0
- package/dist/js/modelBinding.d.ts.map +1 -0
- package/dist/js/postProcess.d.ts +3 -0
- package/dist/js/postProcess.d.ts.map +1 -0
- package/dist/js/pubSub.d.ts +11 -0
- package/dist/js/pubSub.d.ts.map +1 -0
- package/dist/js/reactiveProxy.d.ts +28 -0
- package/dist/js/reactiveProxy.d.ts.map +1 -0
- package/dist/js/renderForOfBinding.d.ts +8 -0
- package/dist/js/renderForOfBinding.d.ts.map +1 -0
- package/dist/js/renderIfBinding.d.ts +22 -0
- package/dist/js/renderIfBinding.d.ts.map +1 -0
- package/dist/js/renderIteration.d.ts +16 -0
- package/dist/js/renderIteration.d.ts.map +1 -0
- package/dist/js/renderTemplate.d.ts +14 -0
- package/dist/js/renderTemplate.d.ts.map +1 -0
- package/dist/js/renderTemplatesBinding.d.ts +19 -0
- package/dist/js/renderTemplatesBinding.d.ts.map +1 -0
- package/dist/js/showBinding.d.ts +13 -0
- package/dist/js/showBinding.d.ts.map +1 -0
- package/dist/js/switchBinding.d.ts +13 -0
- package/dist/js/switchBinding.d.ts.map +1 -0
- package/dist/js/textBinding.d.ts +13 -0
- package/dist/js/textBinding.d.ts.map +1 -0
- package/dist/js/types/_escape.d.ts +14 -0
- package/dist/js/types/_escape.d.ts.map +1 -0
- package/dist/js/types/applyBinding.d.ts +11 -0
- package/dist/js/types/applyBinding.d.ts.map +1 -0
- package/dist/js/types/attrBinding.d.ts +12 -0
- package/dist/js/types/attrBinding.d.ts.map +1 -0
- package/dist/js/types/binder.d.ts +67 -0
- package/dist/js/types/binder.d.ts.map +1 -0
- package/dist/js/types/changeBinding.d.ts +19 -0
- package/dist/js/types/changeBinding.d.ts.map +1 -0
- package/dist/js/types/commentWrapper.d.ts +39 -0
- package/dist/js/types/commentWrapper.d.ts.map +1 -0
- package/dist/js/types/config.d.ts +55 -0
- package/dist/js/types/config.d.ts.map +1 -0
- package/dist/js/types/createBindingOption.d.ts +32 -0
- package/dist/js/types/createBindingOption.d.ts.map +1 -0
- package/dist/js/types/createEventBinding.d.ts +10 -0
- package/dist/js/types/createEventBinding.d.ts.map +1 -0
- package/dist/js/types/cssBinding.d.ts +15 -0
- package/dist/js/types/cssBinding.d.ts.map +1 -0
- package/dist/js/types/domWalker.d.ts +9 -0
- package/dist/js/types/domWalker.d.ts.map +1 -0
- package/dist/js/types/forOfBinding.d.ts +12 -0
- package/dist/js/types/forOfBinding.d.ts.map +1 -0
- package/dist/js/types/hoverBinding.d.ts +13 -0
- package/dist/js/types/hoverBinding.d.ts.map +1 -0
- package/dist/js/types/ifBinding.d.ts +12 -0
- package/dist/js/types/ifBinding.d.ts.map +1 -0
- package/dist/js/types/index.d.ts +10 -0
- package/dist/js/types/index.d.ts.map +1 -0
- package/dist/js/types/modelBinding.d.ts +12 -0
- package/dist/js/types/modelBinding.d.ts.map +1 -0
- package/dist/js/types/postProcess.d.ts +3 -0
- package/dist/js/types/postProcess.d.ts.map +1 -0
- package/dist/js/types/pubSub.d.ts +11 -0
- package/dist/js/types/pubSub.d.ts.map +1 -0
- package/dist/js/types/reactiveProxy.d.ts +28 -0
- package/dist/js/types/reactiveProxy.d.ts.map +1 -0
- package/dist/js/types/renderForOfBinding.d.ts +8 -0
- package/dist/js/types/renderForOfBinding.d.ts.map +1 -0
- package/dist/js/types/renderIfBinding.d.ts +22 -0
- package/dist/js/types/renderIfBinding.d.ts.map +1 -0
- package/dist/js/types/renderIteration.d.ts +16 -0
- package/dist/js/types/renderIteration.d.ts.map +1 -0
- package/dist/js/types/renderTemplate.d.ts +14 -0
- package/dist/js/types/renderTemplate.d.ts.map +1 -0
- package/dist/js/types/renderTemplatesBinding.d.ts +19 -0
- package/dist/js/types/renderTemplatesBinding.d.ts.map +1 -0
- package/dist/js/types/showBinding.d.ts +13 -0
- package/dist/js/types/showBinding.d.ts.map +1 -0
- package/dist/js/types/switchBinding.d.ts +13 -0
- package/dist/js/types/switchBinding.d.ts.map +1 -0
- package/dist/js/types/textBinding.d.ts +13 -0
- package/dist/js/types/textBinding.d.ts.map +1 -0
- package/dist/js/types/types.d.ts +111 -0
- package/dist/js/types/types.d.ts.map +1 -0
- package/dist/js/types/util.d.ts +119 -0
- package/dist/js/types/util.d.ts.map +1 -0
- package/dist/js/types.d.ts +111 -0
- package/dist/js/types.d.ts.map +1 -0
- package/dist/js/util.d.ts +119 -0
- package/dist/js/util.d.ts.map +1 -0
- package/eslint.config.js +124 -0
- package/examples/DBMONSTER_COMPARISON.md +123 -0
- package/examples/afterRenderDemo.html +119 -0
- package/examples/bootstrap/css/animate.css +1579 -1579
- package/examples/bootstrap/css/bootstrap.min.css +6 -6
- package/examples/bootstrap/css/homeservices.css +378 -390
- package/examples/bootstrap/css/open-iconic.css +511 -511
- package/examples/bootstrap/fonts/open-iconic.svg +543 -543
- package/examples/bootstrap/js/compMessageDialog.js +20 -19
- package/examples/bootstrap/js/compSearchBar.js +12 -19
- package/examples/bootstrap/js/compSearchResults.js +50 -46
- package/examples/bootstrap/js/featureAdsResult.json +65 -65
- package/examples/bootstrap/js/searchResult.json +57 -57
- package/examples/bootstrap.html +343 -332
- package/examples/css/baseTodo.css +141 -141
- package/examples/css/dbMonsterStyles.css +27 -27
- package/examples/css/indexTodo.css +374 -374
- package/examples/dbmonsterForOfReactive.html +40 -0
- package/examples/dbmonsterReact.html +19 -0
- package/examples/forOfBindingSimpleDebug.html +45 -0
- package/examples/form.html +20 -4
- package/examples/globalConfig.html +131 -0
- package/examples/js/afterRenderDemo.js +190 -0
- package/examples/js/appTodo.js +46 -46
- package/examples/js/attrBindingDemo.js +2 -2
- package/examples/js/dbMonApp.js +24 -26
- package/examples/js/dbMonAppReact.jsx +79 -0
- package/examples/js/dbMonAppReactive.js +28 -0
- package/examples/js/fiberDemo.js +4 -4
- package/examples/js/filtersDemo.js +8 -8
- package/examples/js/forOfDemo.js +7 -9
- package/examples/js/forOfDemoComplex.js +44 -17
- package/examples/js/form.js +44 -12
- package/examples/js/globalConfig.js +117 -0
- package/examples/js/ifBindingDemo.js +16 -16
- package/examples/js/reactiveDemo.js +119 -0
- package/examples/js/switchBindingDemo.js +8 -8
- package/examples/react-dbmonster/dist/bundle.js +43 -0
- package/examples/react-dbmonster/package-lock.json +537 -0
- package/examples/react-dbmonster/package.json +16 -0
- package/examples/react-dbmonster/src/index.jsx +80 -0
- package/examples/reactiveDemo.html +127 -0
- package/examples/refreshRateTest.html +75 -75
- package/index.html +841 -0
- package/package.json +31 -34
- package/rollup.config.js +79 -36
- package/src/{_escape.js → _escape.ts} +19 -17
- package/src/applyBinding.ts +179 -0
- package/src/{attrBinding.js → attrBinding.ts} +14 -13
- package/src/binder.ts +289 -0
- package/src/changeBinding.ts +93 -0
- package/src/{commentWrapper.js → commentWrapper.ts} +33 -30
- package/src/config.ts +107 -0
- package/src/createBindingOption.ts +91 -0
- package/src/createEventBinding.ts +88 -0
- package/src/{cssBinding.js → cssBinding.ts} +13 -11
- package/src/{domWalker.js → domWalker.ts} +44 -30
- package/src/{forOfBinding.js → forOfBinding.ts} +4 -3
- package/src/hoverBinding.ts +84 -0
- package/src/{ifBinding.js → ifBinding.ts} +14 -12
- package/src/index.ts +53 -0
- package/src/{modelBinding.js → modelBinding.ts} +11 -9
- package/src/postProcess.ts +22 -0
- package/src/{pubSub.js → pubSub.ts} +24 -15
- package/src/reactiveProxy.ts +285 -0
- package/src/{renderForOfBinding.js → renderForOfBinding.ts} +55 -33
- package/src/{renderIfBinding.js → renderIfBinding.ts} +45 -20
- package/src/renderIteration.ts +53 -0
- package/src/renderTemplate.ts +165 -0
- package/src/renderTemplatesBinding.ts +73 -0
- package/src/{showBinding.js → showBinding.ts} +4 -3
- package/src/{switchBinding.js → switchBinding.ts} +18 -15
- package/src/{textBinding.js → textBinding.ts} +5 -4
- package/src/types.ts +124 -0
- package/src/util.ts +810 -0
- package/test/css/reporter.css +9 -9
- package/test/fixtures/dataBindBootstrap.html +2 -2
- package/test/fixtures/formBindings.html +9 -1
- package/test/globals.d.ts +19 -0
- package/test/helpers/testHelper.js +46 -11
- package/test/mocks/featureAdsResult.json +65 -65
- package/test/mocks/searchResult.json +57 -57
- package/test/specs/{attrBinding.spec.js → attrBinding.spec.ts} +103 -106
- package/test/specs/{binder.spec.js → binder.spec.ts} +29 -27
- package/test/specs/blurBinding.spec.ts +60 -0
- package/test/specs/chainableUse.spec.ts +125 -0
- package/test/specs/clickBinding.spec.ts +194 -0
- package/test/specs/{cssBinding.spec.js → cssBinding.spec.ts} +72 -79
- package/test/specs/{dataBindBootstrap.spec.js → dataBindBootstrap.spec.ts} +332 -313
- package/test/specs/{filter.spec.js → filter.spec.ts} +75 -76
- package/test/specs/{forOfBinding.spec.js → forOfBinding.spec.ts} +208 -219
- package/test/specs/formBinding.spec.ts +272 -0
- package/test/specs/ifBinding.spec.ts +165 -0
- package/test/specs/{nestedComponent.spec.js → nestedComponent.spec.ts} +88 -88
- package/test/specs/reactiveProxy.spec.ts +465 -0
- package/test/specs/{showBinding.spec.js → showBinding.spec.ts} +148 -149
- package/test/specs/{switchBinding.spec.js → switchBinding.spec.ts} +172 -173
- package/test/specs/templateBinding.spec.ts +273 -0
- package/test/specs/{textBinding.spec.js → textBinding.spec.ts} +47 -48
- package/test/tsconfig.json +31 -0
- package/test-output.txt +200 -0
- package/test-reactive.html +224 -0
- package/tsconfig.json +28 -0
- package/vendors/lodash.custom.js +4577 -4577
- package/vendors/lodash.custom.min.js +45 -45
- package/vitest.config.js +27 -0
- package/.eslintrc.js +0 -1
- package/.grunt/grunt-contrib-jasmine/boot.js +0 -161
- package/.grunt/grunt-contrib-jasmine/dist/js/dataBind.js +0 -9
- package/.grunt/grunt-contrib-jasmine/grunt-template-jasmine-istanbul/reporter.js +0 -23
- package/.grunt/grunt-contrib-jasmine/jasmine-html.js +0 -853
- package/.grunt/grunt-contrib-jasmine/jasmine.css +0 -271
- package/.grunt/grunt-contrib-jasmine/jasmine.js +0 -9761
- package/.grunt/grunt-contrib-jasmine/jasmine_favicon.png +0 -0
- package/.grunt/grunt-contrib-jasmine/json2.js +0 -489
- package/.grunt/grunt-contrib-jasmine/reporter.js +0 -107
- package/coverage/coverage.json +0 -1
- package/coverage/lcov/lcov-report/base.css +0 -213
- package/coverage/lcov/lcov-report/index.html +0 -93
- package/coverage/lcov/lcov-report/js/dataBind.js.html +0 -6596
- package/coverage/lcov/lcov-report/js/index.html +0 -93
- package/coverage/lcov/lcov-report/prettify.css +0 -1
- package/coverage/lcov/lcov-report/prettify.js +0 -1
- package/coverage/lcov/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov/lcov-report/sorter.js +0 -158
- package/coverage/lcov/lcov.info +0 -1991
- package/eslintrc.json +0 -40
- package/examples/bootstrap/js/bootstrap.min.js +0 -6
- package/examples/bootstrap/js/popper.min.js +0 -5
- package/examples/bootstrap/js/searchSuggestion.js +0 -58
- package/examples/bootstrap/js/typeahead.jquery.js +0 -1538
- package/gruntfile.js +0 -92
- package/gulpfile.js +0 -32
- package/src/binder.js +0 -422
- package/src/changeBinding.js +0 -57
- package/src/config.js +0 -65
- package/src/createBindingOption.js +0 -66
- package/src/createEventBinding.js +0 -46
- package/src/eventSystem.js +0 -46
- package/src/hoverBinding.js +0 -57
- package/src/index.js +0 -26
- package/src/renderTemplate.js +0 -128
- package/src/util.js +0 -648
- package/test/specs/blurBinding.spec.js +0 -57
- package/test/specs/formBinding.spec.js +0 -292
- package/test/specs/ifBinding.spec.js +0 -169
- package/test/specs/templateBinding.spec.js +0 -117
- package/vendors/jasmine-jquery.js +0 -841
- package/vendors/jquery-3.2.1.min.js +0 -4
package/README.md
CHANGED
|
@@ -3,973 +3,410 @@
|
|
|
3
3
|
[](https://www.codacy.com/app/gogocat/dataBind?utm_source=github.com&utm_medium=referral&utm_content=gogocat/dataBind&utm_campaign=Badge_Grade)
|
|
4
4
|

|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
# dataBind.js
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
> Simple, fast, reactive data binding for modern browsers. No build tools required.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## Why dataBind?
|
|
11
11
|
|
|
12
|
-
dataBind is
|
|
12
|
+
**dataBind.js is not another front-end UI framework.** It's a lightweight, pragmatic solution for adding reactive data binding to your existing HTML, without the complexity of modern frameworks.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
### Key Features
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
✨ **Simple** - Just HTML + JavaScript. No JSX, no virtual DOM, no build pipeline
|
|
17
|
+
⚡ **Fast** - Extremely small footprint (~15KB min+gzip) and high performance ([see benchmarks](https://gogocat.github.io/dataBind/examples/dbmonsterForOf.html))
|
|
18
|
+
🔄 **Reactive** - Automatic UI updates when data changes (like Vue 3, but simpler)
|
|
19
|
+
🎯 **Focused** - View + ViewModel pattern. No router, no state management, no opinions
|
|
20
|
+
🛠️ **Zero Setup** - Drop in a `<script>` tag and go. Works with any backend or framework
|
|
21
|
+
📦 **Tiny** - No dependencies, no build tools, no configuration
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
## Quick Start
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* **Isolated scope:** Each component only works with its own viewModel scope. No complex props pass up and down
|
|
23
|
-
|
|
24
|
-
* **zero setup:** There is no need to run any build tool for development or production
|
|
25
|
-
|
|
26
|
-
* **framework agnostic :** dataBind can work with any other framework. There is no need to rebuild everything in order to use it. It is design to leverage and modernise what is already working
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
## How to use it?
|
|
32
|
-
|
|
33
|
-
For web load via script tag
|
|
34
|
-
|
|
35
|
-
<script src="dist/js/dataBind.min.js"></script>
|
|
36
|
-
|
|
37
|
-
Or node_module
|
|
38
|
-
|
|
39
|
-
npm install @gogocat/data-bind
|
|
40
|
-
|
|
41
|
-
then
|
|
42
|
-
|
|
43
|
-
import dataBind from '@gogocat/data-bind';
|
|
44
|
-
|
|
45
|
-
**Usage**
|
|
46
|
-
|
|
47
|
-
The following is a very simple example shows text binding.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
Most of the component logic will be in the viewModel(plain old JavaScript object).
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
`dataBind.init` will return an instance of `Binder`(this is the bound dataBind object).
|
|
56
|
-
|
|
57
|
-
Then just call `render` to start render to the page.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
**HTML**
|
|
25
|
+
### Installation
|
|
62
26
|
|
|
27
|
+
**Via CDN:**
|
|
63
28
|
```html
|
|
64
|
-
|
|
65
|
-
<section data-bind-comp="simpleComponent">
|
|
66
|
-
<div>
|
|
67
|
-
<h5 data-bind-text="heading"></h5>
|
|
68
|
-
<p data-bind-text="description"></p>
|
|
69
|
-
</div>
|
|
70
|
-
</section>
|
|
71
|
-
|
|
29
|
+
<script src="https://unpkg.com/@gogocat/data-bind/dist/js/dataBind.min.js"></script>
|
|
72
30
|
```
|
|
73
31
|
|
|
74
|
-
**
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const simpleComponentViewModel = {
|
|
79
|
-
heading: 'Test heading',
|
|
80
|
-
description: 'This is my test description',
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// init data bind with view
|
|
84
|
-
const simpleComponent = dataBind.init(
|
|
85
|
-
document.querySelector('[data-bind-comp="simpleComponent"]'),
|
|
86
|
-
simpleComponentViewModel
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// trigger render and log after render
|
|
91
|
-
simpleComponent
|
|
92
|
-
.render()
|
|
93
|
-
.then(function() {
|
|
94
|
-
// for debug
|
|
95
|
-
console.log(simpleComponent);
|
|
96
|
-
});
|
|
97
|
-
|
|
32
|
+
**Via NPM:**
|
|
33
|
+
```bash
|
|
34
|
+
npm install @gogocat/data-bind
|
|
98
35
|
```
|
|
99
36
|
|
|
100
|
-
To make change, just update the data in viewModel and then call `render()`.
|
|
101
|
-
|
|
102
37
|
```javascript
|
|
103
|
-
|
|
104
|
-
simpleComponentViewModel.heading='new heading';
|
|
105
|
-
|
|
106
|
-
simpleComponent.render();
|
|
107
|
-
|
|
38
|
+
import dataBind from '@gogocat/data-bind';
|
|
108
39
|
```
|
|
109
40
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
> :bulb: *All declarative bindings accept value or function that returns value from the viewModel*.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
Example: `heading` in the viewModel can be a value or function that returns value.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
The binding can also pass-in parameters.
|
|
41
|
+
### Basic Example
|
|
123
42
|
|
|
43
|
+
**HTML:**
|
|
124
44
|
```html
|
|
125
|
-
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
The following parameters are helpers reference `$index` or `$data` or `$root`. More details below
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
For more advance example. Please check [**examples/bootstrap.html**](https://gogocat.github.io/dataBind/examples/bootstrap.html).
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
> *bootstrap example shows how to use multiple, nested components and services together. Please run this example from a local server*.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
----
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
### The init and render functions
|
|
148
|
-
|
|
149
|
-
```javascript
|
|
150
|
-
|
|
151
|
-
...
|
|
152
|
-
|
|
153
|
-
// DOM ready bind viewModel with target DOM element
|
|
154
|
-
const simpleComponent = dataBind.init(
|
|
155
|
-
document.querySelector('[data-bind-comp="simpleComponent"]'),
|
|
156
|
-
simpleComponentViewModel
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
// trigger render, then console log for debug
|
|
162
|
-
simpleComponent
|
|
163
|
-
.render()
|
|
164
|
-
.then(function(ctx) {
|
|
165
|
-
// for debug
|
|
166
|
-
console.log(simpleComponent === ctx);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
In this simple example. First we call `.init` to initialise the component with the viewModle:
|
|
173
|
-
|
|
174
|
-
```javascript
|
|
175
|
-
|
|
176
|
-
const simpleComponent = dataBind.init([targetDOMElement], [viewModel]);
|
|
177
|
-
|
|
45
|
+
<div id="app">
|
|
46
|
+
<h1 data-bind-text="greeting"></h1>
|
|
47
|
+
<button data-bind-click="changeGreeting">Change</button>
|
|
48
|
+
</div>
|
|
178
49
|
```
|
|
179
50
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
The next call of `render` function is to render value from viewModel to the DOM (if there are difference). It returns a `promise` object for logic that can be trigger after the component fully rendered.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
The resolver callback will receive a `context` object; because inside the resolver function `this` is refer to window.
|
|
189
|
-
|
|
190
|
-
`context` object is the same object as `simpleComponent` in this example.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
To re-render the component, just call `render`. As mentioned, this function is an asynchronous and debounced operation. This mean, doesn't matter how many times it get call it will only make change to DOM once. Minimise browser repaint/reflow.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
For edge case; pass an optional setting object when calling `render` to control what binding should be render or not.
|
|
199
|
-
|
|
51
|
+
**JavaScript:**
|
|
200
52
|
```javascript
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
showBinding: true,
|
|
208
|
-
modelBinding: true,
|
|
209
|
-
attrBinding: true,
|
|
210
|
-
forOfBinding: true,
|
|
211
|
-
switchBinding: true,
|
|
212
|
-
changeBinding: true,
|
|
213
|
-
clickBinding: true,
|
|
214
|
-
dblclickBinding: true,
|
|
215
|
-
blurBinding: true,
|
|
216
|
-
focusBinding: true,
|
|
217
|
-
hoverBinding: true,
|
|
218
|
-
submitBinding: true,
|
|
53
|
+
const app = dataBind.init(document.getElementById('app'), {
|
|
54
|
+
greeting: 'Hello, World!',
|
|
55
|
+
changeGreeting() {
|
|
56
|
+
this.greeting = 'Hello, dataBind!';
|
|
57
|
+
// That's it! UI updates automatically in reactive mode
|
|
58
|
+
}
|
|
219
59
|
});
|
|
220
60
|
|
|
61
|
+
app.render();
|
|
221
62
|
```
|
|
222
63
|
|
|
223
|
-
|
|
64
|
+
That's it! No JSX, no compilation, no complex setup.
|
|
224
65
|
|
|
225
|
-
|
|
66
|
+
## Reactive State (Default)
|
|
226
67
|
|
|
227
|
-
|
|
68
|
+
By default, dataBind uses **reactive mode** - changes to data automatically update the UI:
|
|
228
69
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
dblclick: 'data-xy-dblclick',
|
|
236
|
-
blur: 'data-xy-blur',
|
|
237
|
-
focus: 'data-xy-focus',
|
|
238
|
-
hover: 'data-xy-hover',
|
|
239
|
-
change: 'data-xy-change',
|
|
240
|
-
submit: 'data-xy-submit',
|
|
241
|
-
model: 'data-xy-model',
|
|
242
|
-
show: 'data-xy-show',
|
|
243
|
-
css: 'data-xy-css',
|
|
244
|
-
attr: 'data-xy-attr',
|
|
245
|
-
forOf: 'data-xy-for',
|
|
246
|
-
if: 'data-xy-if',
|
|
247
|
-
switch: 'data-xy-switch',
|
|
248
|
-
case: 'data-xy-case',
|
|
249
|
-
default: 'data-xy-default'
|
|
70
|
+
```javascript
|
|
71
|
+
const app = dataBind.init(element, {
|
|
72
|
+
counter: 0,
|
|
73
|
+
items: [],
|
|
74
|
+
increment() {
|
|
75
|
+
this.counter++; // UI updates automatically!
|
|
250
76
|
},
|
|
77
|
+
addItem() {
|
|
78
|
+
this.items.push({ text: 'New Item' }); // UI updates automatically!
|
|
79
|
+
}
|
|
251
80
|
});
|
|
252
81
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
// init
|
|
256
|
-
const simpleComponent = dataBind.init(
|
|
257
|
-
document.querySelector('[data-bind-comp="simpleComponent"]'),
|
|
258
|
-
simpleComponentViewModel
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
// render
|
|
262
|
-
simpleComponent.render();
|
|
263
|
-
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
dataBind `use` method can be use to set global setting of binding attribute namespace. It accept an option object showing in above example.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
## Visual bindings
|
|
271
|
-
|
|
272
|
-
The following bindings produce visual changes
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
### Template binding
|
|
277
|
-
|
|
278
|
-
```html
|
|
279
|
-
|
|
280
|
-
<section
|
|
281
|
-
data-bind-comp="simpleComponent"
|
|
282
|
-
data-bind-tmp="{id: 'exampleTemplate', data: '$root'}"
|
|
283
|
-
></section>
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
<template id="exampleTemplate">
|
|
287
|
-
<h1 data-bind-text="heading"></h1>
|
|
288
|
-
</template>
|
|
289
|
-
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
The attribute `data-bind-tmp` accept a JSON like object. `id` is reference to the `template` element id. `data` is reference to the data object within the bound viewModel. In this example `$root` means the root of the viewModel itself.
|
|
293
|
-
|
|
294
|
-
If there a 3rd option as `append: true` or `prepend: true`, the content will then append or preprend to the target container (the section tag in this example). This make building infinity scroll content very easy and efficient.
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
### Text binding
|
|
300
|
-
|
|
301
|
-
```html
|
|
302
|
-
|
|
303
|
-
<h1 data-bind-text="heading"></h1>
|
|
304
|
-
|
|
305
|
-
<h1 data-bind-text="fullName | uppercase"></h1>
|
|
306
|
-
|
|
82
|
+
app.render();
|
|
307
83
|
```
|
|
308
84
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
The 2nd example shows usage of **filter** ' | '. The value from viewModel's property `fullName` will pass on to the viewModel's `uppercase` function that returns value to be display. Filters can be chain together one after the other. more detail below.
|
|
314
|
-
|
|
315
|
-
### css binding
|
|
316
|
-
|
|
317
|
-
```html
|
|
85
|
+
### How It Works
|
|
318
86
|
|
|
319
|
-
|
|
87
|
+
dataBind uses JavaScript Proxies to detect data changes:
|
|
320
88
|
|
|
89
|
+
```javascript
|
|
90
|
+
// After init, use component.viewModel for reactive updates
|
|
91
|
+
app.viewModel.counter++; // ✅ Triggers automatic render
|
|
92
|
+
app.viewModel.items.push({...}); // ✅ Triggers automatic render
|
|
93
|
+
app.viewModel.user.name = 'Jane'; // ✅ Deep reactivity works!
|
|
321
94
|
```
|
|
322
95
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
96
|
+
### Manual Mode (Optional)
|
|
326
97
|
|
|
327
|
-
|
|
98
|
+
For maximum performance control, disable reactive mode:
|
|
328
99
|
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
<h1 data-bind-if="myCondition">
|
|
332
|
-
<span>Hello</span>
|
|
333
|
-
</h1>
|
|
334
|
-
|
|
335
|
-
// conditditional render the DIV element and its template binding
|
|
336
|
-
<div
|
|
337
|
-
data-bind-if="!myCondition"
|
|
338
|
-
data-bind-tmp="{id: 'someTemplateId', data: 'someData'}"
|
|
339
|
-
></div>
|
|
100
|
+
```javascript
|
|
101
|
+
const app = dataBind.use({ reactive: false }).init(element, viewModel);
|
|
340
102
|
|
|
103
|
+
// In manual mode, call render() explicitly
|
|
104
|
+
viewModel.counter++;
|
|
105
|
+
app.render(); // Manual render call
|
|
341
106
|
```
|
|
342
107
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
If `myCondition` is false. the children elements will be removed from DOM. When later `myCondition` is set to true. The elements will then render back.
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
With negate expression(second example above), when the expression `!myCondition` evaluate to true. The template binding `data-bind-tmp` will execute and render accordingly.
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
[example](https://gogocat.github.io/dataBind/examples/ifBinding.html)
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
### show binding
|
|
108
|
+
## Core Bindings
|
|
360
109
|
|
|
110
|
+
### Text Binding
|
|
361
111
|
```html
|
|
362
|
-
|
|
363
|
-
<
|
|
364
|
-
<span>Hello</span>
|
|
365
|
-
</h1>
|
|
366
|
-
|
|
112
|
+
<h1 data-bind-text="title"></h1>
|
|
113
|
+
<p data-bind-text="user.name"></p> <!-- Deep paths supported -->
|
|
367
114
|
```
|
|
368
115
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
### model binding
|
|
374
|
-
|
|
116
|
+
### Event Binding
|
|
375
117
|
```html
|
|
376
|
-
|
|
377
|
-
<input
|
|
378
|
-
|
|
379
|
-
name="userName"
|
|
380
|
-
type="text"
|
|
381
|
-
data-bind-model="personalDetails.userName"
|
|
382
|
-
data-bind-change="onInputChange"
|
|
383
|
-
required
|
|
384
|
-
>
|
|
385
|
-
|
|
118
|
+
<button data-bind-click="handleClick">Click Me</button>
|
|
119
|
+
<input data-bind-change="handleChange" data-bind-model="username">
|
|
120
|
+
<form data-bind-submit="handleSubmit">...</form>
|
|
386
121
|
```
|
|
387
122
|
|
|
388
|
-
The attribute `data-bind-model` is refernce to the viewModel's property '**personalDetails.userName**'. This property can be a string or a function that returns string. Model binding is a one-way binding operation that populate the input field `value` attribute with value come from the viewModel.
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
**data-bind-model**
|
|
393
|
-
|
|
394
|
-
> viewModel -> DOM
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
For two-way data binding; use together with `data-bind-change`. It will update the viewModel if the value has changed and then trigger the event handler `onInputChange`. More detail below.
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
**data-bind-change**
|
|
403
|
-
|
|
404
|
-
> DOM -> viewModel
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
[example](https://gogocat.github.io/dataBind/examples/todomvc.html)
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
### attribute binding
|
|
413
|
-
|
|
414
123
|
```javascript
|
|
415
|
-
|
|
416
|
-
<img data-bind-attr="getImgAttr">
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
// js
|
|
420
124
|
const viewModel = {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
125
|
+
handleClick(event, element) {
|
|
126
|
+
console.log('Clicked!', event, element);
|
|
127
|
+
},
|
|
128
|
+
handleChange(event, element, newValue, oldValue) {
|
|
129
|
+
console.log('Changed from', oldValue, 'to', newValue);
|
|
426
130
|
}
|
|
427
131
|
};
|
|
428
|
-
|
|
429
|
-
```
|
|
430
|
-
|
|
431
|
-
The attribute `data-bind-attr` is refernce to the viewModel's property '**getImgAttr**'. This property can be a object or a function that returns object with `key:value`. The key is the attribute name and value is the value of that attribute.
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
attribute binding is useful for more complex usage together with `data-bind-for` binding.
|
|
436
|
-
|
|
437
|
-
Please see the `<select>` elements in this [example](https://gogocat.github.io/dataBind/examples/forOfBindingComplex.html)
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
### forOf binding
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
```javascript
|
|
446
|
-
|
|
447
|
-
<p
|
|
448
|
-
data-bind-for="result of results"
|
|
449
|
-
data-bind-text="result.content"
|
|
450
|
-
></p>
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
// js
|
|
454
|
-
const viewModel = {
|
|
455
|
-
results: [
|
|
456
|
-
{
|
|
457
|
-
content: '1'
|
|
458
|
-
},
|
|
459
|
-
{
|
|
460
|
-
content: '2'
|
|
461
|
-
},
|
|
462
|
-
{
|
|
463
|
-
content: '3'
|
|
464
|
-
}
|
|
465
|
-
]
|
|
466
|
-
};
|
|
467
|
-
|
|
468
132
|
```
|
|
469
133
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
The result will looks like this:
|
|
475
|
-
|
|
134
|
+
### List Rendering
|
|
476
135
|
```html
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
<
|
|
480
|
-
|
|
481
|
-
<p data-bind-text="result.content">3</p>
|
|
482
|
-
<!--data-forOf_result_of_results_end-->
|
|
483
|
-
|
|
136
|
+
<div data-bind-for="item in items">
|
|
137
|
+
<p data-bind-text="item.name"></p>
|
|
138
|
+
<button data-bind-click="$root.deleteItem($index)">Delete</button>
|
|
139
|
+
</div>
|
|
484
140
|
```
|
|
485
141
|
|
|
486
|
-
[example](https://gogocat.github.io/dataBind/examples/forOfBinding.html)
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
### switch binding
|
|
491
|
-
|
|
492
142
|
```javascript
|
|
493
|
-
|
|
494
|
-
<div data-bind-switch="selectedStory">
|
|
495
|
-
<div data-bind-case="s1">
|
|
496
|
-
<h2>Case 1</h2>
|
|
497
|
-
</div>
|
|
498
|
-
<div data-bind-case="s2">
|
|
499
|
-
<h2>Case 2</h2>
|
|
500
|
-
</div>
|
|
501
|
-
<div data-bind-case="s3">
|
|
502
|
-
<h2>Case 3</h2>
|
|
503
|
-
</div>
|
|
504
|
-
<div data-bind-default="">
|
|
505
|
-
<p>No story found...</p>
|
|
506
|
-
</div>
|
|
507
|
-
</div>
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
// js
|
|
511
143
|
const viewModel = {
|
|
512
|
-
|
|
144
|
+
items: [
|
|
145
|
+
{ name: 'Item 1' },
|
|
146
|
+
{ name: 'Item 2' }
|
|
147
|
+
],
|
|
148
|
+
deleteItem(index) {
|
|
149
|
+
this.items.splice(index, 1); // Reactive update!
|
|
150
|
+
}
|
|
513
151
|
};
|
|
514
|
-
|
|
515
152
|
```
|
|
516
153
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
The attribute `data-bind-switch` is refernce to the viewModel's property '**selectedStory**'. This property can be a string or a function that returns a string.
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
In this example the result will looks like this, since selectedStory` match `data-bind-case="s1"`.
|
|
526
|
-
|
|
154
|
+
### Conditional Rendering
|
|
527
155
|
```html
|
|
156
|
+
<!-- Removes from DOM when false -->
|
|
157
|
+
<div data-bind-if="isLoggedIn">
|
|
158
|
+
<p data-bind-text="user.name"></p>
|
|
159
|
+
</div>
|
|
528
160
|
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
</div>
|
|
161
|
+
<!-- Hides with CSS when false -->
|
|
162
|
+
<div data-bind-show="isVisible">
|
|
163
|
+
<p>Visible content</p>
|
|
533
164
|
</div>
|
|
534
165
|
|
|
166
|
+
<!-- Switch statement for multiple conditions -->
|
|
167
|
+
<div data-bind-switch="currentState">
|
|
168
|
+
<div data-bind-case="loading">Loading...</div>
|
|
169
|
+
<div data-bind-case="error">An error occurred</div>
|
|
170
|
+
<div data-bind-case="success">Data loaded successfully!</div>
|
|
171
|
+
<div data-bind-default>Please wait...</div>
|
|
172
|
+
</div>
|
|
535
173
|
```
|
|
536
174
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
The following binding produce interactivities
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
### change binding
|
|
548
|
-
|
|
549
|
-
```javascript
|
|
175
|
+
### CSS Binding
|
|
176
|
+
```html
|
|
177
|
+
<div data-bind-css="{ active: isActive, disabled: !isEnabled }"></div>
|
|
178
|
+
<div data-bind-css="dynamicClass"></div>
|
|
179
|
+
```
|
|
550
180
|
|
|
551
|
-
|
|
552
|
-
|
|
181
|
+
### Two-Way Data Binding
|
|
182
|
+
```html
|
|
183
|
+
<input
|
|
553
184
|
type="text"
|
|
554
|
-
data-bind-
|
|
555
|
-
|
|
556
|
-
autofocus
|
|
557
|
-
>
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
// js
|
|
561
|
-
const viewModel = {
|
|
562
|
-
onAddTask: function(e, $el, newValue, oldValue) {
|
|
563
|
-
// do something...
|
|
564
|
-
},
|
|
565
|
-
}
|
|
566
|
-
|
|
185
|
+
data-bind-model="username"
|
|
186
|
+
data-bind-change="onUsernameChange">
|
|
567
187
|
```
|
|
568
188
|
|
|
569
|
-
`data-bind-
|
|
189
|
+
The `data-bind-model` populates the input value from viewModel, while `data-bind-change` updates the viewModel when the input changes.
|
|
570
190
|
|
|
571
|
-
|
|
191
|
+
## Advanced Features
|
|
572
192
|
|
|
573
|
-
|
|
193
|
+
### Templates
|
|
194
|
+
```html
|
|
195
|
+
<div data-bind-tmp="{id: 'userCard', data: 'user'}"></div>
|
|
574
196
|
|
|
575
|
-
|
|
197
|
+
<template id="userCard">
|
|
198
|
+
<div class="card">
|
|
199
|
+
<h2 data-bind-text="name"></h2>
|
|
200
|
+
<p data-bind-text="email"></p>
|
|
201
|
+
</div>
|
|
202
|
+
</template>
|
|
203
|
+
```
|
|
576
204
|
|
|
577
|
-
|
|
205
|
+
### Filters
|
|
206
|
+
```html
|
|
207
|
+
<p data-bind-text="price | toDiscount | addGst"></p>
|
|
208
|
+
```
|
|
578
209
|
|
|
579
210
|
```javascript
|
|
580
|
-
|
|
581
|
-
<div data-bind-comp="todoComponent">
|
|
582
|
-
<input
|
|
583
|
-
id="new-todo"
|
|
584
|
-
type="text"
|
|
585
|
-
data-bind-change="onAddTask"
|
|
586
|
-
data-bind-model="currentTask"
|
|
587
|
-
placeholder="What needs to be done?"
|
|
588
|
-
autofocus
|
|
589
|
-
>
|
|
590
|
-
</div>
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
// js
|
|
594
211
|
const viewModel = {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
212
|
+
price: 100,
|
|
213
|
+
toDiscount(value) {
|
|
214
|
+
return value * 0.9; // 10% discount
|
|
215
|
+
},
|
|
216
|
+
addGst(value) {
|
|
217
|
+
return value * 1.1; // Add 10% GST
|
|
601
218
|
}
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
// init data bind with view
|
|
606
|
-
const toDoApp = dataBind.init(
|
|
607
|
-
document.querySelector('[data-bind-comp="todoComponent"]'),
|
|
608
|
-
viewModel
|
|
609
|
-
);
|
|
610
|
-
|
|
611
|
-
// trigger render
|
|
612
|
-
toDoApp.render();
|
|
613
|
-
|
|
614
|
-
|
|
219
|
+
};
|
|
615
220
|
```
|
|
616
221
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
> Once the viewModel bound with `dataBind.init` call, the viewModel will be extended. `APP` property is the bound dataBind object.
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
### click binding
|
|
626
|
-
|
|
222
|
+
### Component Communication (Pub/Sub)
|
|
627
223
|
```javascript
|
|
224
|
+
// Component A: Subscribe to events
|
|
225
|
+
componentA.subscribe('USER_UPDATED', (userData) => {
|
|
226
|
+
console.log('User updated:', userData);
|
|
227
|
+
});
|
|
628
228
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
data-bind-click="onClearAllCompleted"
|
|
632
|
-
>
|
|
633
|
-
Clear completed
|
|
634
|
-
</button>
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
// js
|
|
638
|
-
const viewModel = {
|
|
639
|
-
onClearAllCompleted: function(e, $el) {
|
|
640
|
-
// do something...
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
|
|
229
|
+
// Component B: Publish events
|
|
230
|
+
componentB.publish('USER_UPDATED', { name: 'John', email: 'john@example.com' });
|
|
644
231
|
```
|
|
645
232
|
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
### dblclick binding
|
|
651
|
-
|
|
233
|
+
### AfterRender Callback
|
|
652
234
|
```javascript
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
>
|
|
658
|
-
Clear completed
|
|
659
|
-
</button>
|
|
660
|
-
|
|
661
|
-
// js
|
|
662
|
-
const viewModel = {
|
|
663
|
-
onDoubleClicked: function(e, $el) {
|
|
664
|
-
// do something...
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
|
|
235
|
+
app.afterRender(() => {
|
|
236
|
+
console.log('Render completed!');
|
|
237
|
+
// Perform DOM operations, analytics, etc.
|
|
238
|
+
});
|
|
668
239
|
```
|
|
669
240
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
### blur binding
|
|
675
|
-
|
|
241
|
+
### Global Configuration
|
|
676
242
|
```javascript
|
|
243
|
+
// Set global defaults for all components
|
|
244
|
+
dataBind.use({
|
|
245
|
+
reactive: true, // Enable reactive mode globally
|
|
246
|
+
trackChanges: false // Track individual property changes
|
|
247
|
+
});
|
|
677
248
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
onBlur: function(e, $el) {
|
|
683
|
-
// do something...
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
|
|
249
|
+
// Or use chainable API
|
|
250
|
+
const app = dataBind
|
|
251
|
+
.use({ reactive: false })
|
|
252
|
+
.init(element, viewModel);
|
|
687
253
|
```
|
|
688
254
|
|
|
689
|
-
|
|
255
|
+
## Performance
|
|
690
256
|
|
|
691
|
-
|
|
257
|
+
dataBind is **extremely fast**. Try our benchmarks:
|
|
692
258
|
|
|
693
|
-
|
|
259
|
+
- [**DBMonster - dataBind** (1000+ updates/sec)](https://gogocat.github.io/dataBind/examples/dbmonsterForOf.html)
|
|
260
|
+
- [**DBMonster - React** (comparison)](https://gogocat.github.io/dataBind/examples/dbmonsterReact.html)
|
|
261
|
+
- [**Fiber** (Complex nested updates)](https://gogocat.github.io/dataBind/examples/fiber-demo.html)
|
|
694
262
|
|
|
695
|
-
|
|
263
|
+
Compare with [other frameworks](http://mathieuancelin.github.io/js-repaint-perfs/).
|
|
696
264
|
|
|
697
|
-
|
|
265
|
+
### Why So Fast?
|
|
698
266
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
}
|
|
704
|
-
}
|
|
267
|
+
- **No Virtual DOM** - Direct DOM updates with minimal overhead
|
|
268
|
+
- **Efficient Diffing** - Only updates changed elements
|
|
269
|
+
- **Debounced Rendering** - Batches multiple changes via requestAnimationFrame
|
|
270
|
+
- **Tiny Size** - ~15KB min+gzip (vs React 40KB+, Vue 33KB+)
|
|
705
271
|
|
|
706
|
-
|
|
272
|
+
## Real-World Examples
|
|
707
273
|
|
|
708
|
-
|
|
274
|
+
- [**TodoMVC**](https://gogocat.github.io/dataBind/examples/todomvc.html) - Classic todo app
|
|
275
|
+
- [**Bootstrap Integration**](https://gogocat.github.io/dataBind/examples/bootstrap.html) - Multi-component app
|
|
276
|
+
- [**Complex Lists**](https://gogocat.github.io/dataBind/examples/forOfBindingComplex.html) - Nested templates
|
|
277
|
+
- [**Reactive Demo**](https://gogocat.github.io/dataBind/examples/reactiveDemo.html) - Reactive state examples
|
|
709
278
|
|
|
710
|
-
|
|
279
|
+
## Use Cases
|
|
711
280
|
|
|
712
|
-
|
|
281
|
+
**Perfect For:**
|
|
713
282
|
|
|
714
|
-
|
|
283
|
+
✅ Adding interactivity to server-rendered pages (PHP, .NET, Rails, etc.)
|
|
715
284
|
|
|
716
|
-
|
|
285
|
+
✅ Progressive enhancement of existing sites
|
|
717
286
|
|
|
718
|
-
|
|
719
|
-
const viewModel = {
|
|
720
|
-
onHover: {
|
|
721
|
-
in: function(e, $el) {
|
|
722
|
-
// do something when mouse in
|
|
723
|
-
},
|
|
724
|
-
out: function(e, $el) {
|
|
725
|
-
// do something when mouse out
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
}
|
|
287
|
+
✅ Rapid prototyping without build setup
|
|
729
288
|
|
|
730
|
-
|
|
289
|
+
✅ Small to medium web applications
|
|
731
290
|
|
|
732
|
-
|
|
291
|
+
✅ Projects where bundle size matters (eg Interactive Ad / widget on 3rd party web site)
|
|
733
292
|
|
|
734
|
-
|
|
293
|
+
✅ Teams that prefer vanilla JavaScript
|
|
735
294
|
|
|
736
|
-
### submit binding
|
|
737
295
|
|
|
738
|
-
|
|
296
|
+
**Not Ideal For:**
|
|
739
297
|
|
|
740
|
-
|
|
298
|
+
❌ Large single-page applications (consider Vue, React, Angular)
|
|
741
299
|
|
|
742
|
-
|
|
300
|
+
❌ Projects requiring full framework ecosystem (routing, state management, etc.)
|
|
743
301
|
|
|
744
|
-
|
|
302
|
+
❌ Micro-components smaller than a section/widget
|
|
745
303
|
|
|
746
|
-
|
|
747
304
|
|
|
748
|
-
|
|
749
|
-
const viewModel = {
|
|
750
|
-
onSubmit: function(e, $el, formData) {
|
|
751
|
-
// do something...
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
```
|
|
305
|
+
## Philosophy
|
|
756
306
|
|
|
757
|
-
|
|
307
|
+
dataBind follows these principles:
|
|
758
308
|
|
|
759
|
-
|
|
309
|
+
1. **Simplicity** - HTML is the template, JavaScript is the logic. No new syntax to learn.
|
|
310
|
+
2. **Pragmatism** - Leverage existing infrastructure. Work with what you have.
|
|
311
|
+
3. **Performance** - Small, fast, and efficient. No bloat.
|
|
312
|
+
4. **Zero Dependencies** - No build tools, no framework lock-in.
|
|
313
|
+
5. **Progressive Enhancement** - Add reactivity where needed, keep it simple where possible.
|
|
760
314
|
|
|
761
|
-
|
|
315
|
+
## API Overview
|
|
762
316
|
|
|
317
|
+
### Initialization
|
|
763
318
|
```javascript
|
|
764
|
-
|
|
765
|
-
<p>Price: <span data-bind-text="story.price | toDiscount | addGst"></span></p>
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
// js
|
|
770
|
-
const viewModel = {
|
|
771
|
-
gstRate: 1.1,
|
|
772
|
-
discountRate: 10,
|
|
773
|
-
story: {
|
|
774
|
-
price: 100
|
|
775
|
-
},
|
|
776
|
-
toDiscount: function(value) {
|
|
777
|
-
return Number(value) * this.discountRate;
|
|
778
|
-
},
|
|
779
|
-
addGst: function(value) {
|
|
780
|
-
return Number(value) * this.gstRate;
|
|
781
|
-
},
|
|
782
|
-
}
|
|
783
|
-
|
|
319
|
+
dataBind.init(element, viewModel, options?)
|
|
784
320
|
```
|
|
321
|
+
- `element`: Root DOM element
|
|
322
|
+
- `viewModel`: Plain JavaScript object
|
|
323
|
+
- `options`: `{ reactive: boolean, trackChanges: boolean }`
|
|
785
324
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
'Filter' is just simple function that recevie a value and return a value.
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
### $data, $root and $index
|
|
793
|
-
|
|
325
|
+
### Rendering
|
|
794
326
|
```javascript
|
|
795
|
-
|
|
796
|
-
<div data-bind-for="question of questions">
|
|
797
|
-
<label
|
|
798
|
-
data-bind-text="question.title"
|
|
799
|
-
data-bind-attr="getQuestionLabelAttr($data, $index)"
|
|
800
|
-
data-bind-css="$root.labelCss"
|
|
801
|
-
>
|
|
802
|
-
</label>
|
|
803
|
-
<input type="text" data-bind-attr="getQuestionInputAttr($data, $index)">
|
|
804
|
-
</div>
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
// js
|
|
809
|
-
const viewModel = {
|
|
810
|
-
labelCss: 'form-label',
|
|
811
|
-
questions: [{
|
|
812
|
-
title: 'How are you?',
|
|
813
|
-
fieldName: 'howAreYou',
|
|
814
|
-
}],
|
|
815
|
-
getQuestionLabelAttr: function(data, index, oldAttr, $el) {
|
|
816
|
-
return {
|
|
817
|
-
'for': `${data.fieldName}-${index}`,
|
|
818
|
-
};
|
|
819
|
-
},
|
|
820
|
-
getQuestionInputAttr: function(data, index, oldAttr, $el) {
|
|
821
|
-
return {
|
|
822
|
-
'name': `${data.fieldName}-${index}`,
|
|
823
|
-
'id': `${data.fieldName}-${index}`,
|
|
824
|
-
};
|
|
825
|
-
},
|
|
826
|
-
}
|
|
827
|
-
|
|
327
|
+
app.render(options?) // Returns Promise
|
|
828
328
|
```
|
|
829
329
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
`$root` is refer to the viewModel root level.
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
### One time binding
|
|
837
|
-
|
|
330
|
+
### Reactive Updates
|
|
838
331
|
```javascript
|
|
839
|
-
|
|
840
|
-
<div data-bind-if="renderIntro | once">
|
|
841
|
-
<h1>Introduction</h1>
|
|
842
|
-
</div>
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
// js
|
|
847
|
-
const viewModel = {
|
|
848
|
-
renderIntro: false
|
|
849
|
-
}
|
|
850
|
-
|
|
332
|
+
app.viewModel.property = value; // Automatic render in reactive mode
|
|
851
333
|
```
|
|
852
334
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
### Communicate between components
|
|
858
|
-
|
|
859
|
-
dataBind use pub/sub pattern to cross comminicate between components. In the [**bootstrap examples**](https://gogocat.github.io/dataBind/examples/bootstrap.html)
|
|
860
|
-
|
|
335
|
+
### Lifecycle Hooks
|
|
861
336
|
```javascript
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
viewModel
|
|
866
|
-
);
|
|
867
|
-
|
|
868
|
-
compSearchBar
|
|
869
|
-
.render()
|
|
870
|
-
.then(function(comp) {
|
|
871
|
-
let self = comp;
|
|
872
|
-
compSearchBar.subscribe('SEARCH-COMPLETED', self.viewModel.onSearchCompleted);
|
|
873
|
-
});
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
\\ compSearchResults.js
|
|
878
|
-
|
|
879
|
-
...
|
|
880
|
-
|
|
881
|
-
compSearchResults.publish('SEARCH-COMPLETED', data);
|
|
882
|
-
|
|
883
|
-
..
|
|
884
|
-
|
|
337
|
+
app.afterRender(callback) // Called after each render
|
|
338
|
+
app.removeAfterRender(callback) // Remove specific callback
|
|
339
|
+
app.clearAfterRender() // Remove all callbacks
|
|
885
340
|
```
|
|
886
341
|
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
-
|
|
908
|
-
|
|
909
|
-
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
342
|
+
### Events
|
|
343
|
+
```javascript
|
|
344
|
+
app.subscribe(event, handler)
|
|
345
|
+
app.subscribeOnce(event, handler)
|
|
346
|
+
app.publish(event, data)
|
|
347
|
+
app.unsubscribe(event)
|
|
348
|
+
app.unsubscribeAll()
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Complete Binding Reference
|
|
352
|
+
|
|
353
|
+
| Binding | Purpose | Example |
|
|
354
|
+
|---------|---------|---------|
|
|
355
|
+
| `data-bind-text` | Display text content | `<p data-bind-text="message"></p>` |
|
|
356
|
+
| `data-bind-click` | Click event | `<button data-bind-click="handleClick"></button>` |
|
|
357
|
+
| `data-bind-change` | Change event (inputs) | `<input data-bind-change="onChange">` |
|
|
358
|
+
| `data-bind-model` | Two-way binding | `<input data-bind-model="username">` |
|
|
359
|
+
| `data-bind-if` | Conditional render | `<div data-bind-if="isVisible">` |
|
|
360
|
+
| `data-bind-show` | Conditional display | `<div data-bind-show="isVisible">` |
|
|
361
|
+
| `data-bind-for` | List rendering | `<li data-bind-for="item in items">` |
|
|
362
|
+
| `data-bind-css` | Dynamic classes | `<div data-bind-css="{ active: isActive }">` |
|
|
363
|
+
| `data-bind-attr` | Dynamic attributes | `<img data-bind-attr="getImageAttrs">` |
|
|
364
|
+
| `data-bind-tmp` | Template rendering | `<div data-bind-tmp="{id: 'tpl', data: 'user'}">` |
|
|
365
|
+
| `data-bind-switch` | Switch statement | `<div data-bind-switch="state">` |
|
|
366
|
+
| `data-bind-submit` | Form submit | `<form data-bind-submit="onSubmit">` |
|
|
367
|
+
| `data-bind-blur` | Blur event | `<input data-bind-blur="onBlur">` |
|
|
368
|
+
| `data-bind-focus` | Focus event | `<input data-bind-focus="onFocus">` |
|
|
369
|
+
| `data-bind-dblclick` | Double-click | `<div data-bind-dblclick="onDblClick">` |
|
|
370
|
+
| `data-bind-hover` | Hover in/out | `<div data-bind-hover="onHover">` |
|
|
371
|
+
|
|
372
|
+
## Browser Support
|
|
373
|
+
|
|
374
|
+
- Chrome (latest)
|
|
375
|
+
- Firefox (latest)
|
|
376
|
+
- Safari (latest)
|
|
377
|
+
- Edge (latest)
|
|
378
|
+
|
|
379
|
+
**Note:** Reactive mode requires Proxy support (IE11 not supported for reactive mode, but manual mode works).
|
|
380
|
+
|
|
381
|
+
## Documentation
|
|
382
|
+
|
|
383
|
+
- [Configuration Guide](./CONFIGURATION.md) - Global settings and options
|
|
384
|
+
- [Examples](./examples/) - Live examples and demos
|
|
385
|
+
- [API Reference](./docs/) - Complete API documentation
|
|
386
|
+
|
|
387
|
+
## Migration from Manual to Reactive Mode
|
|
388
|
+
|
|
389
|
+
**Before (Manual Mode):**
|
|
390
|
+
```javascript
|
|
391
|
+
viewModel.counter++;
|
|
392
|
+
app.render(); // Manual render call
|
|
923
393
|
```
|
|
924
394
|
|
|
925
|
-
**
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
Next time calling `render` method will then update the view according to the viewModel.
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
> The viewModel should has exact same data as the server side rendered version. So when later on calls `render` the content will update correctly.
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
*Currently rehydration for **if, forOf and switch** bindings are still work in progress.*
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
## What dataBind is good for
|
|
944
|
-
|
|
945
|
-
dataBind is designed for leaverage existing infrastructure.
|
|
946
|
-
|
|
947
|
-
It is good fit for web sites that is:
|
|
948
|
-
|
|
949
|
-
- has exisitng server side render technology eg. PHP, .Net, JSP etc
|
|
950
|
-
|
|
951
|
-
- quickly build something to test the market but maintainable and easy to unit test
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
### what not
|
|
956
|
-
|
|
957
|
-
- new project - Angular, Aurelia or React... may be a better choice
|
|
958
|
-
|
|
959
|
-
- dataBind is not an full stack soultion
|
|
960
|
-
|
|
961
|
-
- micro component base - dataBind's component concept is not aim to be as small as a `<p>` tag seen in some library
|
|
395
|
+
**After (Reactive Mode - Default):**
|
|
396
|
+
```javascript
|
|
397
|
+
app.viewModel.counter++; // Automatic render!
|
|
398
|
+
```
|
|
962
399
|
|
|
963
|
-
|
|
400
|
+
See [CONFIGURATION.md](./CONFIGURATION.md) for complete migration guide.
|
|
964
401
|
|
|
965
|
-
##
|
|
402
|
+
## Contributing
|
|
966
403
|
|
|
967
|
-
|
|
404
|
+
Contributions welcome! Please read our [contributing guidelines](./CONTRIBUTING.md) first.
|
|
968
405
|
|
|
969
|
-
|
|
406
|
+
## License
|
|
970
407
|
|
|
971
|
-
|
|
408
|
+
[MIT](./LICENSE.txt)
|
|
972
409
|
|
|
973
|
-
|
|
410
|
+
---
|
|
974
411
|
|
|
975
|
-
|
|
412
|
+
**Made with ❤️ by developers who love simplicity**
|