Search code examples
javascriptes6-modulesecma

Binding class function to HTML DOM Element


I use Es6 class and reference the method in it in my DOM elements. And it has been easy to work with. When I convert the class to a module, the scope is limited. There are workarounds. But I am looking for an efficient way to add those class.method reference in my DOM element. Here is one that works.

es6\simpleForm.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body>
<main role="main" class="container">

    <div class="row">
        <div class="col-md-6 order-md-1 form_custom">

            <h4 class="mb-3">Simple Form</h4>
            <form name="simpleForm" method="POST">

                <div class="form-group">
                    <label class="font-weight-bold" for="firstName">First Name</label>
                    <div class="input-group">
                        <input type="text" class="form-control" id="firstName" name="firstName" placeholder="<first name>" required>
                        <span class="validity"></span>
                    </div>
                    <div class="small text-danger messages"></div>
                </div>
                <div class="form-group">
                    <label class="font-weight-bold" for="lastName">Last Name</label>
                    <div class="input-group">
                        <input type="text" class="form-control" id="lastName" name="lastName" placeholder="<last name>" required>
                        <span class="validity"></span>
                    </div>
                    <div class="small text-danger messages"></div>
                </div>

                <div class="form-group">
                    <label class="font-weight-bold" for="active">Status</label>
                    <span><i class="fa fa-server fa-fw"></i></span>

                    <select class="custom-select d-block w-100" name="active" id="active"
                            required>
                        <option value="true" selected="selected">Active</option>
                        <option value="false">Inactive</option>
                    </select>
                    <div class="small text-danger messages"></div>
                </div>

                <hr class="mb-4">
                <button type="button" class="btn btn-success" onclick="simpleService.clearForm(this.form)">New</button>
                <button type="button" class="btn btn-primary" onclick="simpleService.save(this.form)">Save</button>
                <button type="button" class="btn btn-primary" onclick="simpleService.remove(this.form)">Delete</button>
            </form>
        </div>
    </div>
</main>
<br>
<script type="text/javascript" src="/es6/simpleForm.js"></script>
<script type="text/javascript">
    var simpleService = new SimpleService("simpleForm");
    simpleService.test();

</script>
</body>
</html>

es6\simpleForm.js

class SimpleService {

    constructor(formName) {
        this.formName = formName;
        this.form = document.forms[formName];
    }

    clearForm() {
        this.form.reset();
    }

    save (form) {

        let caller = this;

        let data = new FormData();

        data.append("firstName", form.firstName.value);
        data.append("lastName", form.lastName.value);
        data.append("active", form.active.value);

        let xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function () {
            if (this.readyState == 4) {
                if (this.status == 200 || this.status == 201) {
                    alert('Record is saved');
                    caller.clearForm(form);
                } else {
                    //alert(this.responseText);
                    alert('Imagine the path exist and the record is saved');
                }
            }
        }
        xhttp.open("POST", "/saveSimpleData", true);
        xhttp.send(data);
    }

    remove (form) {

        let caller = this;
        let data = new FormData();

        if (form.firstName.value && form.lastName.value) {
            data.append("firstName", form.firstName.value);
            data.append("lastName", form.lastName.value);
        } else {
            alert("Cannot delete a non-existing record");
            return;
        }

        let xhttp = new XMLHttpRequest();

        xhttp.onreadystatechange = function () {
            if (this.readyState == 4) {
                if (this.status == 200 || this.status == 201) {
                    caller.clearForm(form);
                } else {
                    // alert(this.responseText);
                    alert('Imagine the path exist and the record is deleted');
                }
            }
        }
        xhttp.open("POST", "/deleteSimpleData", true);
        xhttp.send(data);
    }

    test() {
        console.log('Class Object is loaded');
    }
}

And when I modify to this it works. But binding like this for every DOM is painful. Any easy method?

es6module\simpleForm.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
    <meta content="utf-8" http-equiv="encoding">

    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>
<body>
<main role="main" class="container">

    <div class="row">
        <div class="col-md-6 order-md-1 form_custom">

            <h4 class="mb-3">Simple Form</h4>
            <form name="simpleForm" method="POST">

                <div class="form-group">
                    <label class="font-weight-bold" for="firstName">First Name</label>
                    <div class="input-group">
                        <input type="text" class="form-control" id="firstName" name="firstName" placeholder="<first name>" required>
                        <span class="validity"></span>
                    </div>
                    <div class="small text-danger messages"></div>
                </div>
                <div class="form-group">
                    <label class="font-weight-bold" for="lastName">Last Name</label>
                    <div class="input-group">
                        <input type="text" class="form-control" id="lastName" name="lastName" placeholder="<last name>" required>
                        <span class="validity"></span>
                    </div>
                    <div class="small text-danger messages"></div>
                </div>

                <div class="form-group">
                    <label class="font-weight-bold" for="active">Status</label>
                    <span><i class="fa fa-server fa-fw"></i></span>

                    <select class="custom-select d-block w-100" name="active" id="active"
                            required>
                        <option value="true" selected="selected">Active</option>
                        <option value="false">Inactive</option>
                    </select>
                    <div class="small text-danger messages"></div>
                </div>

                <hr class="mb-4">
                <!-- Prefer something similar to this
                <button type="button" class="btn btn-success" onclick="simpleService.clearForm(this.form)">New</button>
                <button type="button" class="btn btn-primary" onclick="simpleService.save(this.form)">Save</button>
                <button type="button" class="btn btn-primary" onclick="simpleService.remove(this.form)">Delete</button>
                -->

                <button id='cfButt' type="button" class="btn btn-success">New</button>
                <button id='saveButt' type="button" class="btn btn-primary">Save</button>
                <button id='delButt' type="button" class="btn btn-primary">Delete</button>
            </form>
        </div>
    </div>
</main>
<br>
<script type="module">
    import SimpleService from "./simpleForm.js";
    let simpleService = new SimpleService("simpleForm");
    simpleService.test();

    // Is there a way to bind these to the DOM like in ES6

    document.getElementById("cfButt").addEventListener('click', () => {
        simpleService.clearForm();
    });

    document.getElementById("saveButt").addEventListener('click', () => {
        simpleService.save();
    });

    document.getElementById("delButt").addEventListener('click', () => {
        simpleService.remove();
    });

</script>
</body>
</html>

es6module\simpleForm.js

class SimpleService {

    constructor(formName) {
        this.formName = formName;
        this.form = document.forms[formName];
    }

    clearForm() {
        this.form.reset();
    }

    save (form) {

        let caller = this;

        let data = new FormData();

        data.append("firstName", form.firstName.value);
        data.append("lastName", form.lastName.value);
        data.append("active", form.active.value);

        let xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function () {
            if (this.readyState == 4) {
                if (this.status == 200 || this.status == 201) {
                    alert('Record is saved');
                    caller.clearForm(form);
                } else {
                    //alert(this.responseText);
                    alert('Imagine the path exist and the record is saved');
                }
            }
        }
        xhttp.open("POST", "/saveSimpleData", true);
        xhttp.send(data);
    }

    remove (form) {

        let caller = this;
        let data = new FormData();

        if (form.firstName.value && form.lastName.value) {
            data.append("firstName", form.firstName.value);
            data.append("lastName", form.lastName.value);
        } else {
            alert("Cannot delete a non-existing record");
            return;
        }

        let xhttp = new XMLHttpRequest();

        xhttp.onreadystatechange = function () {
            if (this.readyState == 4) {
                if (this.status == 200 || this.status == 201) {
                    caller.clearForm(form);
                } else {
                    // alert(this.responseText);
                    alert('Imagine the path exist and the record is deleted');
                }
            }
        }
        xhttp.open("POST", "/deleteSimpleData", true);
        xhttp.send(data);
    }

    test() {
        console.log('Class Object is loaded');
    }
}

Any cleaner better method to bind the function to the DOM? Thanks


Solution

  • You can still use your original approach with the global variable and inline html attribute handlers if you prefer that style. You just need to explicitly create the global variable, you cannot use a let declaration in the module scope:

    <script type="module">
        import SimpleService from "./simpleForm.js";
        window.simpleService = new SimpleService("simpleForm");
        simpleService.test();
    </script>
    

    However, onclick="…" attributes are discouraged for good reasons, binding the handlers through the DOM is much cleaner. You can shorten the syntax a bit:

    <script type="module">
        import SimpleService from "./simpleForm.js";
        const simpleService = new SimpleService("simpleForm");
    
        document.getElementById("cfButt").onclick = e => simpleService.clearForm();
        document.getElementById("saveButt").onclick = e => simpleService.save();
        document.getElementById("delButt").onclick = e => simpleService.remove();
    
        simpleService.test();
    </script>