简体   繁体   中英

JS Revealing Pattern event undefined issue

I am using the modular design pattern for JS and I keep running into issues when using arguments bound functions. I have a particular function that I would like to bind to different events to keep from having to write the function for each bound event. The only difference in the function, or the argument, is the table that will be updated. The problem is that when I build a function with the arguments I need and pass those arguments to bound events, I get an undefined error, in the console, on load. Keep in mind, I want to stick with this design pattern for the security it offers.

Here is my JS:

var Users = (function(){

    var $addRoleForm = $('#addUserRole');
    var $rolesTableBody = $('#table-roles tbody');

    $addRoleForm.submit(ajaxUpdate(event, $rolesTableBody));

    function ajaxUpdate(event, tableName) {
        event.preventDefault();
        event.stopPropagation();
        var url = this.action;
        var data = $(this).serialize();
        var $this = $(this);
        $.ajax({
            type: 'POST',
            url: url,
            dataType: 'json',
            data: data,
            success: function(data) {
                if(data.st === 0){
                    $messageContainer.html('<p class="alert alert-danger">' + data.msg + '</p>');
                    setTimeout(function(){
                        $messageContainer.hide();
                    }, 7000);
                } else {
                    $messageContainer.html('<p class="alert alert-success">' + data.msg + '</p>');
                    tableName.fadeOut().html('').html(data.build).fadeIn();
                    $this.find('input').val('');
                    setTimeout(function(){
                        $messageContainer.hide();
                    }, 7000);
                }
            },
            error: function(xhr, status, error){
                console.log(xhr.responseText);
            }
        });

    }
})();

Here is the error I get in the console, on load:

Uncaught TypeError: Cannot read property 'preventDefault' of undefined

I have tried to bind the event like this: $addRoleForm.on('submit', ajaxUpdate(event, $rolesTableBody)); and receive the same results.

Any ideas how to fix this?

You're seeing that issue, because the way you have it written now, ajaxUpdate executes, returns undefined and THEN passes undefined to the event listener, so you're basically doing this: $addRoleForm.submit(undefined) .

2 Choices here:

1) You can wrap it in an anonymous function:

$addRoleForm.submit(function(event) {
  //pass the value of "this" along using call
  ajaxUpdate.call(this, event, someValue);
});

$someOtherForm.submit(function(event) {
  //pass the value of "this" along using call
  ajaxUpdate.call(this, event, someOtherValue);
});

2) You can set the first argument in-advance using bind:

$addRoleForm.submit(ajaxUpdate.bind($addRoleForm, someValue));
$someOtherForm.submit(ajaxUpdate.bind($someOtherForm, someOtherValue));

Using this way, you're binding the value of this to be $addRoleForm , setting the first argument to always be someValue , so it's the same as:

ajaxUpdate(someValue, event) {
  //value of "this" will be $addRoleForm;
}

To pass the event, and the custom argument, you should be using an anonymous function call

$addRoleForm.submit(function(event) {
    ajaxUpdate(event, $rolesTableBody));
});

This is by far the easiest and most readable way to do this.


What you're doing right now equates to this

var $addRoleForm    = $('#addUserRole');
var $rolesTableBody = $('#table-roles tbody');

var resultFromCallingFunction = ajaxUpdate(event, $rolesTableBody); // undefined

$addRoleForm.submit(resultFromCallingFunction);

Where you're calling the ajaxUpdate function, as that's what the parentheses do, and pass the returned result back to the submit callback, which in your case is undefined , the default value a function returns when nothing else is specified.

You could reference the function, like this

$addRoleForm.submit(ajaxUpdate);

but then you can't pass the second argument

The question refers to the Revealing Module pattern. Benefit of using this design is readability. Going with the anon function may work, but defeats the overall purpose of the module pattern itself.

A good way to structure your module to help maintain your scope is to setup helper functions first, then call a return at the end.

Example use case with events:

var User = function() {

    // local VARS available to User
    var addRoleForm = document.querySelector('#addUserRole');
    var rolesTableBody = document.querySelector('#table-roles tbody');

    // Helper function 1
    function ajaxUpdate(tableName) {
         ...
    }

    // Helper function 2
    function someFunc() {
         ...
    }

    function bindEvents() {
        addRoleForm.addEventListener('submit', ajaxUpdate, false);
        addRoleForm.addEventListener('click', someFunc, false); 
    }

    function init() {
        bindEvents();
    }        

    return {
             runMe:init
           }

}().runMe();

Helps to "modularize" your workflow. You are also writing your revealing pattern as an IIFE. This can cause debugging headaches in the future. Editing the IIFE to instead invoke via the return is easier to maintain and for other devs to work with and learn initially. Also, it allows you to extend outside of your IFFE into another Module, example:

var Clothes = function() {

function anotherFunc() {
    ...
}

init() {
    User.runMe();
    anotherFunc();
}

return {
         addClothes: init
       }

}().addClothes();

I hope this helps to give you a better understanding of how/when/why to use the JS revealing pattern. Quick note: You can make your modules into IIFE, that's not a problem. You just limit the context of the scope you can work with. Another way of doing things would be to wrap the var User and var Clothes into a main module, and then make that an IIFE. This helps in preventing polluting your global namespace.

Example with what I wrote above:

// MAIN APPLICATION    
var GettinDressed = (function() {


    // MODULE ONE
    ///////////////////////////
    Var User = function() {

    // local VARS available to User
    var addRoleForm = document.querySelector('#addUserRole');
    var rolesTableBody = document.querySelector('#table-roles tbody');

    // Helper function 1
    function ajaxUpdate(tableName) {
         ...
    }

    // Helper function 2
    function someFunc() {
         ...
    }

    function bindEvents() {
        addRoleForm.addEventListener('submit', ajaxUpdate, false);
        addRoleForm.addEventListener('click', someFunc, false); 
    }

    function init() {
        bindEvents();
    }        

    return {
             runMe:init,
             style: someFunc
           }

    }();

    // MODULE TWO
    //////////////////////////
    var Clothes = function() {

    function anotherFunc() {
        ...
    }

    init() {
        User.style();
        anotherFunc();
    }

    return {
             dressUp: init
           }

    }();

  // Define order of instantiation
  User.runMe();
  Clothes.dressUp();
}());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM