resonantjs 1.0.1 → 1.0.2

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 CHANGED
@@ -10,7 +10,6 @@ Resonant.js is an open-source lightweight JavaScript framework that enables reac
10
10
  - **Efficient Conditional Updates**: Only evaluate conditional expressions tied to specific variable changes.
11
11
  - **Lightweight and Easy to Integrate**: Minimal setup required to get started.
12
12
  - **Compatible with Modern Browsers**: Works seamlessly across all modern web browsers.
13
-
14
13
  ## Installation
15
14
  ## NPM
16
15
  To install via NPM, use the following command:
@@ -32,87 +31,29 @@ To use via CDN, include the following URLs in your HTML file:
32
31
  ## Usage
33
32
  Include resonant.js in your HTML file, and use the following example to understand how to integrate it into your web application.
34
33
 
35
- ```javascript
34
+ ```html
36
35
  <!DOCTYPE html>
37
36
  <html lang="en">
38
37
  <head>
39
- <title>Resonant.js Quick Demo</title>
40
- <script src="https://unpkg.com/resonantjs@latest/resonant.js"></script>
38
+ <title>Resonant.js Basic Example</title>
39
+ <script src="https://unpkg.com/resonantjs@latest/resonant.js"></script>
41
40
  </head>
42
41
  <body>
43
- <h1>Resonant.js Quick Demo</h1>
44
-
45
- <!-- Display and update a single item -->
46
- <div>
47
- <h2>Counter</h2>
48
- <p>
49
- Current count: <span res="counter"></span>
50
- </p>
51
- <div res-conditional="counter >= 5">
52
- Only shows when counter is greater than or equal to 5
53
- </div>
54
- <button onclick="counter++">Increment Counter</button>
55
- </div>
56
-
57
- <!-- Demonstrate object property binding -->
58
- <div>
59
- <h2>Person Information</h2>
60
- <div res="person">
61
- <span res-prop="firstname"></span>
62
- <span res-prop="lastname"></span>
63
- <br/>
64
- <div res-conditional="person.firstname == 'Andrew' && person.lastname == 'Murgola'">
65
- Only shows when firstname is Andrew and lastname is Murgola
66
- </div>
67
- <br/>
68
-
69
- First Name: <input type="text" res-prop="firstname" />
70
- Last Name: <input type="text" res-prop="lastname" />
71
- </div>
72
- </div>
73
-
74
- <!-- Demonstrate dynamic list rendering -->
75
- <div>
76
- <h2>Team Members</h2>
77
- <ul res="team">
78
- <li>
79
- <span res-prop="name"></span> - <span res-prop="role"></span>
80
- </li>
81
- </ul>
82
- <button onclick="addTeamMember()">Add Team Member</button>
83
- </div>
84
-
85
- <script>
86
- const resonantJs = new Resonant();
87
-
88
- // Initialize a counter
89
- resonantJs.add("counter", 0);
90
-
91
- // Initialize a single object
92
- resonantJs.add("person", {
93
- firstname: "Andrew",
94
- lastname: "Murgola"
95
- });
96
-
97
- // Initialize an array of objects
98
- resonantJs.add("team", [
99
- { name: "Alice", role: "Developer" },
100
- { name: "Bob", role: "Designer" }
101
- ]);
102
-
103
- // Example of a callback
104
- resonantJs.addCallback("person", (result) => {
105
- console.log(result.firstname + " " + result.lastname);
106
- });
107
-
108
- function addTeamMember() {
109
- const newMember = { name: "Charlie", role: "Product Manager" };
110
- team.push(newMember);
111
- }
112
- </script>
42
+ <h1>Resonant.js Basic Example</h1>
43
+ <div>
44
+ <h2>Counter</h2>
45
+ <p>Current count: <span res="counter"></span></p>
46
+ <button onclick="counter++">Increment Counter</button>
47
+ </div>
48
+
49
+ <script>
50
+ const resonantJs = new Resonant();
51
+ resonantJs.add("counter", 0);
52
+ </script>
113
53
  </body>
114
54
  </html>
115
55
  ```
56
+
116
57
  ## Features Overview
117
58
 
118
59
  ### Core Concepts
@@ -120,6 +61,8 @@ Include resonant.js in your HTML file, and use the following example to understa
120
61
  - `res` is used to identify an overarching data model.
121
62
  - `res-prop` links individual properties within that model to corresponding UI elements.
122
63
  - **`res-conditional` Attribute**: Conditionally display elements based on the data model's properties.
64
+ - **`res-onclick` Attribute**: Triggers a function when an element is clicked, allowing for custom event handling.
65
+ - **`res-onclick-remove` Attribute**: Removes an item from an array when the associated element is clicked.
123
66
  - **Automatic UI Updates**: Changes to your JavaScript objects instantly reflect in the associated UI components, reducing manual DOM manipulation.
124
67
 
125
68
  ### Advanced Features
@@ -127,10 +70,80 @@ Include resonant.js in your HTML file, and use the following example to understa
127
70
  - **Event Callbacks**: Register custom functions to execute whenever your data model changes.
128
71
  - **Bidirectional Input Binding**: Bind form input fields directly to your data, making two-way synchronization simple.
129
72
 
130
- ### Example Applications
131
- - **Single-Page Applications**: Build dynamic and responsive single-page applications quickly.
132
- - **Admin Dashboards**: Create data-rich dashboards that reflect real-time changes in your database.
133
- - **Form-Based Applications**: Automate forms, surveys, and user profiles for seamless data entry.
73
+ ### New Features in Version 1.0.2
74
+
75
+ #### Pending Updates Mechanism
76
+ - Introduced to prevent redundant updates and ensure callbacks are only triggered once per update cycle, improving performance and user experience.
77
+
78
+ #### Callback Parameter Enhancement
79
+ - Callbacks now receive detailed parameters including the specific action taken (`added`, `modified`, `removed`), the item affected, and the previous value. This provides better context for handling updates.
80
+
81
+ #### Batched Updates for Object Properties
82
+ - Improved handling of object property updates to ensure changes are batched together, preventing multiple redundant callback triggers.
83
+
84
+ #### Refined Data Binding
85
+ - Enhanced data binding between model and view to ensure consistent synchronization without unnecessary updates.
86
+
87
+ ### Task Manager Example
88
+
89
+ Here is an example of a task manager application using Resonant.js:
90
+
91
+ ```html
92
+ <!DOCTYPE html>
93
+ <html lang="en">
94
+ <head>
95
+ <title>Resonant.js Task Manager Demo</title>
96
+ <script src="https://cdn.jsdelivr.net/npm/resonantjs@latest/dist/resonant.min.js"></script>
97
+ </head>
98
+ <body>
99
+ <h1>Resonant.js Task Manager Demo</h1>
100
+
101
+ <!-- Task Input -->
102
+ <div>
103
+ <h2>Add New Task</h2>
104
+ <input type="text" placeholder="Task Name" res="taskName" />
105
+ <button onclick="addTask()">Add Task</button>
106
+ </div>
107
+
108
+ <!-- Task List -->
109
+ <div>
110
+ <h2>Task List</h2>
111
+ <ul res="tasks">
112
+ <li>
113
+ <input type="checkbox" res-prop="done" />
114
+ <span res-prop="name"></span>
115
+ <button res-onclick-remove="name">Remove</button>
116
+ </li>
117
+ </ul>
118
+ </div>
119
+
120
+ <script>
121
+ const resonantJs = new Resonant();
122
+
123
+ // Initialize variables using a configuration object
124
+ resonantJs.addAll({
125
+ tasks: [
126
+ { name: "Task 1", done: false },
127
+ { name: "Task 2", done: true }
128
+ ],
129
+ taskName: ""
130
+ });
131
+
132
+ // Add a callback to log actions taken on tasks
133
+ resonantJs.addCallback("tasks", (tasks, task, action) => {
134
+ console.log(`Action taken: ${action} for ${task.name}`);
135
+ });
136
+
137
+ // Add a function to add a new task
138
+ function addTask() {
139
+ const newTask = { name: taskName, done: false };
140
+ tasks.push(newTask);
141
+ taskName = '';
142
+ }
143
+ </script>
144
+ </body>
145
+ </html>
146
+ ```
134
147
 
135
148
  ## Future Enhancements
136
149
 
@@ -0,0 +1,88 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Resonant.js Practical Demo</title>
5
+ <script src="../resonant.js"></script>
6
+ </head>
7
+ <body>
8
+ <h1>Resonant.js Practical Demo</h1>
9
+
10
+ <!-- Display and update a single item -->
11
+ <div>
12
+ <h2>Counter</h2>
13
+ <p>
14
+ Current count: <span res="counter"></span>
15
+ </p>
16
+ <div res-conditional="counter < 10">
17
+ Only shows when counter is less than 10
18
+ </div>
19
+ <div res-conditional="counter >= 10">
20
+ Only shows when counter is greater than or equal to 10
21
+ </div>
22
+ <button onclick="counter++">Increment Counter</button>
23
+ </div>
24
+
25
+ <!-- Demonstrate object property binding -->
26
+ <div>
27
+ <h2>User Information</h2>
28
+ <div res="user">
29
+ <span res-prop="firstname"></span>
30
+ <span res-prop="lastname"></span>
31
+ <br/>
32
+ <div res-conditional="user.firstname == 'John' && user.lastname == 'Doe'">
33
+ Only shows when firstname is John and lastname is Doe
34
+ </div>
35
+ <br/>
36
+ First Name: <input type="text" res-prop="firstname" />
37
+ Last Name: <input type="text" res-prop="lastname" />
38
+ </div>
39
+ </div>
40
+
41
+ <!-- Demonstrate dynamic list rendering -->
42
+ <div>
43
+ <h2>Project Members</h2>
44
+ <ul res="projectTeam">
45
+ <li>
46
+ <span res-prop="name"></span> - <span res-prop="role"></span>
47
+ </li>
48
+ </ul>
49
+ <button onclick="addProjectMember()">Add Project Member</button>
50
+ </div>
51
+
52
+ <script>
53
+ const resonantJs = new Resonant();
54
+
55
+ // Initialize a single object with add method
56
+ resonantJs.add("counter", 0);
57
+
58
+ // Initialize variables using a configuration object
59
+ resonantJs.addAll({
60
+ user: {
61
+ firstname: "John",
62
+ lastname: "Doe",
63
+ email: ""
64
+ },
65
+ projectTeam: [
66
+ { name: "Alice", role: "Developer" },
67
+ { name: "Bob", role: "Designer" }
68
+ ]
69
+ });
70
+
71
+ // Chain together callbacks
72
+ resonantJs.addCallback("user", (user) => {
73
+ console.log(`User updated: ${user.firstname} ${user.lastname}`);
74
+ // You can nest updates within callbacks
75
+ counter++;
76
+ });
77
+
78
+ resonantJs.addCallback("counter", (count) => {
79
+ console.log(`Counter updated: ${count}`);
80
+ });
81
+
82
+ function addProjectMember() {
83
+ const newMember = { name: "Charlie", role: "Product Manager" };
84
+ projectTeam.push(newMember);
85
+ }
86
+ </script>
87
+ </body>
88
+ </html>
@@ -0,0 +1,54 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <title>Resonant.js Task Manager Demo</title>
5
+ <script src="../resonant.js"></script>
6
+ </head>
7
+ <body>
8
+ <h1>Resonant.js Task Manager Demo</h1>
9
+
10
+ <!-- Task Input -->
11
+ <div>
12
+ <h2>Add New Task</h2>
13
+ <input type="text" placeholder="Task Name" res="taskName" />
14
+ <button onclick="addTask()">Add Task</button>
15
+ </div>
16
+
17
+ <!-- Task List -->
18
+ <div>
19
+ <h2>Task List</h2>
20
+ <ul res="tasks">
21
+ <li>
22
+ <input type="checkbox" res-prop="done" />
23
+ <span res-prop="name"></span>
24
+ <button res-onclick-remove="name">Remove</button>
25
+ </li>
26
+ </ul>
27
+ </div>
28
+
29
+ <script>
30
+ const resonantJs = new Resonant();
31
+
32
+ // Initialize variables using a configuration object
33
+ resonantJs.addAll({
34
+ tasks: [
35
+ { name: "Task 1", done: false },
36
+ { name: "Task 2", done: true }
37
+ ],
38
+ taskName: ""
39
+ });
40
+
41
+ // Add a callback to log actions taken on tasks
42
+ resonantJs.addCallback("tasks", (tasks, task, action) => {
43
+ console.log(`Action taken: ${action} for ${task.name}`);
44
+ });
45
+
46
+ // Add a function to add a new task
47
+ function addTask() {
48
+ const newTask = { name: taskName, done: false };
49
+ tasks.push(newTask);
50
+ taskName = '';
51
+ }
52
+ </script>
53
+ </body>
54
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "resonantjs",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "A lightweight JavaScript framework that enables reactive data-binding for building dynamic and responsive web applications. It simplifies creating interactive UIs by automatically updating the DOM when your data changes.",
5
5
  "main": "resonant.js",
6
6
  "repository": {
package/resonant.js CHANGED
@@ -2,21 +2,21 @@ class Resonant {
2
2
  constructor() {
3
3
  this.data = {};
4
4
  this.callbacks = {};
5
+ this.pendingUpdates = new Set();
5
6
  }
6
7
 
7
- add(variableName, ...values) {
8
- let value;
9
- if (values.length > 1) {
10
- value = values;
11
- } else {
12
- value = values[0];
13
- }
14
-
8
+ add(variableName, value) {
15
9
  this._assignValueToData(variableName, value);
16
10
  this._defineProperty(variableName);
17
11
  this.updateElement(variableName);
18
12
  }
19
13
 
14
+ addAll(config) {
15
+ Object.entries(config).forEach(([variableName, value]) => {
16
+ this.add(variableName, value);
17
+ });
18
+ }
19
+
20
20
  _assignValueToData(variableName, value) {
21
21
  if (Array.isArray(value)) {
22
22
  this.data[variableName] = this._createArray(variableName, value);
@@ -27,35 +27,65 @@ class Resonant {
27
27
  }
28
28
  }
29
29
 
30
- _createObject(parentName, obj) {
30
+ _createObject(variableName, obj) {
31
31
  obj[Symbol('isProxy')] = true;
32
32
  return new Proxy(obj, {
33
33
  set: (target, property, value) => {
34
- target[property] = value;
35
- this.updateElement(parentName);
36
- this.updateConditionalsFor(parentName);
34
+ if (target[property] !== value) {
35
+ const oldValue = target[property];
36
+ target[property] = value;
37
+ this._queueUpdate(variableName, 'modified', target, property, oldValue);
38
+ }
37
39
  return true;
38
40
  }
39
41
  });
40
42
  }
41
43
 
42
44
  _createArray(variableName, arr) {
45
+ const self = this;
43
46
  return new Proxy(arr, {
44
- get: (target, index) => {
47
+ get(target, index) {
45
48
  if (typeof target[index] === 'object' && !target[index][Symbol('isProxy')]) {
46
- target[index] = this._createObject(`${variableName}[${index}]`, target[index]);
49
+ target[index] = self._createObject(`${variableName}[${index}]`, target[index]);
47
50
  }
48
51
  return target[index];
49
52
  },
50
- set: (target, index, value) => {
51
- target[index] = value;
52
- this.updateElement(variableName);
53
- this.updateConditionalsFor(variableName);
53
+ set(target, index, value) {
54
+ if (target[index] !== value) {
55
+ const action = target.hasOwnProperty(index) ? 'modified' : 'added';
56
+ const oldValue = target[index];
57
+ target[index] = value;
58
+ self._queueUpdate(variableName, action, target[index], index, oldValue);
59
+ }
60
+ return true;
61
+ },
62
+ deleteProperty(target, index) {
63
+ const oldValue = target[index];
64
+ target.splice(index, 1);
65
+ self._queueUpdate(variableName, 'removed', oldValue, index);
54
66
  return true;
55
67
  }
56
68
  });
57
69
  }
58
70
 
71
+ _queueUpdate(variableName, action, item, property, oldValue) {
72
+ if (!this.pendingUpdates.has(variableName)) {
73
+ this.pendingUpdates.add(variableName);
74
+ setTimeout(() => {
75
+ this.pendingUpdates.delete(variableName);
76
+ this._triggerCallbacks(variableName, action, item, property, oldValue);
77
+ this.updateElement(variableName);
78
+ this.updateConditionalsFor(variableName);
79
+ }, 0);
80
+ }
81
+ }
82
+
83
+ _triggerCallbacks(variableName, action, item, property, oldValue) {
84
+ if (this.callbacks[variableName]) {
85
+ this.callbacks[variableName].forEach(callback => callback(this.data[variableName], item, action, property, oldValue));
86
+ }
87
+ }
88
+
59
89
  _defineProperty(variableName) {
60
90
  Object.defineProperty(window, variableName, {
61
91
  get: () => this.data[variableName],
@@ -63,6 +93,9 @@ class Resonant {
63
93
  this._assignValueToData(variableName, newValue);
64
94
  this.updateElement(variableName);
65
95
  this.updateConditionalsFor(variableName);
96
+ if (!Array.isArray(newValue) && typeof newValue !== 'object') {
97
+ this._queueUpdate(variableName, 'modified', this.data[variableName]);
98
+ }
66
99
  }
67
100
  });
68
101
  }
@@ -72,20 +105,39 @@ class Resonant {
72
105
  const value = this.data[variableName];
73
106
 
74
107
  elements.forEach(element => {
75
- if (Array.isArray(value)) {
76
- element.querySelectorAll(`[res="${variableName}"]` && "[res-rendered=true]").forEach(el => el.remove());
108
+ if (element.tagName === 'INPUT' || element.tagName === 'TEXTAREA') {
109
+ if (!element.hasAttribute('data-resonant-bound')) {
110
+ element.value = value;
111
+ element.oninput = () => {
112
+ this.data[variableName] = element.value;
113
+ };
114
+ element.setAttribute('data-resonant-bound', 'true');
115
+ }
116
+ } else if (Array.isArray(value)) {
117
+ element.querySelectorAll(`[res="${variableName}"][res-rendered=true]`).forEach(el => el.remove());
77
118
  this._renderArray(variableName, element);
78
119
  } else if (typeof value === 'object') {
79
120
  const subElements = element.querySelectorAll(`[res-prop]`);
80
-
81
121
  subElements.forEach(subEl => {
82
122
  const key = subEl.getAttribute('res-prop');
83
123
  if (key && key in value) {
84
- if (subEl.tagName === 'INPUT' || subEl.tagName === 'TEXTAREA') {
85
- subEl.value = value[key];
86
- subEl.oninput = () => this.data[variableName][key] = subEl.value;
87
- } else {
88
- subEl.innerHTML = value[key];
124
+ if (!subEl.hasAttribute('data-resonant-bound')) {
125
+ if (subEl.tagName === 'INPUT' || subEl.tagName === 'TEXTAREA') {
126
+ if (subEl.type === 'checkbox') {
127
+ subEl.checked = value[key];
128
+ subEl.onchange = () => {
129
+ this.data[variableName][key] = subEl.checked;
130
+ };
131
+ } else {
132
+ subEl.value = value[key];
133
+ subEl.oninput = () => {
134
+ this.data[variableName][key] = subEl.value;
135
+ };
136
+ }
137
+ } else {
138
+ subEl.innerHTML = value[key];
139
+ }
140
+ subEl.setAttribute('data-resonant-bound', 'true');
89
141
  }
90
142
  }
91
143
  });
@@ -94,12 +146,7 @@ class Resonant {
94
146
  }
95
147
  });
96
148
 
97
- // Call the variable-specific condition update
98
149
  this.updateConditionalsFor(variableName);
99
-
100
- if (this.callbacks[variableName]) {
101
- this.callbacks[variableName](value);
102
- }
103
150
  }
104
151
 
105
152
  updateConditionalsFor(variableName) {
@@ -128,25 +175,72 @@ class Resonant {
128
175
  template = window[variableName + "_template"];
129
176
  }
130
177
 
131
- this.data[variableName].forEach((instance) => {
178
+ this.data[variableName].forEach((instance, index) => {
132
179
  const clonedEl = template.cloneNode(true);
180
+ clonedEl.setAttribute("res-index", index);
133
181
  for (let key in instance) {
134
182
  const subEl = clonedEl.querySelector(`[res-prop="${key}"]`);
135
183
  if (subEl) {
136
- if (subEl.tagName === 'INPUT' || subEl.tagName === 'TEXTAREA') {
137
- subEl.value = instance[key];
138
- subEl.oninput = () => instance[key] = subEl.value;
139
- } else {
140
- subEl.innerHTML = instance[key];
184
+ if (!subEl.hasAttribute('data-resonant-bound')) {
185
+ if (subEl.tagName === 'INPUT' || subEl.tagName === 'TEXTAREA') {
186
+ if (subEl.type === 'checkbox') {
187
+ subEl.checked = instance[key];
188
+ subEl.onchange = () => {
189
+ instance[key] = subEl.checked;
190
+ this._queueUpdate(variableName, 'modified', instance, key, instance[key]);
191
+ };
192
+ } else {
193
+ subEl.value = instance[key];
194
+ subEl.oninput = () => {
195
+ instance[key] = subEl.value;
196
+ this._queueUpdate(variableName, 'modified', instance, key, instance[key]);
197
+ };
198
+ }
199
+ } else {
200
+ subEl.innerHTML = instance[key];
201
+ }
202
+ subEl.setAttribute('data-resonant-bound', 'true');
141
203
  }
142
204
  }
143
205
  }
206
+
207
+ // Handle res-onclick
208
+ const onclickElements = clonedEl.querySelectorAll('[res-onclick], [res-onclick-remove]');
209
+ onclickElements.forEach(onclickEl => {
210
+ const functionName = onclickEl.getAttribute('res-onclick');
211
+ const removeKey = onclickEl.getAttribute('res-onclick-remove');
212
+
213
+ if (functionName) {
214
+ // Remove any existing event listeners to prevent duplicates
215
+ onclickEl.onclick = null;
216
+
217
+ onclickEl.onclick = () => {
218
+ const func = new Function('item', `return ${functionName}(item)`);
219
+ func(instance);
220
+ };
221
+ }
222
+
223
+ if (removeKey) {
224
+ onclickEl.onclick = null;
225
+
226
+ onclickEl.onclick = () => {
227
+ const index = this.data[variableName].findIndex(t => t[removeKey] === instance[removeKey]);
228
+ if (index !== -1) {
229
+ this.data[variableName].splice(index, 1);
230
+ }
231
+ };
232
+ }
233
+ });
234
+
144
235
  clonedEl.setAttribute("res-rendered", true);
145
236
  el.appendChild(clonedEl);
146
237
  });
147
238
  }
148
239
 
149
240
  addCallback(variableName, method) {
150
- this.callbacks[variableName] = method;
241
+ if (!this.callbacks[variableName]) {
242
+ this.callbacks[variableName] = [];
243
+ }
244
+ this.callbacks[variableName].push(method);
151
245
  }
152
246
  }
package/example.html DELETED
@@ -1,79 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <title>Resonant.js Quick Demo</title>
5
- <script src="resonant.js"></script>
6
- </head>
7
- <body>
8
- <h1>Resonant.js Quick Demo</h1>
9
-
10
- <!-- Display and update a single item -->
11
- <div>
12
- <h2>Counter</h2>
13
- <p>
14
- Current count: <span res="counter"></span>
15
- </p>
16
- <div res-conditional="counter >= 5">
17
- Only shows when counter is greater than or equal to 5
18
- </div>
19
- <button onclick="counter++">Increment Counter</button>
20
- </div>
21
-
22
- <!-- Demonstrate object property binding -->
23
- <div>
24
- <h2>Person Information</h2>
25
- <div res="person">
26
- <span res-prop="firstname"></span>
27
- <span res-prop="lastname"></span>
28
- <br/>
29
- <div res-conditional="person.firstname == 'Andrew' && person.lastname == 'Murgola'">
30
- Only shows when firstname is Andrew and lastname is Murgola
31
- </div>
32
- <br/>
33
-
34
- First Name: <input type="text" res-prop="firstname" />
35
- Last Name: <input type="text" res-prop="lastname" />
36
- </div>
37
- </div>
38
-
39
- <!-- Demonstrate dynamic list rendering -->
40
- <div>
41
- <h2>Team Members</h2>
42
- <ul res="team">
43
- <li>
44
- <span res-prop="name"></span> - <span res-prop="role"></span>
45
- </li>
46
- </ul>
47
- <button onclick="addTeamMember()">Add Team Member</button>
48
- </div>
49
-
50
- <script>
51
- const resonantJs = new Resonant();
52
-
53
- // Initialize a counter
54
- resonantJs.add("counter", 0);
55
-
56
- // Initialize a single object
57
- resonantJs.add("person", {
58
- firstname: "Andrew",
59
- lastname: "Murgola"
60
- });
61
-
62
- // Initialize an array of objects
63
- resonantJs.add("team", [
64
- { name: "Alice", role: "Developer" },
65
- { name: "Bob", role: "Designer" }
66
- ]);
67
-
68
- // Example of a callback
69
- resonantJs.addCallback("person", (result) => {
70
- console.log(result.firstname + " " + result.lastname);
71
- });
72
-
73
- function addTeamMember() {
74
- const newMember = { name: "Charlie", role: "Product Manager" };
75
- team.push(newMember);
76
- }
77
- </script>
78
- </body>
79
- </html>