Yii2 Pjax and ActiveForm beforeSubmit not working after reload?

Mike Pearson picture Mike Pearson · Apr 8, 2015 · Viewed 11.1k times · Source

I am creating a loading indicator on my submit button and attaching the "start" procedure to the beforeSubmit event using the registerJs function.

It works properly the first time, but after the Pjax container is reloaded, the event won't fire when the form is submitted again.

I am enclosing the entire view file in the Pjax container so that the flash messages are properly displayed.

Here is the main.php layout file snippet:

<?php Pjax::begin([
'id' => 'pjax-container',
'enablePushState' => false,
]); ?>

<?= Alert::widget(); ?>

<?= $content; ?>

<?php Pjax::end(); ?>

And here is the registerJs function, located in main.php in the section:

<?php
$js = <<< 'SCRIPT'
$('#form').on('beforeSubmit', function(){
alert('Submitted...');
//$("#spinner").fadeIn();
});
SCRIPT;
$this->registerJs($js);
?>

And here is the activeForm:

<?php $form = ActiveForm::begin(['id' => 'form', 'options' => ['data-pjax' => true]]); ?>

<?= $form->field($model, 'name')->textInput(['type' => 'text']) ?>

<?= $form->field($model, 'email')->textInput(['type' => 'email']) ?>

<?= $form->field($model, 'subject')->textInput(['type' => 'text']) ?>

<?php
echo $form->field($model, 'message')->textArea(['rows' => 6]);

echo $form->field($model, 'verify_code')->widget(Captcha::className(), [
        'captchaAction' => 'site/captcha',
        'template' => '<div class="row"><div class="col-lg-4">{image}</div><div class="col-lg-6">{input}</div></div>',
]);
?>

<button class="btn btn-success">
<span id="spinner" style="display: none; float:left; margin-right: 26px;">
<?php
echo Spinner::widget([
'pluginOptions' => [
    'top' => '50%',
    'left' => '10px',
    'lines' => 11,
    'length' => 5,
    'width' => 3,
    'corners' => 1,
    'trail' => 100,
    'speed' => 1.25,
    'radius' => 4,
],
]);
?>
</span>
<?php echo $model->isNewRecord ? 'Send Message' : 'Update'; ?>
</button>

<?php ActiveForm::end(); ?>

I am using the Kartik Spinner Widget (Details and Demo)

Any ideas on why the beforeSubmit event won't fire after Pjax has loaded the data container after a successful for submit?

Thanks.

Answer

D.Mill picture D.Mill · Apr 9, 2015

This problem occurs for several reasons.

Firstly, Any code placed within $(document).ready(); and loaded on the ajax called page (contained page) will not be executed after it is loaded.

Secondly, any references to external scripts (<script src>) aren't guarantied to load before inline scripts. In the case of Yii2 these inline scripts would be registered with registerJs()

Getting Pjax to play nice with complex script dependent pages to load is not trivial.

A few things to solve your issues:

1) Inline scripts loaded with registerJs() and set with a position of POS_READY (default?) will be contained within a $(document).ready() if the view is rendered via render().

Thus it is important to render via renderAjax(). This will ensure that the inline scripts will run.

Sometimes (most times) you will need to use render() when statically loaded and renderAjax() when pjax loads a page. In your instance this is required in order to place the Pjax widget in the layout. You can use the following code for these situations:

if(Yii::$app->request->getHeaders()->has('X-PJAX'))
{
    return $this->renderAjax('myview');
}
else
{
    return $this->render('myview');
}

2) When writing external scripts that you know will load within the context of a pjax call. Use:

$(document).on('ready pjax:success', function(){
    // ...
});

This will load the script correctly. But keep in mind that it will reload/rebind the script every time a Pjax call succeeds. That might not be desired unfortunately.

Also keep in mind that the pjax:success event does not necessarily fire after the external scripts from the contained page are loaded. See 3)

3) To solve script order issues you will need to remove the following line from the pjax.js script:

obj.scripts = findAll(obj.contents, 'script[src]').remove() 

This will however create issues with browsers that are set to disable eval() (hence why the line exists in the first place)

Pjax will eventualy have this sorted. You can keep track of the progress here

If anything is unclear let me know and I'll rephrase.

UPDATE: Yii 2.0.7 has introduced changes. One of which is updating the bower\yii2-pjaxto 2.0.6. This makes point 3) no longer necessary and makes point 2) less important, although it's still a good thing to know.