JS Revealing Pattern事件未定义的问题

时间:2015-12-13 13:33:56

标签: javascript jquery design-patterns

我正在使用JS的模块化设计模式,并且在使用参数绑定函数时我一直遇到问题。我有一个特定的函数,我想绑定到不同的事件,以避免必须为每个绑定事件编写函数。函数或参数的唯一区别是要更新的表。问题是,当我使用我需要的参数构建一个函数并将这些参数传递给绑定事件时,我在控制台中,在加载时得到一个未定义的错误。请记住,我希望坚持使用这种设计模式来提供它所提供的安全性。

这是我的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);
            }
        });

    }
})();

以下是我在控制台中遇到的错误:

Uncaught TypeError: Cannot read property 'preventDefault' of undefined

我试图像这样绑定事件:$addRoleForm.on('submit', ajaxUpdate(event, $rolesTableBody));并收到相同的结果。

任何想法如何解决这个问题?

3 个答案:

答案 0 :(得分:2)

您已经看到了这个问题,因为您现在正在编写它的方式,ajaxUpdate执行,返回undefined,然后THEN将undefined传递给事件监听器,因此您&# 39;基本上这样做:$addRoleForm.submit(undefined)

2选择:

1)您可以将其包装在匿名函数中:

$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)您可以使用bind:

预先设置第一个参数
$addRoleForm.submit(ajaxUpdate.bind($addRoleForm, someValue));
$someOtherForm.submit(ajaxUpdate.bind($someOtherForm, someOtherValue));

使用这种方式,您将this的值绑定为$addRoleForm,将第一个参数设置为始终为someValue,所以它是相同的为:

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

答案 1 :(得分:1)

要传递事件和自定义参数,您应该使用匿名函数调用

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

这是迄今为止最简单,最易读的方式。

您现在正在做的事情等同于此

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

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

$addRoleForm.submit(resultFromCallingFunction);

您正在调用ajaxUpdate函数,就像括号所做的那样,并将返回的结果传递回提交回调,在您的情况下为undefined ,当没有指定其他内容时函数返回的默认值。

你可以像这样引用这个功能

$addRoleForm.submit(ajaxUpdate);

但是你不能通过第二个参数

答案 2 :(得分:1)

问题涉及Revealing Module模式。使用这种设计的好处是可读性。使用anon函数可能会起作用,但会破坏模块模式本身的整体目的。

构建模块以帮助维护范围的好方法是首先设置辅助函数,然后在最后调用返回函数。

带事件的示例用例:

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();

帮助“模块化”您的工作流程。你也将你的揭示模式写成IIFE。这可能会导致将来出现调试问题。编辑IIFE而不是通过返回调用更容易维护,并且其他开发人员最初可以使用和学习。此外,它允许您将IFFE扩展到另一个模块,例如:

var Clothes = function() {

function anotherFunc() {
    ...
}

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

return {
         addClothes: init
       }

}().addClothes();

我希望这有助于让您更好地了解如何/何时/为何使用JS揭示模式。快速说明:您可以将模块转换为IIFE,这不是问题。您只需限制可以使用的范围的上下文。另一种做法是将var User和var Clothes包装到一个主模块中,然后将其作为IIFE。这有助于防止污染您的全局命名空间。

我上面写的例子:

// 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();
}());