Yii2 Pjax 和 ActiveForm beforeSubmit 在重新加载后不起作用?
Yii2 Pjax and ActiveForm beforeSubmit not working after reload?
我正在我的提交按钮上创建一个加载指示器,并使用 registerJs 函数将 "start" 过程附加到 beforeSubmit 事件。
第一次可以正常使用,但重新加载Pjax容器后,再次提交表单时不会触发该事件。
我将整个视图文件封装在 Pjax 容器中,以便正确显示 flash 消息。
这是 main.php 布局文件片段:
<?php Pjax::begin([
'id' => 'pjax-container',
'enablePushState' => false,
]); ?>
<?= Alert::widget(); ?>
<?= $content; ?>
<?php Pjax::end(); ?>
这里是 registerJs 函数,位于 main.php 部分:
<?php
$js = <<< 'SCRIPT'
$('#form').on('beforeSubmit', function(){
alert('Submitted...');
//$("#spinner").fadeIn();
});
SCRIPT;
$this->registerJs($js);
?>
这里是活动表单:
<?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(); ?>
我正在使用 Kartik Spinner Widget (Details and Demo)
关于为什么在成功提交后 Pjax 加载数据容器后 beforeSubmit 事件不会触发的任何想法?
谢谢。
出现这个问题有几个原因。
首先,任何放在$(document).ready();
内并加载到ajax调用页面(包含页面)的代码在加载后将不会执行。
其次,任何对外部脚本 (<script src>
) 的引用都不能保证在内联脚本之前加载。在 Yii2 的情况下,这些内联脚本将使用 registerJs()
注册
让 Pjax 很好地处理复杂的脚本依赖页面来加载是不微不足道的。
解决您问题的几件事:
1) 使用 registerJs()
加载并设置位置 POS_READY
(默认?)的内联脚本将包含在 $(document).ready()
如果视图是通过 render()
.
呈现的
因此通过 renderAjax()
渲染很重要。这将确保内联脚本 运行.
有时(大多数时候)您需要在静态加载时使用 render()
,在 pjax 加载页面时使用 renderAjax()
。在您的实例中,这是将 Pjax 小部件放置在布局中所必需的。您可以针对这些情况使用以下代码:
if(Yii::$app->request->getHeaders()->has('X-PJAX'))
{
return $this->renderAjax('myview');
}
else
{
return $this->render('myview');
}
2) 在编写您知道将在 pjax 调用的上下文中加载的外部脚本时。使用:
$(document).on('ready pjax:success', function(){
// ...
});
这将正确加载脚本。但请记住,每次 Pjax 调用成功时,它都会 reload/rebind 脚本。不幸的是,这可能不是我们想要的。
另请记住,pjax:success
事件不一定会在包含页面的外部脚本加载后触发。参见 3)
3) 要解决脚本顺序问题,您需要从 pjax.js 脚本中删除以下行:
obj.scripts = findAll(obj.contents, 'script[src]').remove()
然而,这将导致设置为禁用 eval()
的浏览器出现问题(因此该行首先存在的原因)
Pjax 最终会对此进行排序。您可以跟踪进度here
如果有任何不清楚的地方,请告诉我,我会重新措辞。
更新: Yii 2.0.7 引入了变化。其中之一是将 bower\yii2-pjax
更新为 2.0.6
。这使得点 3) 不再必要,并且点 2) 不那么重要,尽管知道它仍然是一件好事。
所以。我有一个解决方案,但它与文档相矛盾...
如果您查看实际的重定向功能(来源):
public function redirect($url, $statusCode = 302, $checkAjax = true)
743 {
744 if (is_array($url) && isset($url[0])) {
745 // ensure the route is absolute
746 $url[0] = '/' . ltrim($url[0], '/');
747 }
748 $url = Url::to($url);
749 if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) {
750 $url = Yii::$app->getRequest()->getHostInfo() . $url;
751 }
752
753 if ($checkAjax) {
754 if (Yii::$app->getRequest()->getIsPjax()) {
755 $this->getHeaders()->set('X-Pjax-Url', $url);
756 } elseif (Yii::$app->getRequest()->getIsAjax()) {
757 $this->getHeaders()->set('X-Redirect', $url);
758 } else {
759 $this->getHeaders()->set('Location', $url);
760 }
761 } else {
762 $this->getHeaders()->set('Location', $url);
763 }
764
765 $this->setStatusCode($statusCode);
766
767 return $this;
768 }
您可以看到它在 Ajax 之前检查 Pjax,并且有两个不同的 header。在整个网络和多个站点中提到的 X-Redirect header 不适用于 Pjax 调用。您必须检查 X-Pjax-Url 是否存在 Pjax 调用。
我注册了自己的 javascript 函数来处理重定向,如下所示:
$js = <<< 'SCRIPT'
$(document).on('pjax:complete', function (event, xhr, textStatus, options) {
//$(document).ajaxComplete(function (event, xhr, settings) {
var url = xhr.getResponseHeader('X-Pjax-Url');
if (url) {
window.location = url;
}
});
SCRIPT;
$this->registerJs($js);
并且出于某种原因,这本身是不够的。默认情况下,source 函数运行 checkAjax 代码,但在我从重定向代码中明确调用它之前,它不会工作。
所以这是我在我的控制器中使用的:
Yii::$app->response->redirect(Yii::getAlias('@web') . '/secure/dashboard/', true)->send();
return;
这个问题已经讨论过很多很多次了,我希望这对其他人有所帮助。
我正在我的提交按钮上创建一个加载指示器,并使用 registerJs 函数将 "start" 过程附加到 beforeSubmit 事件。
第一次可以正常使用,但重新加载Pjax容器后,再次提交表单时不会触发该事件。
我将整个视图文件封装在 Pjax 容器中,以便正确显示 flash 消息。
这是 main.php 布局文件片段:
<?php Pjax::begin([
'id' => 'pjax-container',
'enablePushState' => false,
]); ?>
<?= Alert::widget(); ?>
<?= $content; ?>
<?php Pjax::end(); ?>
这里是 registerJs 函数,位于 main.php 部分:
<?php
$js = <<< 'SCRIPT'
$('#form').on('beforeSubmit', function(){
alert('Submitted...');
//$("#spinner").fadeIn();
});
SCRIPT;
$this->registerJs($js);
?>
这里是活动表单:
<?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(); ?>
我正在使用 Kartik Spinner Widget (Details and Demo)
关于为什么在成功提交后 Pjax 加载数据容器后 beforeSubmit 事件不会触发的任何想法?
谢谢。
出现这个问题有几个原因。
首先,任何放在$(document).ready();
内并加载到ajax调用页面(包含页面)的代码在加载后将不会执行。
其次,任何对外部脚本 (<script src>
) 的引用都不能保证在内联脚本之前加载。在 Yii2 的情况下,这些内联脚本将使用 registerJs()
让 Pjax 很好地处理复杂的脚本依赖页面来加载是不微不足道的。
解决您问题的几件事:
1) 使用 registerJs()
加载并设置位置 POS_READY
(默认?)的内联脚本将包含在 $(document).ready()
如果视图是通过 render()
.
因此通过 renderAjax()
渲染很重要。这将确保内联脚本 运行.
有时(大多数时候)您需要在静态加载时使用 render()
,在 pjax 加载页面时使用 renderAjax()
。在您的实例中,这是将 Pjax 小部件放置在布局中所必需的。您可以针对这些情况使用以下代码:
if(Yii::$app->request->getHeaders()->has('X-PJAX'))
{
return $this->renderAjax('myview');
}
else
{
return $this->render('myview');
}
2) 在编写您知道将在 pjax 调用的上下文中加载的外部脚本时。使用:
$(document).on('ready pjax:success', function(){
// ...
});
这将正确加载脚本。但请记住,每次 Pjax 调用成功时,它都会 reload/rebind 脚本。不幸的是,这可能不是我们想要的。
另请记住,pjax:success
事件不一定会在包含页面的外部脚本加载后触发。参见 3)
3) 要解决脚本顺序问题,您需要从 pjax.js 脚本中删除以下行:
obj.scripts = findAll(obj.contents, 'script[src]').remove()
然而,这将导致设置为禁用 eval()
的浏览器出现问题(因此该行首先存在的原因)
Pjax 最终会对此进行排序。您可以跟踪进度here
如果有任何不清楚的地方,请告诉我,我会重新措辞。
更新: Yii 2.0.7 引入了变化。其中之一是将 bower\yii2-pjax
更新为 2.0.6
。这使得点 3) 不再必要,并且点 2) 不那么重要,尽管知道它仍然是一件好事。
所以。我有一个解决方案,但它与文档相矛盾...
如果您查看实际的重定向功能(来源):
public function redirect($url, $statusCode = 302, $checkAjax = true)
743 {
744 if (is_array($url) && isset($url[0])) {
745 // ensure the route is absolute
746 $url[0] = '/' . ltrim($url[0], '/');
747 }
748 $url = Url::to($url);
749 if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) {
750 $url = Yii::$app->getRequest()->getHostInfo() . $url;
751 }
752
753 if ($checkAjax) {
754 if (Yii::$app->getRequest()->getIsPjax()) {
755 $this->getHeaders()->set('X-Pjax-Url', $url);
756 } elseif (Yii::$app->getRequest()->getIsAjax()) {
757 $this->getHeaders()->set('X-Redirect', $url);
758 } else {
759 $this->getHeaders()->set('Location', $url);
760 }
761 } else {
762 $this->getHeaders()->set('Location', $url);
763 }
764
765 $this->setStatusCode($statusCode);
766
767 return $this;
768 }
您可以看到它在 Ajax 之前检查 Pjax,并且有两个不同的 header。在整个网络和多个站点中提到的 X-Redirect header 不适用于 Pjax 调用。您必须检查 X-Pjax-Url 是否存在 Pjax 调用。
我注册了自己的 javascript 函数来处理重定向,如下所示:
$js = <<< 'SCRIPT'
$(document).on('pjax:complete', function (event, xhr, textStatus, options) {
//$(document).ajaxComplete(function (event, xhr, settings) {
var url = xhr.getResponseHeader('X-Pjax-Url');
if (url) {
window.location = url;
}
});
SCRIPT;
$this->registerJs($js);
并且出于某种原因,这本身是不够的。默认情况下,source 函数运行 checkAjax 代码,但在我从重定向代码中明确调用它之前,它不会工作。
所以这是我在我的控制器中使用的:
Yii::$app->response->redirect(Yii::getAlias('@web') . '/secure/dashboard/', true)->send();
return;
这个问题已经讨论过很多很多次了,我希望这对其他人有所帮助。