I created a widget that registers its own script like below
class MyWidget extends CWidget {
public function run(){
Yii::app()->clientScript->registerScript(__CLASS__, <<<JAVASCRIPT
var a = "Hello World!";
JAVASCRIPT
, CClientScript::POS_END);
}
}
And in the layout, I call the widget like this
<?php $this->widget('MyWidget');?>
<?php echo $content;?>
But in a view file, I need the variable declared by that widget.
<?php
Yii::app()->clientScript->registerScript('script', <<<JAVASCRIPT
alert(a);
JAVASCRIPT
, CClientScript::POS_END);
?>
Note that in both registerScript method I use POS_END as the script position since I intend to put all of the scripts (including the CoreScript e.g. jQuery, jQueryUI etc) after the <body>
tag.
The problem is that the rendered script will show the one from the view file and after that the one from the widget.
alert(a);
var a = "Hello World!";
As we can see, the above code won't work so I need to put the the second line above the first line.
Any idea on how to force the order? I'm okay with extending the CClientScript
(and creating a new registerScript
method) as long as all of the scripts will be rendered in the end position and I don't have to pull those inline Javascript codes above to a new package or file.
So I finally find a hack to do this. I extend a new
ClientScript
class and modify theregisterScript
method so it will accept another param$level
.Think about
$level
just likez-index
in CSS, except the greater the number of$level
is, the lower the position of the script will be.For example
Even though
script3
is declared afterscript2
, in the rendered script, it will show abovescript2
since the$level
value ofscript2
is greater thanscript3
's.Here's my solution's code. It's working like what I want although I'm not sure if the arranging method is optimized enough.
So I just need to put the class as the clientScript component in the config
I've contributed a patch to Yii to allow ordering (prepend, append, number)
you can find it here https://github.com/yiisoft/yii/pull/2263
if it did not get its place into you, then you can extend CClientScript to your own class to apply my changes
but in general properly written JS should work independent of order for example instead of defining some global scope vars you can assign them as properties to window (accessing a non-existing property does not throw an error in js)
Try register to HEAD
There is no need to extend CClienScript. A simple solution to this problem would be to create a $script array in your controller in your controller and add the view scripts to that variable. In your layout file You would register the scripts stored in your $scripts array after you have registered the ones you want to be rendered before it. e.g.:
in your controller
In your layout:
and in your view
Override the CClientScript class as @Petra said, here is the code I am using, after using @Petra code and didn't worked out for me.
and then, go to your config file, and in the components section edit the following: