0

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

Shriram M.
  • 383
  • 1
  • 7
  • "Is there a way to bind these to the DOM like in ES6" <= can you elaborate on this? Because using `addEventListener` is the prefered way of making event bindings. Inline bindings are not. – Taplar Jul 28 '20 at 18:41
  • There are examples here (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind) where a function that can be bound. I think I am trying to get something that is declared in a module, become available in javascript or DOM, so that the code is simpler. – Shriram M. Jul 28 '20 at 19:16
  • `bind` is used to create a replica of a function, with the context changed. It doesn't have anything to do specifically with event listeners. *"The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called."* – Taplar Jul 28 '20 at 19:17
  • bind might be the wrong word I used. Apologies. The first way (using just class and an object in javascript) is easier to link to a DOM event. Whereas with the second way (using module), there is an extra step to be written for each of the DOM objects. Question is, is there an efficient way of writing this code. Thanks – Shriram M. Jul 28 '20 at 19:36
  • How many times are you having to create event bindings in your document? Because the example you have given, you are binding three times and each binding is unique. It's hard to see what is inefficient about that. – Taplar Jul 28 '20 at 19:40
  • I have about 50 forms in 50 different html pages similar to this. But only about 40 would have about 4 onclick each. There are a few onchange, onblur, scattered all over. The other difference is, you can see that it passes the value as a function parameter. With onchange events, it is usually the value of the DOM element and rarely another element's value. With module method, instead of passing those, I have to do a query. Those are the other changes I have to do. Thanks for your response. Looks like there is no other way to do it differently. If there is, please help. Thanks – Shriram M. Jul 28 '20 at 19:59
  • You have 50 forms on a single page? – Taplar Jul 28 '20 at 20:00
  • Your *es6module\simpleForm.js* should have `default export class SimpleService {` – Bergi Jul 28 '20 at 22:50

1 Answers1

1

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>
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you Bergi. This is very helpful. I agree with you, keeping all the 3 different types (html, js & css) separate and that's one of the reasons why I have modularized the java script code by html file. But this is one feature find a solution early and now difficult to part with, as one can see the html form and the js are bound together for screen related activities and for business activities using modules are better as that can be shared across. Good to know that there is a way to tie the object to the window object and use that way also. Thank you so much. – Shriram M. Jul 30 '20 at 02:54