2012-07-25 9 views
6

stworzyłem widżet, który rejestruje swój własny skrypt jak poniżejZmuszanie Script kolejności od metody registerScript w Yii

class MyWidget extends CWidget { 
    public function run(){ 
     Yii::app()->clientScript->registerScript(__CLASS__, <<<JAVASCRIPT 
var a = "Hello World!"; 
JAVASCRIPT 
     , CClientScript::POS_END); 
    } 
} 

A w układzie wzywam widżet lubię tego

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

ale w zobacz plik, potrzebuję zmiennej zadeklarowanej przez ten widget.

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

Należy zauważyć, że zarówno w sposób registerScript użyć POS_END jako położenie skryptu ponieważ zamierza umieścić wszystkie skryptów (w tym CoreScript przykład jQuery jQueryUI etc) po znaczniku <body>.

Problem polega na tym, że renderowany skrypt wyświetli ten z pliku widoku, a następnie ten z widżetu.

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

Jak widać powyższy kod nie zadziała, więc muszę umieścić drugi wiersz powyżej pierwszego wiersza.

Każdy pomysł, jak zmusić zamówienie? Nie mam problemu z rozszerzeniem CClientScript (i tworzeniem nowej metody registerScript), o ile wszystkie skrypty będą renderowane w pozycji końcowej i nie będę musiał pobierać tych wewnętrznych kodów JavaScript powyżej do nowego pakietu lub pliku.

Odpowiedz

8

więc w końcu znaleźć hack, aby to zrobić. Rozszerzam nową klasę ClientScript i modyfikuję metodę registerScript, aby akceptował inny parametr $level.

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

Pomyśl o $level jak z-index w CSS, z wyjątkiem większa liczba $level, tym niższa pozycja skryptu będzie.

Na przykład

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); 

Nawet script3 jest zadeklarowanym po script2, w wytopione skryptu, to pokaże powyżej script2 ponieważ wartość $level od script2 jest większa niż script3 wieku.

Oto kod mojego rozwiązania. Działa to tak, jak chcę, chociaż nie jestem pewien, czy sposób aranżacji jest odpowiednio zoptymalizowany.

/** 
* 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; 
     } 
    } 
} 

Więc wystarczy umieścić klasę jako składnik clientScript w config

'components' => array(
    'clientScript' => array(
     'class' => 'ClientScript' 
) 
) 
1

Spróbuj zarejestrować się HEAD

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

Moją intencją jest świadczenie wszystkich skryptów na końcu. Chodzi o to, że skrypt w widgecie zależy od jQuery, a ja włożyłem wszystkie coreScripty na koniec, ustawiając pole 'coreScriptPosition' (http://www.yiiframework.com/doc/api/1.1/CClientScript#coreScriptPosition- szczegóły) jako POS_END. –

+0

Cóż, masz zły pomysł, lub musisz podzielić deklarację var na HEAD, treść widżetów skryptu END, lub U musisz zmienić END na GOTOWE: Yii :: app() -> clientScript-> registerScript ('script' , <<< JAVASCRIPT alert (a); JAVASCRIPT , CClientScript :: POS_READY); – Sergey

1

jakie wniósł poprawkę do Yii aby umożliwić zamawianie (prepend, dołącz, numer)

można go znaleźć tutaj: https://github.com/yiisoft/yii/pull/2263

jeśli nie dostał się do ciebie, możesz rozszerzyć CClientScript do swojej własnej klasy Ply moje zmiany

ale ogólnie poprawnie napisany JS powinien działać na przykład niezależnie od zamówienia zamiast definiowania niektórych globalnych zakresów można przypisać je jako właściwości do okna (dostęp do nieistniejącej właściwości nie powoduje błędu w js)

0

Nie ma potrzeby rozszerzania CClienScript. Prostym rozwiązaniem tego problemu byłoby utworzenie tablicy $ script w kontrolerze w kontrolerze i dodanie skryptów widoku do tej zmiennej. W twoim pliku układu zarejestrujesz skrypty przechowywane w twojej tablicy $ scripts po zarejestrowaniu tych, które chcesz przedtem wyrenderować. np .:

w kontrolerze

public $scripts = []; 

w układzie:

$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); 

i w widoku

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

Najprościej i najlepsza wydajność. –

+0

nie, nie działa – surfer190

0

Zastąp klasę CClientScript jako @Petra powiedział, tutaj jest kod, którego używam, po użyciu kodu @Petra i nie wyszło dla mnie.

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; 
} 

} 

, a następnie przejdź do pliku konfiguracyjnego, aw Edytuj sekcję następujące elementy:

'components' => array(
    . 
    . 
    'clientScript' => array(
     'class' => 'ClientScript' 
    ), 
) 
Powiązane problemy