Yii2: pjax in layout file clears view file's registered assets

Created on 29 Apr 2015  路  29Comments  路  Source: yiisoft/yii2

I have a pjax widget inside my layout file. The pjax widget calls \yii\web\View::clear() in its init. Unfortunately this also clears the view's registered assets, link tags etc., since the view is rendered before the layout.

pjax

Most helpful comment

we are planning to drop Pjax support with the next Yii2 release 2.1. Many of us know why. Pjax is a time crusher. When it works, its fantastic. But if it doesnt work, the evaporation of our time begins. Many hours of all of us have been wasted on debugging or by making a complex usecase work. It is simply not designed for complex usecases. And we all are ambitious. So we will continue to run into troubles, proven by our 200 subjects in our tracker https://github.com/yiisoft/yii2/issues?utf8=%E2%9C%93&q=pjax.

We are thinking about creating a solution as a complete Yii extension ourselves. We think that making our own extension will cost less time in the end, than to give support on the state of pjax. If there are people eager to contribute, please let us now.

All 29 comments

For my project I had to remove that line. Can someone explain to me the disadvantages of remove that line ?

When in pjax mode, the widget is supposed to only output the html content inside itself, that means only assets and meta tags registered inside the widget should be outputted. If you clear it, it means everything goes out. For example, if you register JQueryAsset in your layout file, your pjax page loads will include jquery.js (the js code will prevent the loading of any script files if they're already loaded though).

The problem is that the layout file is rendered after the view file. So the view file is inside the pjax widget (i.e. the assets registered by the view file should be included in the pjax output), but since it has already been rendered (it has registered its assets), the pjax widget will clear its assets.

After I remove yiiwebView::clear() my problem is this issue: #6917 . The 3rd party apps don't have is asset's to run.

Any news? Pjax is useless with that issue.

No, the problem is still unsolved and we don't have any good ideas how to fix it.
However, there is a

Workaround

Put all your assets to your main application asset and let the client download them on first page load. Ofc, it will eat much traffic on first load, but the problem will be solved.

@nkovacs could we do with CSS files the same, as we've done to JS files, ha? :)

The idea is don't call View::clear(), pass it to client side and handle with ajax prefiler. I guess it should word. What do you think?

Yes, the ajax prefilter can also be extended to css files (and it could handle updating title and meta tags, not sure how those are handled currently).
A proper fix would be for a view to commit its assets once it has finished rendering, so the pjax widget would not clear them, only uncommitted assets (e.g. if the pjax widget is in a view, all assets before it are not committed, so they'll be cleared).

Simple example with standard template. Let's say I want to wrap whole site into Pjax. So, I wrap the inner body of main layout. It works great if I navigate through Home page, About Us. It looks like a Single Page Application. Very cool! But when I come to Contact Us page I get a problem: pjax do not return inline scripts. So, validation do not work. I can still submit a form and validation will happen on the server-side, and pjax will return html with marked fields. So, in theory I could deal with that. But this is just one simple example, as I said. There could be more situations when inline script is required to be executed.

@cbepxpa3ym the use case you've provided is fixed in current dev version of https://github.com/yiisoft/jquery-pjax. See PR https://github.com/yiisoft/jquery-pjax/pull/30 for details
You can check it

Nope, and this is why: http://c2n.me/3toCnOh.png

Any ideas?

You're on 2.0.6 stable release, the behavior is changed in the dev-version and will be included in 2.0.7 stable release.

I used the jquery.pjax.js code from 2.0.7 to test, and provided that screenshot.
Did I do something wrong?

Did you clear the assets cache?

Well, for real that file cannot do anything with the problem. Because that problem happens here: yiiwebView::clear()

In Pjax.php is the line which removes the scripts: http://c2n.me/3tGA04v.png

You show the Pjax::init() method. When you register the JS inside the Pjax block, it will not be removed by pjax init.

Reproduce on Advanced App:

  1. Add Pjax inside div with "wrap" class of main.php: http://c2n.me/3tGCYrx.png
  2. Go to home page and open dev tools.
  3. Click Contact link in the header.
  4. View the source code of ajax request: http://c2n.me/3tGDoXP.png
  5. Compare to the page loaded in non-ajax mode: http://c2n.me/3tGDM08.png

Am I doing something unexpected?

So, I register scripts inside a pjax block. But they are still removed.

I'm assigned to this issue and will check it in details asap

@cbepxpa3ym You should use renderAjax() instead of render() or renderPartial().
the renderAjax() method will register all assets of Yii::$app->assetManager->bundles, yes, it also includes assets which were loaded in main layout.
See details in yii\web\View::renderAjax()
The render() or renderPartial() will not touch assets in view file rendering, but in layout file, so you will not see any links, tags or external scripts in rendered result if you use render() or renderPartial() in AJAX request (PJAX is also essentially AJAX).
But we should resolve the problem that how to avoid loading the assets which have been loaded.

I should not do anything. PJAX should do all for me. This why it was developed. It loads the same content as regular browser request, and then extracts the required content from there to update some specific part of the page.

Also, what you explained tells me you do not understand what we are talking about here. So, you can simply implement what you suggested into the pjax to see why you was wrong.

@cbepxpa3ym Do you really think I was wrong?
The following result is rendered by renderPartial() method:
qq 20160425161501
Obviously, the style sheets were not loaded.

then, the following result is rendered by renderAjax() method:
qq 20160425161636

If I did not do, I dare say it?

The correct way to use AJAX (also PJAX) is load contents only, not related to any styles, js or any other asset files. If you want to register js, css, or any other assets, please use renderAjax(), otherwise reload whole page.

No, it's not. pjax was designed to seamlessly ajax-ify a page, so you don't have to use renderAjax.
It also does load styles and js correctly (and is designed to do that), the only issue is that the Yii pjax widget clears the styles and js files, because it only wants to output the styles and js files that are registered inside the pjax widget.
This works fine if the pjax widget is in the view. It does not work when the widget is in the layout, because of the way Yii renders views and layouts.

Would this work? Having the layout render the view.

Your controller

$view = 'my-view-file-name';
$this->renderContent($view);

Your layout

//....
Pjax::begin()
echo $this->render($content);
Pjax::end();
//....

The layout would be loaded first then so the views scripts would come after Pjax::begin()

we are planning to drop Pjax support with the next Yii2 release 2.1. Many of us know why. Pjax is a time crusher. When it works, its fantastic. But if it doesnt work, the evaporation of our time begins. Many hours of all of us have been wasted on debugging or by making a complex usecase work. It is simply not designed for complex usecases. And we all are ambitious. So we will continue to run into troubles, proven by our 200 subjects in our tracker https://github.com/yiisoft/yii2/issues?utf8=%E2%9C%93&q=pjax.

We are thinking about creating a solution as a complete Yii extension ourselves. We think that making our own extension will cost less time in the end, than to give support on the state of pjax. If there are people eager to contribute, please let us now.

Was this page helpful?
0 / 5 - 0 ratings