Two-way binding

Two-way binding is a strategy to simplify the synchronization between a JavaScript reference and the HTML element value and vice versa. That means every change in the self property value reflects in the HTML element value, and every change in the HTML element value propagated back to the self.

The @bind attribute

The @bind attribute defines which self property should be bound to the value of the HTML element. It helps to synchronize the value from all different native HTML form elements and custom HTML elements. Examples of that will be available further in this chapter.


Two-way binding examples

The following examples show how to bind simple and more complex native HTML elements, such as multiple dropdowns, checkbox and much more.

Text input

The @bind creates a transparent event to keep the self property in sync with the value of the input text.
<html>
<script src="https://lemonadejs.net/v2/lemonade.js"></script>
<div id='root'></div>
<script>
let Input = function() {
    let self = {
        input: 'paul@beatles.com'
    }

    // Any change in the self.input will update the input and vice-versa.
    let template = `<>
        <h1>{{self.input}}</h1>
        <input type='text' @bind='self.input' />
        <input type='button' value='Update' onclick="self.input = 'New value'" />
        </>`

    return lemonade.element(template, self);
}

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

See this example on codesandbox



Checkboxes

The checkbox works similarly to the example above. The state of the checkbox and the value of the self property is bound.
<html>
<script src="https://lemonadejs.net/v2/lemonade.js"></script>
<div id='root'></div>
<script>
var Checkbox = (function() {
    var self = {};
    self.email = true;
    self.phone = true;

    var template = `<>
        <span>Email: {{self.email}}</span><br>
        <span>Phone: {{self.phone}}</span><br>
        <fieldset>
        <legend>Contact options</legend>
        <label><input type='checkbox' @bind='self.email' /> Email</label>
        <label><input type='checkbox' @bind='self.phone' /> Phone</label>
        </fieldset>
        </>
    `;
    return lemonade.element(template, self);
});
lemonade.render(Checkbox, document.getElementById('root'));
</script>
</html>

See this example on codesandbox

Example



Radio

On a radio HTML element the self attribute should be the same so, that self property holds the same value of the radio.
<html>
<script src="https://lemonadejs.net/v2/lemonade.js"></script>
<div id='root'></div>
<script>
var Radio = (function() {
    var self = {};
    self.favorite = 'Pears';
    var template = `
        <>
        <fieldset>
            <legend>Favorite fruit</legend>
            <label>
                <input type='radio' name='favorite'
                    value='Apples' @bind='self.favorite' />
                Apples
            </label>
            <label>
                <input type='radio' name='favorite'
                    value='Pears' @bind='self.favorite' />
                Pears
            </label>
            <label>
                <input type='radio' name='favorite'
                    value='Oranges' @bind='self.favorite' />
                Oranges
            </label>
        </fieldset>
        <br/>
        <input type='button' onclick="alert(self.favorite)" value='Get' />
        <input type='button' onclick="self.favorite = 'Oranges'"
            value='Set (Oranges)' />
        </>`;
    return lemonade.element(template, self);
});
lemonade.render(Radio, document.getElementById('root'));
</script>
</html>

See this example on codesandbox

Example



Multiple select

The multiple select has a different handler from other HTML elements. That is because a multiple select updates a array that holds the multiple value options.

<html>
<script src="https://lemonadejs.net/v2/lemonade.js"></script>
<div id='root'></div>
<script>
var Multiple = function() {
    var self = {};
    self.options = ['John','George'];

    var template = `
        <>
        <h1>{{self.options}}</h1>
        <select @bind='self.options' multiple='multiple' size='10'>
            <option>Paul</option>
            <option>Ringo<<option>
            <option>John</option>
            <option>George</option>
        </select><br/>
        <button onclick="self.options = ['Ringo'];">Update</button>
        </>`;

    return lemonade.element(template, self);
};
lemonade.render(Multiple, document.getElementById('root'));
</script>
</html>

See this example on codesandbox

Example



ContentEditable

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

<html>
<script src="https://lemonadejs.net/v2/lemonade.js"></script>
<div id='root'></div>
<script>
var Editable = function() {
    var self = {};
    self.editor = 'Hello world';

    var template = `
        <>
        <h1>Editor</h1>
        <div @bind='self.editor' contentEditable='true'
            style='border:1px solid black'></div><br/>
        <input type='button' onclick="alert(self.editor)" value="Get" />
        </>`;

    return lemonade.element(template, self);
};
lemonade.render(Editable, document.getElementById('root'));
</script>
</html>

See this example on codesandbox

Example



Two-way binding in custom elements

To use @bind with a non-native HTML form elements you must implement a val() as shown below.

Basic implementation

In the following example shown a basic custom DIV element that implements the method val() to get or set the value for the component.
<html>
<script src="https://lemonadejs.net/v2/lemonade.js"></script>
<div id='root'></div>
<script>
function Custom() {
    var self = {};

    // Create a non-native HTML form element
    self.create = function(element) {
        // DIV represents a custom form element, for example a JS calendar.
        element.val = function(v) {
            if (typeof (v) === 'undefined') {
                // Get value of your custom element
                return this.innerText;
            } else {
                // Set the value of the custom element
                this.innerText = v;
            }
        }

        // Set the initial value
        self.value = 'Hello world';
    }

    var template = `
        <>
        <div @bind='self.value' @ready='self.create(this)'></div><br/>
        <input type='button' onclick="alert(self.value)" value="Get" />
        <input type='button' onclick="self.value = 'Test'" value="Set" />
        </>`;

    return lemonade.element(template, self);
}
lemonade.render(Custom, document.getElementById('root'));
</script>
</html>

See this example on codesandbox

Example



Integration with custom third-party plugins

The following example shows a jSuites Tags Plugin integrated with LemonadeJS.
<html>
<script src="https://jsuites.net/v4/jsuites.js"></script>
<link rel="stylesheet" href="https://jsuites.net/v4/jsuites.css" type="text/css" />

<script src="https://lemonadejs.net/v2/lemonade.js"></script>
<div id='root'></div>
<script>
var Keywords = (function() {
    var self = {};

    // Render component
    self.create = function(o) {
        jSuites.tags(o);

        // List of keywords
        self.keywords = 'Oranges,pears';
    }

    // Component template
    var template = `<>
            <div @bind='self.keywords' @ready='self.create(this)'></div>
            <div>Keywords: {{self.keywords}}</div>
        </>`;

    return lemonade.element(template, self);
});

lemonade.render(Keywords, document.getElementById('root'));
</script>
</html>

See this example on codesandbox

The tags plugin component implements val() to integrate with LemonadeJS @bind.



You might notice, in the example above, the usage of another of the native lemonade special HTML attribute: @ready. That is a method is called when the element is created and appended to the DOM.

More about other components that can be integrated with LemonadeJS, visit the jsuites javascript plugins website.


Next chapter: Dealing with arrays and loops