Forcing Script Order from registerScript method in Yii

I created a widget that registers my own script, as shown 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 a widget like this

 <?php $this->widget('MyWidget');?> <?php echo $content;?> 

But in the view file I need a variable declared by these widgets.

 <?php Yii::app()->clientScript->registerScript('script', <<<JAVASCRIPT alert(a); JAVASCRIPT , CClientScript::POS_END); ?> 

Note that in both registerScript methods I use POS_END as the position of the script, since I intend to put all the scripts (including CoreScript, for example jQuery, jQueryUI, etc.) after the <body> .

The problem is that the displayed script will display one of the view file, and after that, that of the widget.

 alert(a); var a = "Hello World!"; 

As we can see, the above code will not work, so I need to put the second line above the first line.

Any idea on how to force an order? I am fine with the CClientScript extension (and creating a new registerScript method) until all scripts are displayed in the final position, and I do not need to pull these built-in Javascript codes above to a new package or file.

+6
source share
5 answers

So, I finally found to hack this. I am extending the ClientScript class and ClientScript the registerScript method so that it accepts another $level parameter.

 public function registerScript($id, $script, $position = self::POS_END, $level = 1); 

Think of $level as you would a z-index in CSS, except that the higher the number of $level , the lower the position of the script.

for instance

 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 if script3 declared after script2 , it will be shown above script2 in the displayed script, since the value of $level script2 greater than script3 .

Here is my solution code. It works the way I want, although I'm not sure if the organization 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 a clientScript component in config

 'components' => array( 'clientScript' => array( 'class' => 'ClientScript' ) ) 
+8
source

Try registering in HEAD

 Yii::app()->clientScript->registerScript(__CLASS__, <<<JAVASCRIPT var a = "Hello World!"; JAVASCRIPT , CClientScript::POS_HEAD); 
+1
source

I have implemented a patch for Yii to allow ordering (preend, append, number)

you can find it here https://github.com/yiisoft/yii/pull/2263

if it doesn't hit you, then you can extend CClientScript to your own class to apply my changes.

but in general, correctly written JS should work regardless of order, for example, instead of defining some global scopes, you can assign them as properties for a window (access to a nonexistent property does not cause an error in js)

+1
source

No need to extend CClienScript. A simple solution to this problem would be to create an $ script array in your controller's controller and add view scripts to this variable. In your layout file. You would register the scripts stored in the $ scripts array after you register the ones you want to receive before it. eg:.

in your controller

 public $scripts = []; 

In your layout

 $baseURL = Yii::app()->request->baseUrl; $clientScript = Yii::app()->clientScript; $clientScript->registerScriptFile( $baseURL . '/js/jquery.min.js', CClientScript::POS_END ); $clientScript->registerScriptFile( $baseURL . '/js/bootstrap.min.js', CClientScript::POS_END); 

and in your opinion

 <?php array_push($this->scripts,"/js/summernote.min.js");?> 
0
source

Override the CClientScript class, as @Petra said, here is the code I use, after using the @Petra code, it didn’t work for me.

 class ClientScript extends CClientScript { public $scriptLevels; public function registerScript($id, $script, $position = null, $level = 1, array $htmlOptions = array()) { $this->scriptLevels[$id] = $level; return parent::registerScript($id, $script, $position, $htmlOptions); } public function render(&$output) { foreach ($this->scripts as $key => $value) { $this->scripts[$key] = $this->reorderScripts($value); } parent::render($output); } public function reorderScripts($element) { $tempScripts = array(); $buffer = array(); foreach ($element as $key => $value) { if (isset($this->scriptLevels[$key])) { $tempScripts[$this->scriptLevels[$key]][$key] = $value; } } ksort($tempScripts); foreach ($tempScripts as $value) { foreach ($value as $k => $v) { $buffer[$k] = $v; } } return $buffer; } } 

and then go to your configuration file and in the components section change the following:

 'components' => array( . . 'clientScript' => array( 'class' => 'ClientScript' ), ) 
0
source

Source: https://habr.com/ru/post/921311/


All Articles