javascript Yii2 Pjax 和 ActiveForm beforeSubmit 在重新加载后不起作用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29524980/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Yii2 Pjax and ActiveForm beforeSubmit not working after reload?
提问by Mike Pearson
I am creating a loading indicator on my submit button and attaching the "start" procedure to the beforeSubmit event using the registerJs function.
我正在我的提交按钮上创建一个加载指示器,并使用 registerJs 函数将“开始”过程附加到 beforeSubmit 事件。
It works properly the first time, but after the Pjax container is reloaded, the event won't fire when the form is submitted again.
第一次运行正常,但是在重新加载 Pjax 容器后,再次提交表单时不会触发该事件。
I am enclosing the entire view file in the Pjax container so that the flash messages are properly displayed.
我将整个视图文件包含在 Pjax 容器中,以便正确显示 Flash 消息。
Here is the main.php layout file snippet:
这是 main.php 布局文件片段:
<?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:
这是 registerJs 函数,位于 main.php 部分:
<?php
$js = <<< 'SCRIPT'
$('#form').on('beforeSubmit', function(){
alert('Submitted...');
//$("#spinner").fadeIn();
});
SCRIPT;
$this->registerJs($js);
?>
And here is the activeForm:
这是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)
我正在使用 Kartik Spinner 小部件(详细信息和演示)
Any ideas on why the beforeSubmit event won't fire after Pjax has loaded the data container after a successful for submit?
关于为什么在成功提交后 Pjax 加载数据容器后 beforeSubmit 事件不会触发的任何想法?
Thanks.
谢谢。
回答by D.Mill
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.
首先,$(document).ready();
在ajax调用的页面(包含页面)中放置和加载的任何代码在加载后都不会执行。
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()
其次,<script src>
不保证在内联脚本之前加载对外部脚本 ( ) 的任何引用。在 Yii2 的情况下,这些内联脚本将注册registerJs()
Getting Pjax to play nice with complex script dependent pages to load is nottrivial.
让 Pjax 与复杂的脚本相关页面一起正常加载并不是一件容易的事。
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()
.
1)装载有内联的脚本registerJs()
和用的位置设定POS_READY
(缺省值?)将内一个被包含$(document).ready()
如果视图是通过渲染render()
。
Thus it is important to render via renderAjax()
. This will ensure that the inline scripts will run.
因此,渲染 via 很重要renderAjax()
。这将确保内联脚本将运行。
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:
有时(大多数时候)您需要render()
在静态加载和renderAjax()
pjax 加载页面时使用。在您的实例中,这是将 Pjax 小部件放置在布局中所必需的。您可以在这些情况下使用以下代码:
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:
2)在编写您知道将在 pjax 调用的上下文中加载的外部脚本时。利用:
$(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.
这将正确加载脚本。但请记住,每次 Pjax 调用成功时,它都会重新加载/重新绑定脚本。不幸的是,这可能并不理想。
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)
另请记住,在pjax:success
加载包含页面的外部脚本后,不一定会触发该事件。见 3)
3)To solve script order issues you will need to remove the following line from the pjax.js script:
3)要解决脚本顺序问题,您需要从 pjax.js 脚本中删除以下行:
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)
但是,这会在设置为禁用的浏览器中产生问题eval()
(因此首先存在该行的原因)
Pjax will eventualy have this sorted. You can keep track of the progress here
Pjax 最终会对此进行排序。您可以在此处跟踪进度
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-pjax
to 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.
更新:Yii 2.0.7 引入了更改。其中之一是bower\yii2-pjax
将2.0.6
. 这使得第3)点不再必要,并使第2)点变得不那么重要,尽管知道它仍然是一件好事。
回答by Mike Pearson
So. I have a solution, but it contradicts the documentation...
所以。我有一个解决方案,但它与文档相矛盾...
If you look at the actual redirect function (source):
如果您查看实际的重定向功能(来源):
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 }
You can see that it checks for Pjax before Ajax and has two different headers as such. the X-Redirect header that is mentioned all over the web and multiple sites, does NOT work for a Pjax call. You must check for X-Pjax-Url for a Pjax call.
您可以看到它在 Ajax 之前检查 Pjax,并且具有两个不同的标头。在整个网络和多个站点上提到的 X-Redirect 标头不适用于 Pjax 调用。您必须检查 X-Pjax-Url 以获取 Pjax 调用。
I register my own javascript function to handle the redirect like below:
我注册了自己的 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);
And for some reason, this on its own wasn't enough. The source function, by default runs the checkAjax code, but until I explicitly called it from my redirect code, it wouldn't work.
出于某种原因,这本身是不够的。默认情况下,源函数运行 checkAjax 代码,但在我从重定向代码中明确调用它之前,它无法工作。
So here is what I use in my controller:
所以这是我在我的控制器中使用的:
Yii::$app->response->redirect(Yii::getAlias('@web') . '/secure/dashboard/', true)->send();
return;
This has been discussed many, many times and I hope this helps others.
这已经被讨论过很多次了,我希望这能帮助其他人。