yiisoft / yii2

Yii 2: The Fast, Secure and Professional PHP Framework
http://www.yiiframework.com
BSD 3-Clause "New" or "Revised" License
14.24k stars 6.9k forks source link

Exclude assets from rendering #4522

Closed RomeroMsk closed 9 years ago

RomeroMsk commented 10 years ago

Hello. I have this case: On page1 there is Bootstrap Modal which content is loading via ajax from page2. To not include header and footer into page2 I'm using renderAjax in page2 controller. But I need to exclude some assets which are loading automatically: for example, Bootstrap offset. I have two reasons for this:

  1. These assets are already loaded in parent document (page1) and I don't need unnecessary network requests for these CSS and JS files.
  2. On page1 I'm using my own css file which overrides some Bootstrap styles. And when modal content is loaded, styles of page1 are overriding by bootstrap.css.

So I'm looking for a way to exclude specified assets from rendering (by renderAjax, as minimum).

P.S. renderPartial is not a solution, beacuse in case of using it inline JS registered by widgets are missing.

kartik-v commented 10 years ago

I suppose it could be better if we could have an option when using renderAjax to specifically isolate the following:

Then we could control registration of asset bundles separately and inline scripts/additional files separately in renderAjax... in such complex view rendering process.

dynasource commented 10 years ago

you can unregister assets by using ie.:


yii\web\View Object
(
    [assetBundles] => Array
        (
        )
)

-->
unset($this->assetBundles['yii\web\JqueryAsset']);
RomeroMsk commented 10 years ago

Where? In controller and view itself assetBundles is not filled yet. All assets will be defined and filled only inside rendering methods.

dynasource commented 10 years ago

by using a ajax-layout for the renderAjax

dynasource commented 10 years ago

I guess you could also try to use Events for this matter. Perhabs View::EVENT_AFTER_RENDER and then use \Yii::$app->view

RomeroMsk commented 10 years ago

@dynasource yes, your suggestion about ajax layout is working. I'm calling render instead of renderAjax and using special layout with such code:

<?php
unset($this->assetBundles['yii\bootstrap\BootstrapAsset']);
unset($this->assetBundles['yii\web\JqueryAsset']);
unset($this->assetBundles['yii\web\YiiAsset']);
unset($this->assetBundles['kartik\widgets\Select2Asset']);
unset($this->assetBundles['kartik\widgets\WidgetAsset']);
?>
<?php $this->beginPage() ?>
<?php $this->head() ?>
<?php $this->beginBody() ?>
<?= $content ?>
<?php $this->endBody() ?>
<?php $this->endPage() ?>

(as universal solution I can pass an array of bundles to unset from controller action). Thanks! But Kartik's idea is much better than this workaround. Let's wait for Yii devs to look on this.

dynasource commented 10 years ago

Youre welcome. I do think passing data around would make the framework complex. The View component is accessible in the application which would enable one to unset assetBundles easily with ie. Events.

kartik-v commented 10 years ago

@RomeroMsk yes its register - corrected it.

RomeroMsk commented 10 years ago

@dynasource if you try to access assetBundles from controller action before rendering you'll see it's empty (because all assets will be included inside rendering method). I didn't try event (honestly, I don't know how to attach event listener to view from controller before rendering it by view name), but I think it also won't work.

dynasource commented 10 years ago

Romero, I didnt say that before rendering worked. EVENT_AFTER_RENDER is the one you need. To help you out with an example. Put this anywhere in your code:

\yii\base\Event::on(\yii\web\View::className(), \yii\web\View::EVENT_AFTER_RENDER, function ($e) {
  $e->sender->assetBundles = [];
});            
RomeroMsk commented 10 years ago

@dynasource it's magic :) I used your example and it's working. Now I don't need special layout, I'm just attaching event listener with unsets and calling renderAjax. Thank you again, this is the way I've looking for! I leave this issue opened to see devs comments about this case.

alex-code commented 10 years ago

If your ajax response has some <script src=""></script> tags that already exist on the page Yii should stop them from being requested again. Unfortunately it doesn't work for CSS.

dynasource commented 10 years ago

true. Currently, I am using a custom implementation to prevent 'similar' scripts being included. To give you a direction:

    var jsFiles = <?php echo \yii\helpers\Json::encode(($this->context->jsFiles[View::POS_END]))?>;
    var jsFilesDone = $('script[src]');   
    $.each( jsFiles, function( url,script ) {    
        var matchedScripts = jsFilesDone.filter(function () {
            return this.src === 'http://' + document.location.host + url;
        })    
        if (!matchedScripts.length) jsFilesTodo.push(url); 
    });
ghost commented 8 years ago

Insert this code before render )) Yii::$app->assetManager->bundles = [ 'yii\bootstrap\BootstrapPluginAsset' => false, 'yii\bootstrap\BootstrapAsset' => false, 'yii\web\JqueryAsset' => false, ];

VladimirBazhenov commented 1 year ago

Romero, I didnt say that before rendering worked. EVENT_AFTER_RENDER is the one you need. To help you out with an example. Put this anywhere in your code:

\yii\base\Event::on(\yii\web\View::className(), \yii\web\View::EVENT_AFTER_RENDER, function ($e) {
  $e->sender->assetBundles = [];
});            

This works! Thank you!