Я создал виджет, который регистрирует свой собственный скрипт, как показано ниже.
class MyWidget extends CWidget { public function run(){ Yii::app()->clientScript->registerScript(__CLASS__, <<<JAVASCRIPT var a = "Hello World!"; JAVASCRIPT , CClientScript::POS_END); } }
И в макете я вызываю виджет, как это
<?php $this->widget('MyWidget');?> <?php echo $content;?>
Но в файле вида мне нужна переменная, объявленная этим виджетами.
<?php Yii::app()->clientScript->registerScript('script', <<<JAVASCRIPT alert(a); JAVASCRIPT , CClientScript::POS_END); ?>
Обратите внимание, что в обоих методах registerScript я использую POS_END в качестве позиции сценария, так как после <body>
я намерен поместить все сценарии (включая CoreScript, например jQuery, jQueryUI и т. Д.).
Проблема заключается в том, что визуализируемый скрипт покажет тот из файла вида, а затем тот из виджета.
alert(a); var a = "Hello World!";
Как мы видим, приведенный выше код не будет работать, поэтому мне нужно поставить вторую строку над первой строкой.
Любая идея о том, как заставить заказ? Я в порядке с расширением CClientScript
(и созданием нового метода registerScript
), пока все сценарии будут отображаться в конечной позиции, и мне не нужно вытаскивать эти встроенные коды Javascript выше в новый пакет или файл.
Поэтому я наконец нашел взломать это. Я расширяю новый класс ClientScript
и ClientScript
метод registerScript
чтобы он принял другой параметр $level
.
public function registerScript($id, $script, $position = self::POS_END, $level = 1);
Подумайте об $level
как и в z-index
в CSS, за исключением того, что чем больше число $level
, тем ниже будет позиция скрипта.
Например
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);
Несмотря на то, что script3
объявлен после script2
, в визуализированном скрипте он будет показан выше script2
поскольку значение $level
для script2
больше, чем script3
.
Вот код моего решения. Он работает так, как я хочу, хотя я не уверен, что метод организации оптимизирован достаточно.
/** * 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; } } }
Поэтому мне просто нужно поместить класс в качестве компонента clientScript в конфигурацию
'components' => array( 'clientScript' => array( 'class' => 'ClientScript' ) )
Попробуйте зарегистрироваться в HEAD
Yii::app()->clientScript->registerScript(__CLASS__, <<<JAVASCRIPT var a = "Hello World!"; JAVASCRIPT , CClientScript::POS_HEAD);
Я внедрил патч для Yii, чтобы разрешить упорядочивание (добавление, добавление, число)
вы можете найти его здесь https://github.com/yiisoft/yii/pull/2263
если он не попадает в вас, то вы можете расширить CClientScript на свой собственный класс, чтобы применить мои изменения
но в целом правильно написанная JS должна работать независимо от порядка, например, вместо того, чтобы определять некоторые глобальные области видимости, которые вы можете назначить им как свойства окна (доступ к несуществующему свойству не вызывает ошибку в js)
Нет необходимости расширять CClienScript. Простым решением этой проблемы было бы создание массива $ script в контроллере вашего контроллера и добавление сценариев представления к этой переменной. В вашем файле макета. Вы зарегистрировали бы скрипты, хранящиеся в массиве $ scripts, после того, как вы зарегистрировали те, которые хотите получить перед ним. например:
в вашем контроллере
public $scripts = [];
В вашем макете:
$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);
и, на ваш взгляд
<?php array_push($this->scripts,"/js/summernote.min.js");?>
Переопределите класс CClientScript, как сказал @Petra, вот код, который я использую, после использования кода @Petra и не работал для меня.
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; } }
а затем перейдите в свой конфигурационный файл, а в разделе компонентов измените следующее:
'components' => array( . . 'clientScript' => array( 'class' => 'ClientScript' ), )