2. rész
Írta: János Zahratka
Nos, ahogy ígértem, íme a második rész. Ebben a szakaszban származtatni fogjuk a már elkészített jQTBaseOverlay widget-ünket, aminek ezúttal már a HTML oldalon megjelenő eredménye is lesz.
Emlékeztetőül: az előző részben elkészítettünk egy alap widget-et, a jQuery Tools overlay plugin-jéhez. Erre azért volt szükség, mert a plugin-ból több, különböző eszközt lehet csinálni. Ebben a részben két felhasználási lehetőséget fogok megmutatni. Az egyik egy olyan overlay doboz, amibe bármilyen tartalmat el lehet helyezni. A másik pedig egy üzenő/kérdező dialógus, amivel üzeneteket, illetve kérdéseket lehet megjeleníteni a felhasználóknak, és utóbbi esetében a választól függően lehet "reagálni" a felhasználó választására.
jQTOverlay
Első lépésben létrehozunk egy új php állományt a protected/components/widgets mappában jQTOverlay.php névvel. Még mielőtt nekikezdenénk awidget osztályának kiterjesztéséhez, gondoskodnunk kell róla, hogy a widget megtalálja a szülő osztályát, ezt pedig a YiiBase osztály import metódusának segítségével fogjuk megoldani. Az üres osztályunk valahogy így fog kinézni:
<?php
Yii::import('application. components.widgets. jQTBaseOverlay');
class jQTOverlay extends jQTBaseOverlay
{
}
Ezután kicsit átírjuk a publikus init metódust, ami a widget alapértékeinek beállítását végzi el. Ebben az esetben mindőssze annyit fog csinálni ez a metódus, hogy lefuttatja a szülő osztály azonos nevű metódusát, majd meghívja a registerClientScript, és a renderBegin metódusokat. Egyik sem csinál mást, "csak" végrehajtja a szülő osztályban lévő azonos nevű eljárásokat. Ez azért lett így összerakva, mert az alap osztályban már minden szükséges metódus, és paraméter definiálva van, itt pedig csak a megfelelő sorrendben hívogatni kell őket.
...
public function init() {
parent::init();
$this->registerClientScript();
$this->renderBegin();
}
...Ha ezzel megvagyunk, előkapjuk a run metódust, és meghívjuk benne a renderContent és a renderEnd metódusokat. Ezek szintén definiálva vannak a szülő osztályban.
...
public function run() {
$this->renderContent();
$this->renderEnd();
}
...Már csak négy metódus van hátra, melyek egy kivételével szintén csak annyit csinálnak, hogy meghívják a szülő osztály azonos nevű metódusait. Ezek aregisterClientScript, a renderBegin, a renderContent, és a renderEnd metódusok. Az üres metódus arra való, hogy ha valaki tovább szeretné származtatni ezt a widgetet, akkorű jobb, ha megvan ez a metódus is.
...
protected function registerClientScript() {
parent::registerClientScript() ;
}
protected function renderBegin() {
parent::renderBegin();
}
protected function renderContent() {
}
protected function renderEnd() {
parent::renderEnd();
}
...A widget-et tovább lehetne rövidíteni azzal, hogy azokat a metódusokat, amelyek csak a szülő osztályban lévő önmagukat hívják meg nem kell deklarálni, csak meghívni a parent segítségével, azonban gondolni kell arra is, hogy esetleg olyan osztályt fogunk csinálni, ami ezt a már kibővített osztályt fogja tovább származtatni.
Ahogy ígértem, valóban sokkal rövidebb lesz a második rész, ugyanis az a helyzet, hogy a jQTOverlay widget ezzel készen is van. Lássuk a kódot!
<?php
Yii::import('application. components.widgets. jQTBaseOverlay');
class jQTOverlay extends jQTBaseOverlay
{
public function init() {
parent::init();
$this->registerClientScript();
$this->renderBegin();
}
public function run() {
$this->renderEnd();
}
protected function registerClientScript() {
parent::registerClientScript() ;
}
protected function renderBegin() {
parent::renderBegin();
}
protected function renderContent() {
}
protected function renderEnd() {
parent::renderEnd();
}
}
Már csak annyi van hátra, hogy bármely view (nézet) állományban meghívjuk, ami valahogy így néz ki:
...
<!-- Ha erre klikkel, akkor jöhet az overlay. -->
<?php echo CHtml::link(CHtml::encode(' Overlay'),
array('#'),
array(
'id' => 'showOverlay',
'name' => 'showOverlay',
'rel' => '#Overlay'
)
); ?>
...
...
<!-- És ez a widget hívása. -->
<?php $this->beginWidget(' application.components. widgets.jQTOverlay', array(
'htmlOptions' => array(
'id' => 'overlay',
'name' => 'overlay'
),
'trigger' => 'showOverlay',
'useMask' => true,
'title' => 'Overlay',
)); ?>
Bármilyen HTML, vagy php tartalom, amit meg akarunk jeleníteni.
<?php //$this->endWidget(); ?>
...
jQTDialog
A második származtatott widget-ünk egy kicsit - de tényleg csak egy kicsit - bonyolultabb, de csak azért, mert arra vetemedtem, hogy egy type nevű paraméter segítségével három dialógus típust kezeljek egy widget megírásával. Mindhárom változat működése azonos. A különbség abban van, hogy milyen válaszlehetőségeket adok a felhasználónak.
Két paramétert vezettem be, amelyek a szülő osztályban nem léteznek. Az egyik a már előbb említett type, ami a dialógus típusát határozza meg. Értéke "alert" (figyelmeztetés), "question" (kérdés) vagy "decision" (döntés) lehet. A másik a message paraméter, amely a megjelenítendő üzenetet, vagy kérdést tárolja.
A szülő osztályból átvettem még az _openTag paramétert, ami pontosan ugyanazt tárolja, mint a szülő.
Az init metódus ezúttal összetettebb lesz, mint az előző esetben.
- Megvizsgálja, hogy a type paraméter a megfelelő értéket tartalmaz-e, azután azt is meg kell vizsgálni, hogy van-e üzenet, amit meg kell jeleníteni (message). Ha ez a két paraméter nem megfelelő, akkor a widget egyszerűen leáll és nem jelenít meg semmit.
- Megváltoztatja az overlay CSS osztályának nevét a dialógus típusának megfelelően. Ez nem kötelező, viszont szerintem jó megoldás arra, hogy az egyes dialógus típusok más-más megjelenési formát vehessenek fel.
- Meghívja a szülő osztály azonos nevű metódusát, aztán az előbbi overlay widget-ben leírt módon a registerClientScript és renderBegin metódusok hívásai következnek.
...
public function init() {
if('alert' !== $this->type && 'question' !== $this->type && 'decision' !== $this->type) {
return null;
}
if(null === $this->message) {
return null;
}
$this->htmlOptions['class'] = $this->type;
$this->params['closeOnClick'] = false;
parent::init();
$this->registerClientScript();
$this->renderBegin();
}
...A run metódus ugyanazt csinálja, mint az előző widget.
...
public function run() {
$this->renderEnd();
}
...A registerClientScript metódus állítja elő azt a javascript kódot, ami az oldal végére kerül, és a dialógus kliens oldali működéséért felelős.
Ha kérdés típusú dialógust szeretnénk megjeleníteni, akkor több válaszlehetőséget kell biztosítanunk, és egyúttal gondoskodnunk kell róla, hogy a lap tudja, hogy mit válaszolt a felhasználó. Ennek első része az, hogy egy target nevű változóba betöltjük a dialógus azonosítóját, ami vagy meg van adva az overlay plugin paraméterei között, vagy ha nincs, akkor a dialógus azonosítója lesz az.
Ha ez megvan, akkor elkérjük a szülő osztálytól a script első részét, és hozzáfűzzük a válasz gombok kezelésének szkriptjét, aztán az így elkészült kódot visszatesszük a publikus script praméterbe, majd meghívjuk a szülő osztály azonos nevű metódusát.
A script kódja csak annyira van megírva, hogy az igényeknek megfelelően simán folytatni lehessen, tehát az igazi használathoz még dolgozni kell rajta.
...
protected function registerClientScript() {
if('question' === $this->type || 'decision' === $this->type) {
if(isset($this->params[' target'])) {
$target = $this->params['target'];
} else {
$target = $this->htmlOptions['id'];
}
$script = $this->script;
$script = $script . "\n" .
'$("#' . $target . ' .buttons button").click(function(event) {' .
'var result = $(this).attr("value");' .
'});';
$this->script = $script;
}
parent::registerClientScript() ;
}
...És már csak annyi kell, hogy megjelenítsük a dialógusunk HTML kódját. Ennek első felét a renderBegin metódus végzi, ami kiírja a dialógus kezdő részét, címét - ha van -, aztán az üzenetet, vagy kérdést.
...
protected function renderBegin() {
ob_start();
ob_implicit_flush(false);
echo "\n" . CHtml::openTag($this->tagName, $this->htmlOptions) . "\n";
if(null !== $this->title || '' !== $this->title) {
echo CHtml::tag('h3', array('class' => 'title'), CHtml::encode($this->title), true) . "\n";
}
echo CHtml::tag('p', array('class' => 'message'), CHtml::encode($this->message), true);
$this->_openTag = ob_get_contents();
ob_clean();
}
...És jön az utolsó metódus, név szerint a renderEnd, ami mindössze annyit csinál, hogy a kód végére odateszi a dialógus típusának megfelelő gombokat, aztán lezárja a widget záró kódjával az egészet.
...
protected function renderEnd() {
$content = ob_get_clean();
echo $this->_openTag;
echo trim($content);
echo "\n" . CHtml::openTag('div', array('class' => 'buttons')) . "\n";
if('alert' === $this->type) {
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonOk', 'value' => 'ok', 'class' => 'close'), 'OK', true) . "\n";
} elseif('question' === $this->type) {
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonYes', 'value' => 'yes', 'class' => 'close'), 'Igen', true) . "\n";
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonNo', 'value' => 'no', 'class' => 'close'), 'Nem', true) . "\n";
} elseif('decision' === $this->type) {
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonYes', 'value' => 'yes', 'class' => 'close'), 'Igen', true) . "\n";
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonNo', 'value' => 'no', 'class' => 'close'), 'Nem', true) . "\n";
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonCancel', 'value' => 'cancel', 'class' => 'close'), 'Mégsem', true) . "\n";
}
echo CHtml::closeTag('div');
echo "\n" . CHtml::closeTag($this-> tagName) . '' . "\n";
}
...Most pedig nézzük a teljes kódot!
<?php
Yii::import('application. components.widgets. jQTBaseOverlay');
class jQTDialog extends jQTBaseOverlay
{
public $type = 'alert';
public $message;
private $_openTag;
public function init() {
if('alert' !== $this->type && 'question' !== $this->type && 'decision' !== $this->type) {
return null;
}
if(null === $this->message) {
return null;
}
$this->htmlOptions['class'] = $this->type;
$this->params['closeOnClick'] = false;
parent::init();
$this->registerClientScript();
$this->renderBegin();
}
public function run() {
$this->renderEnd();
}
protected function registerClientScript() {
if('question' === $this->type || 'decision' === $this->type) {
if(isset($this->params[' target'])) {
$target = $this->params['target'];
} else {
$target = $this->htmlOptions['id'];
}
$script = $this->script;
$script = $script . "\n" .
'$("#' . $target . ' .buttons button").click(function(event) {' .
'var result = $(this).attr("value");' .
'});';
$this->script = $script;
}
parent::registerClientScript() ;
}
protected function renderBegin() {
ob_start();
ob_implicit_flush(false);
echo "\n" . CHtml::openTag($this->tagName, $this->htmlOptions) . "\n";
if(null !== $this->title || '' !== $this->title) {
echo CHtml::tag('h3', array('class' => 'title'), CHtml::encode($this->title), true) . "\n";
}
echo CHtml::tag('p', array('class' => 'message'), CHtml::encode($this->message), true);
$this->_openTag = ob_get_contents();
ob_clean();
}
protected function renderEnd() {
$content = ob_get_clean();
echo $this->_openTag;
echo trim($content);
echo "\n" . CHtml::openTag('div', array('class' => 'buttons')) . "\n";
if('alert' === $this->type) {
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonOk', 'value' => 'ok', 'class' => 'close'), 'OK', true) . "\n";
} elseif('question' === $this->type) {
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonYes', 'value' => 'yes', 'class' => 'close'), 'Igen', true) . "\n";
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonNo', 'value' => 'no', 'class' => 'close'), 'Nem', true) . "\n";
} elseif('decision' === $this->type) {
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonYes', 'value' => 'yes', 'class' => 'close'), 'Igen', true) . "\n";
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonNo', 'value' => 'no', 'class' => 'close'), 'Nem', true) . "\n";
echo CHtml::tag('button', array('id' => $this->htmlOptions['id'] . 'ButtonCancel', 'value' => 'cancel', 'class' => 'close'), 'Mégsem', true) . "\n";
}
echo CHtml::closeTag('div');
echo "\n" . CHtml::closeTag($this-> tagName) . '' . "\n";
}
}
Végül pedig az elkészült widget használata a view (nézet) állományokban....
<!-- Ha erre klikkel, akkor jöhet a dialógus. -->
<?php echo CHtml::link(CHtml::encode(' Dialog'),
array('#'),
array(
'id' => 'showDialog',
'name' => 'showDialog',
'rel' => '#Dialog'
)
); ?>
...
...
<!-- És ez a widget hívása. -->
<?php $this->widget('application. components.widgets.jQTDialog', array(
'htmlOptions' => array(
'id' => 'overlay',
'name' => 'overlay'
),
'trigger' => 'showDialog',
'useMask' => true,
'title' => 'Dialógus',
'type' => 'question',
'message' => 'Valamit kérdezni akartam, de elfelejtettem. Nem tudod, mi volt az?'
)); ?>
...
Remélem, lesz némi haszna a cikknek! Akinek netán kedve támadna, hogy megjegyzéssel illesse a két cikket, ne fogja vissza magát! Nem vagyok egy über okos programozó, csak szeretek ezzel foglalkozni, és szívesen tanulok én is abból, amit a nálam okosabbak mondanak/írnak.