Yii2 Pjax and ActiveForm beforeSubmit not working after reload?

元气小坏坏 提交于 2019-12-01 20:25:25

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.

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.

I register my own javascript function to handle the redirect like below:

$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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!