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 the registerScript
method so it will accept another param $level
.
public function registerScript($id, $script, $position = self::POS_END, $level = 1);
Think about $level
just like z-index
in CSS, except the greater the number of $level
is, the lower the position of the script will be.
For example
Yii::app()->clientScript->registerScript('script1', '/** SCRIPT #1 **/', CClientScript::POS_END, 1);
Yii::app()->clientScript->registerScript('script2', '/** SCRIPT #2 **/', CClientScript::POS_END, 2);
Yii::app()->clientScript->registerScript('script3', '/** SCRIPT #3 **/', CClientScript::POS_END, 1);
Even though script3
is declared after script2
, in the rendered script, it will show above script2
since the $level
value of script2
is greater than script3
'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.
/**
* ClientScript manages Javascript and CSS.
*/
class ClientScript extends CClientScript {
public $scriptLevels = array();
/**
* Registers a piece of javascript code.
* @param string $id ID that uniquely identifies this piece of JavaScript code
* @param string $script the javascript code
* @param integer $position the position of the JavaScript code.
* @param integer $level the rendering priority of the JavaScript code in a position.
* @return CClientScript the CClientScript object itself (to support method chaining, available since version 1.1.5).
*/
public function registerScript($id, $script, $position = self::POS_END, $level = 1) {
$this->scriptLevels[$id] = $level;
return parent::registerScript($id, $script, $position);
}
/**
* Renders the registered scripts.
* Overriding from CClientScript.
* @param string $output the existing output that needs to be inserted with script tags
*/
public function render(&$output) {
if (!$this->hasScripts)
return;
$this->renderCoreScripts();
if (!empty($this->scriptMap))
$this->remapScripts();
$this->unifyScripts();
//===================================
//Arranging the priority
$this->rearrangeLevels();
//===================================
$this->renderHead($output);
if ($this->enableJavaScript) {
$this->renderBodyBegin($output);
$this->renderBodyEnd($output);
}
}
/**
* Rearrange the script levels.
*/
public function rearrangeLevels() {
$scriptLevels = $this->scriptLevels;
foreach ($this->scripts as $position => &$scripts) {
$newscripts = array();
$tempscript = array();
foreach ($scripts as $id => $script) {
$level = isset($scriptLevels[$id]) ? $scriptLevels[$id] : 1;
$tempscript[$level][$id] = $script;
}
foreach ($tempscript as $s) {
foreach ($s as $id => $script) {
$newscripts[$id] = $script;
}
}
$scripts = $newscripts;
}
}
}
So I just need to put the class as the clientScript component in the config
'components' => array(
'clientScript' => array(
'class' => 'ClientScript'
)
)
Try register to HEAD
Yii::app()->clientScript->registerScript(__CLASS__, <<<JAVASCRIPT
var a = "Hello World!";
JAVASCRIPT
, CClientScript::POS_HEAD);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With