Products

Two-way Data Binding

Two-way data binding is a technique that synchronizes a JavaScript variable with its corresponding HTML element value, allowing updates to flow automatically in both directions. LemonadeJS implements two-way data binding using the bind attribute declared using the : prefix or the lm- attribute as follow:

Examples

The examples below demonstrate using the bind attribute with different DOM elements.

Input Text Binding

The bind attribute establishes micro-events synchronising component properties with real-time input values.

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
function Input() {
    // Property to be tracked
    this.input = 'Reactive';
    // Update property
    const update = () => {
        this.input = 'New value';
    }
    // Template
    return render => render`<div>
        <p>${this.input}</p>
        <input type='text' :bind='self.input' />
        <input type='button' value='Update' onclick=${update} />
    </div>`
}

lemonade.render(Input, document.getElementById('root'));
</script>
</html>
import lemonade from 'lemonadejs';

export default function Input() {
    // Property to be tracked
    this.input = 'Reactive';
    // Update property
    const update = () => {
        this.input = 'New value';
    }
    // Template
    return render => render`<div>
        <p>${this.input}</p>
        <input type='text' lm-bind='self.input' />
        <input type='button' value='Update' onclick=${update} />
    </div>`
}

See this example on codesandbox

Checkbox Binding

Checkboxes work similarly, with their state synchronized to the associated component property.

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
function Checkbox() {
    // Default value
    this.email = true;
    // Component template
    return render => render`<div>
        <p>Value: ${this.email}</p>
        <label><input type='checkbox' :bind='self.email'/> Email is a valid option</label>
    </div>`;
}
lemonade.render(Checkbox, document.getElementById('root'));
</script>
</html>
import lemonade from 'lemonadejs';

export default function Checkbox() {
    // Default value
    this.email = true;
    // Component template
    return render => render`<div>
        <p>Value: ${this.email}</p>
        <label><input type='checkbox' :bind='self.email'/> Email is a valid option</label>
    </div>`;
}

See this example on codesandbox

Radio

When using radio buttons, bind the same component property to all radio elements to ensure it reflects the currently selected value.

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
function Radio() {
    // Default option
    this.favorite = 'Pears';

    return render => render`<div>
        <p>What is your favorite fruit?</p>
        <label>
            <input type='radio' name='favorite'
                value='Apples' :bind='${this.favorite}' />
            Apples
        </label>
        <label>
            <input type='radio' name='favorite'
                value='Pears' :bind='${this.favorite}' />
            Pears
        </label>
        <label>
            <input type='radio' name='favorite'
                value='Oranges' :bind='${this.favorite}' />
            Oranges
        </label>
        <p>
            <input type='button' onclick="${() => alert(this.favorite)}" value='Get' />
            <input type='button' onclick="${() => this.favorite = 'Oranges'}" value='Set (Oranges)' />
        </p>
    </div>`;
}
lemonade.render(Radio, document.getElementById('root'));
</script>
</html>
export default function Radio() {
    // Default option
    this.favorite = 'Pears';

    return render => render`<div>
        <p>What is your favorite fruit?</p>
        <label>
            <input type='radio' name='favorite'
                value='Apples' :bind='${this.favorite}' />
            Apples
        </label>
        <label>
            <input type='radio' name='favorite'
                value='Pears' :bind='${this.favorite}' />
            Pears
        </label>
        <label>
            <input type='radio' name='favorite'
                value='Oranges' :bind='${this.favorite}' />
            Oranges
        </label>
        <p>
            <input type='button' onclick="${() => alert(this.favorite)}" value='Get' />
            <input type='button' onclick="${() => this.favorite = 'Oranges'}" value='Set (Oranges)' />
        </p>
    </div>`;
}

See this example on codesandbox

Multiple Select

Multiple select elements use array-based for two-way data binding, where the bind attribute synchronizes the selected options with a component property.

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
function Multiple() {
    // Default Value
    this.options = ['John','George'];
    
    const update = () => {
        // Update value
        this.options = ['Ringo'];
    }

    return render => render`<div>
        <p>${this.options}</p>
        <select multiple='multiple' size='10' :bind='${this.options}'>
            <option>Paul</option>
            <option>Ringo</option>
            <option>John</option>
            <option>George</option>
        </select>
        <p><input type="button" onclick="${update}" value="Update" /></p>
    </div>`
}
lemonade.render(Multiple, document.getElementById('root'));
</script>
</html>
import lemonade from 'lemonadejs';

export default function Multiple() {
    // Default Value
    this.options = ['John','George'];

    const update = () => {
        // Update value
        this.options = ['Ringo'];
    }

    return render => render`<div>
        <p>${this.options}</p>
        <select multiple='multiple' size='10' :bind='${this.options}'>
            <option>Paul</option>
            <option>Ringo</option>
            <option>John</option>
            <option>George</option>
        </select>
        <p><input type="button" onclick="${update}" value="Update" /></p>
    </div>`
}

See this example on codesandbox

Content Editable

LemonadeJS will track changes and keep the this property value in sync with changes in an editable div and vice versa.

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
function Editor() {
    // Default value
    this.editor = 'Hello world';
    
    // Get the editor value
    const getValue = () => {
        console.log(this.editor)
    }

    return render => render`<div>
        <p>Content: ${this.editor}</p>
        <div contentEditable='true' class='input' :bind='self.editor'></div>
        <p><input type='button' onclick="${getValue}" value="Get value" /></p>
    </div>`;
}
lemonade.render(Editor, document.getElementById('root'));
</script>
</html>
import lemonade from 'lemonadejs';

export default function Editor() {
    // Default value
    this.editor = 'Hello world';

    // Get the editor value
    const getValue = () => {
        console.log(this.editor)
    }

    return render => render`<div>
        <p>Content: ${this.editor}</p>
        <div :bind='self.editor' contentEditable='true' style='border:1px solid black'></div>
        <p><input type='button' onclick="${getValue}" value="Get value" /></p>
    </div>`;
}

See this example on codesandbox

Two-Way Data Binding in Components

LemonadeJS allows two-way data binding between components by synchronizing a parent component's property with a child component's value. Changes in the child's value are automatically reflected in the parent's property.

Example

The example below demonstrates two-way data binding between components.

<html>
<script src="https://lemonadejs.com/v5/lemonade.js"></script>
<div id='root'></div>
<script>
function Test() {
    return render => render`<p>Component value: ${this.value}</p>`;
}

function Component() {
    // Default
    this.test = "Initial value";
    // Template
    return render => render`<div>
        <Test :bind="${this.test}" />
        <p>
            <input type='button' onclick="${() => alert(this.test) }" value="Get"  />
            <input type='button' onclick="${() => this.test = 'Test' }" value="Set" />
        </p>
    </div>`;
}
// Render
lemonade.setComponents({ Test });
// Render the component
lemonade.render(Component, document.getElementById('root'));
</script>
</html>
import lemonade from 'lemonadejs';

function Test() {
    return render => render`<p>Component value: ${this.value}</p>`;
}

// Register Component
lemonade.setComponents({ Test });

function Component() {
    // Default
    this.test = "Initial value";
    // Template
    return render => render`<div>
        <Test :bind="${this.test}" />
        <p>
            <input type='button' onclick="${() => alert(this.test) }" value="Get"  />
            <input type='button' onclick="${() => this.test = 'Test' }" value="Set" />
        </p>
    </div>`;
}

See this example on codesandbox

What is Two-Way Data Binding?

Two-way data binding is a technique that automatically synchronizes data between JavaScript variables and DOM elements, creating a real-time connection that updates in both directions. When a variable changes, the corresponding DOM element reflects the update automatically. Similarly, when a user interacts with the DOM element (e.g., typing in an input field), the linked variable is updated instantly.

This approach is commonly used in form handling, live search interfaces, and interactive data visualizations. By eliminating the need for manual DOM updates, two-way binding simplifies the development of responsive and dynamic user interfaces.

What's Next?

Learn more about how LemonadeJS Form Objects: a boilerplate to form values management.

Form Objects