Skip to content

Commit

Permalink
feat: Add efficient conditional updates for res-conditional
Browse files Browse the repository at this point in the history
- Implemented efficient `res-conditional` updates by enhancing data proxies.
- Scoped conditional evaluation to the relevant data fields only.
- Added error logging for better debugging.
- Updated README with detailed documentation and usage examples.
  • Loading branch information
amurgola committed May 11, 2024
1 parent e18e199 commit 85b2254
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 89 deletions.
137 changes: 70 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Resonant.js is an open-source lightweight JavaScript framework that enables reac
- **Reactive Data Binding**: Automatically synchronize your data with the UI.
- **Dynamic List Rendering**: Easily render lists that react to data changes.
- **Bidirectional Input Binding**: Bind HTML input fields directly to your data model.
- **Efficient Conditional Updates**: Only evaluate conditional expressions tied to specific variable changes.
- **Lightweight and Easy to Integrate**: Minimal setup required to get started.
- **Compatible with Modern Browsers**: Works seamlessly across all modern web browsers.

Expand Down Expand Up @@ -39,84 +40,86 @@ Include resonant.js in your HTML file, and use the following example to understa
<script src="https://unpkg.com/resonantjs@latest/resonant.js"></script>
</head>
<body>
<h1>Resonant.js Quick Demo</h1>

<!-- Display and update a single item -->
<div>
<h2>Counter</h2>
<p>Current count: <span res="counter"></span></p>
<button onclick="incrementCounter()">Increment Counter</button>
</div>

<!-- Demonstrate object property binding -->
<div>
<h2>Person Information</h2>
<div res="person">
<span res-prop="firstname"></span>
<span res-prop="lastname"></span>
<br/><br/>
First Name: <input type="text" res-prop="firstname">
Last Name: <input type="text" res-prop="lastname">
</div>
<h1>Resonant.js Quick Demo</h1>

<!-- Display and update a single item -->
<div>
<h2>Counter</h2>
<p>
Current count: <span res="counter"></span>
</p>
<div res-conditional="counter >= 5">
Only shows when counter is greater than or equal to 5
</div>

<!-- Demonstrate dynamic list rendering -->
<div>
<h2>Team Members</h2>
<ul res="team">
<li>
<span res-prop="name"></span> - <span res-prop="role"></span>
</li>
</ul>
<button onclick="addTeamMember()">Add Team Member</button>
<button onclick="counter++">Increment Counter</button>
</div>

<!-- Demonstrate object property binding -->
<div>
<h2>Person Information</h2>
<div res="person">
<span res-prop="firstname"></span>
<span res-prop="lastname"></span>
<br/>
<div res-conditional="person.firstname == 'Andrew' && person.lastname == 'Murgola'">
Only shows when firstname is Andrew and lastname is Murgola
</div>
<br/>

First Name: <input type="text" res-prop="firstname" />
Last Name: <input type="text" res-prop="lastname" />
</div>

<script>
const resonantJs = new Resonant();

// Initialize a counter
resonantJs.add("counter", 0);

// Initialize a single person object
resonantJs.add("person", {
firstname: "Andy",
lastname: "Murgola"
});

// Example of a callback
resonantJs.addCallback("person", exampleCallbackOutput);

// Initialize a list of people with dynamic properties
resonantJs.add("team", [
{ name: "Alice", role: "Developer" },
{ name: "Bob", role: "Designer" }
]);

function incrementCounter() {
counter++;
}

function addTeamMember() {
const newMember = { name: "Charlie", role: "Product Manager" };
team.push(newMember);
}

function exampleCallbackOutput(result) {
console.log(result.firstname + " " + result.lastname);
}

</script>
</div>

<!-- Demonstrate dynamic list rendering -->
<div>
<h2>Team Members</h2>
<ul res="team">
<li>
<span res-prop="name"></span> - <span res-prop="role"></span>
</li>
</ul>
<button onclick="addTeamMember()">Add Team Member</button>
</div>

<script>
const resonantJs = new Resonant();

// Initialize a counter
resonantJs.add("counter", 0);

// Initialize a single object
resonantJs.add("person", {
firstname: "Andrew",
lastname: "Murgola"
});

// Initialize an array of objects
resonantJs.add("team", [
{ name: "Alice", role: "Developer" },
{ name: "Bob", role: "Designer" }
]);

// Example of a callback
resonantJs.addCallback("person", (result) => {
console.log(result.firstname + " " + result.lastname);
});

function addTeamMember() {
const newMember = { name: "Charlie", role: "Product Manager" };
team.push(newMember);
}
</script>
</body>
</html>

```
## Features Overview

### Core Concepts
- **`res` and `res-prop` Attributes**: Bind HTML elements to your data model seamlessly.
- `res` is used to identify an overarching data model.
- `res-prop` links individual properties within that model to corresponding UI elements.

- **`res-conditional` Attribute**: Conditionally display elements based on the data model's properties.
- **Automatic UI Updates**: Changes to your JavaScript objects instantly reflect in the associated UI components, reducing manual DOM manipulation.

### Advanced Features
Expand Down
32 changes: 14 additions & 18 deletions example.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<title>Resonant.js Quick Demo</title>
<script src="https://unpkg.com/resonantjs@latest/resonant.js"></script>
<script src="resonant.js"></script>
</head>
<body>
<h1>Resonant.js Quick Demo</h1>
Expand All @@ -13,7 +13,10 @@ <h2>Counter</h2>
<p>
Current count: <span res="counter"></span>
</p>
<button onclick="incrementCounter()">Increment Counter</button>
<div res-conditional="counter >= 5">
Only shows when counter is greater than or equal to 5
</div>
<button onclick="counter++">Increment Counter</button>
</div>

<!-- Demonstrate object property binding -->
Expand All @@ -23,6 +26,9 @@ <h2>Person Information</h2>
<span res-prop="firstname"></span>
<span res-prop="lastname"></span>
<br/>
<div res-conditional="person.firstname == 'Andrew' && person.lastname == 'Murgola'">
Only shows when firstname is Andrew and lastname is Murgola
</div>
<br/>

First Name: <input type="text" res-prop="firstname" />
Expand All @@ -47,32 +53,22 @@ <h2>Team Members</h2>
// Initialize a counter
resonantJs.add("counter", 0);

function exampleCallbackOutput(result) {
console.log(result.firstname + " " + result.lastname);
}

// Initialize a single person object
// Initialize a single object
resonantJs.add("person", {
firstname: "Andy",
firstname: "Andrew",
lastname: "Murgola"
});

// Initialize a list of people with dynamic properties
// Initialize an array of objects
resonantJs.add("team", [
{ name: "Alice", role: "Developer" },
{ name: "Bob", role: "Designer" }
]);

// Example of a callback
resonantJs.addCallback("person", exampleCallbackOutput);

function incrementCounter() {
counter++;
}

function updatePersonInfo(prop, value) {
person[prop] = value;
}
resonantJs.addCallback("person", (result) => {
console.log(result.firstname + " " + result.lastname);
});

function addTeamMember() {
const newMember = { name: "Charlie", role: "Product Manager" };
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "resonantjs",
"version": "1.0.0",
"version": "1.0.1",
"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.",
"main": "resonant.js",
"repository": {
Expand Down
23 changes: 22 additions & 1 deletion resonant.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Resonant {
set: (target, property, value) => {
target[property] = value;
this.updateElement(parentName);
this.updateConditionalsFor(parentName);
return true;
}
});
Expand All @@ -41,14 +42,15 @@ class Resonant {
_createArray(variableName, arr) {
return new Proxy(arr, {
get: (target, index) => {
if (typeof target[index] === 'object') {
if (typeof target[index] === 'object' && !target[index][Symbol('isProxy')]) {
target[index] = this._createObject(`${variableName}[${index}]`, target[index]);
}
return target[index];
},
set: (target, index, value) => {
target[index] = value;
this.updateElement(variableName);
this.updateConditionalsFor(variableName);
return true;
}
});
Expand All @@ -60,6 +62,7 @@ class Resonant {
set: (newValue) => {
this._assignValueToData(variableName, newValue);
this.updateElement(variableName);
this.updateConditionalsFor(variableName);
}
});
}
Expand Down Expand Up @@ -91,11 +94,29 @@ class Resonant {
}
});

// Call the variable-specific condition update
this.updateConditionalsFor(variableName);

if (this.callbacks[variableName]) {
this.callbacks[variableName](value);
}
}

updateConditionalsFor(variableName) {
const conditionalElements = document.querySelectorAll(`[res-conditional*="${variableName}"]`);
conditionalElements.forEach(conditionalElement => {
const condition = conditionalElement.getAttribute('res-conditional');
try {
if (eval(condition)) {
conditionalElement.style.display = '';
} else {
conditionalElement.style.display = 'none';
}
} catch (e) {
console.error(`Error evaluating condition for ${variableName}: ${condition}`, e);
}
});
}

_renderArray(variableName, el) {
let template = el.cloneNode(true);
Expand Down

0 comments on commit 85b2254

Please sign in to comment.