jekahm jekahm - 3 months ago 91
Javascript Question

How to prevent multiple form submitting in Yii2?

In order to handle the form I'm using the following code (for test only):

$(document).on("beforeSubmit", "#test-form", function (event, messages) {
$(this).find(':submit').attr('disabled', true);
console.log('Test new form');
return false;
});


But, despite the fact I make a submit button as inactive, we can see in console, that form is submitting as minimum twice when I quickly click on the button. As a temp fix, wrote the following code:

$(document).on("beforeValidate", "form", function(event, messages, deferreds) {
$(this).find(':submit').attr('disabled', true);
console.log('BEFORE VALIDATE TEST');
}).on("afterValidate", "form", function(event, messages, errorAttributes) {
console.log('AFTER VALIDATE TEST');
if (errorAttributes.length > 0) {
$(this).find(':submit').attr('disabled', false);
}
});
$(document).on("beforeSubmit", "#test-form", function (event, messages) {
console.log('Test new form');
return false;
});


But not sure that it's a good decision or not. How to fix this problem?

Thanks in advance!

Answer

One solution is to disable the button using JavaScript. But this is not efficient all the times because of browsers issues or because a user might have disabled JavaScript at his broswer.

A different solution is to check each time on server side if the form has submitted using a token saved in session.

The following code can be found in https://github.com/yiisoft/yii2/issues/10498 :

Model

public function getHiddenFormTokenField() {
    $token = \Yii::$app->getSecurity()->generateRandomString();
    $token = str_replace('+', '.', base64_encode($token));

    \Yii::$app->session->set(\Yii::$app->params['form_token_param'], $token);;
    return Html::hiddenInput(\Yii::$app->params['form_token_param'], $token);
}

"When the form submit, function named 'beforeAction' in controller class compare sent token with value in session. The session is cleaned after every action executed. If the values are different, throws the Exception."

Controller

public function beforeAction($action) {
    $formTokenName = \Yii::$app->params['form_token_param'];

    if ($formTokenValue = \Yii::$app->request->post($formTokenName)) {
        $sessionTokenValue = \Yii::$app->session->get($formTokenName);

        if ($formTokenValue != $sessionTokenValue ) {
            throw new \yii\web\HttpException(400, 'The form token could not be verified.');
        }

        \Yii::$app->session->remove($formTokenName);
    }

    return parent::beforeAction($action);
}