как обратиться к методу виджета ?Добрый

Добры вечер,

Переношу функциональность из контроллера в виджет, возник такой вопрос:

подскажите, при использовании CHtml::ajax в виджите, как обратиться к методу виджета?

если делать через контроллер, то понятно: нужно прописывается ‘url’=>CController::createUrl(‘conv/updateajax?’),

а как обратиться к методу updateajax если он расположен в виджите?




<?php 

		echo CHtml::textField(

			'search_input_field',

			'start typing to filter...',

			array(

				'id'=>'search_input',

				'class'=>"searchbox_text_tt",

				 'onkeyup'=>CHtml::ajax(

				array(

					   'type'=>'POST',

					   'url'=>/*Какой указать url если использую виджет<img src='http://www.yiiframework.com/forum/public/style_emoticons/default/huh.gif' class='bbc_emoticon' alt='???' />?*,,

					   'update'=>'#data',

					   'data'=>'js:jQuery(this).serialize()'

					)

				), 

			)

		);

?>



Если нужно сделать ссылку, то можно обратиться к методу контроллера, который использует этот виджет через $this->controller->createUrl() или к CWebApplication: Yii::app()->createUrl(), смотря какую ссылку надо сделать. Если нужно обратиться непосредственно к методу виджета, то так и обращайся: $this->updateajax().

поясню вопрос…

есть вот такой пример работ с ajax Update content in AJAX with partialRender

в этом примере из view идёт обращение к action контролера (helloWorld/UpdateAjax)

views/helloWorld/index.php:




<div id="data">

   <?php $this->renderPartial('_ajaxContent', array('myValue'=>$myValue)); ?>

</div>

 

<?php echo CHtml::ajaxButton ("Update data",

                              CController::createUrl('helloWorld/UpdateAjax'), 

                              array('update' => '#data'));



этот action контроллера helloWorld возвращает новое значение переменной




public function actionUpdateAjax()

    {

        $data = array();

        $data["myValue"] = "Content updated in AJAX";

 

        $this->renderPartial('_ajaxContent', $data, false, true);

    }



вопрос в том, можно ли эот пример реализовать через виджет, т.е. в виджете сделать аналогичный метод?

Понятно что в виджете надо делать render вместо renderPartial

Но как из view виджета вызвать метод UpdateAjax() и получиться из него данные? т.е. на что заменить вот этот код во view виджета:


CController::createUrl('helloWorld/UpdateAjax')

Полностью отделить виджет от контроллера не получится, потому что он по определению зависит от последнего. Вот похожая тема на английском: http://www.yiiframework.com/forum/index.php?/topic/7343-self-contained-ajaxian-widgets

Так что обработку ajax запросов придется оставить в контроллере, но вызвать виджет внутри класса контроллера также легко, как и в макете:




public function actionUpdateAjax()

    { 

        $this->widget(<всякие параметры>);

    }



Скажу только, что этот вариант я в деле не пробовал и не могу сказать, какие косяки могут всплыть :)

Спасибо!

Жалко, что не получится полностью вынести функционал в виджет, хотелось сделать отдельный виджет который через Ajax фильтрует список (без взаимодействия с контроллером).

Значит, буду из виджета передавать данные ajax’ом в контроллер, а в контроллере делать фильтрацию и выводить результаты через renderPartial :

виджет (ConvList) :


    protected function renderContent()

    {

		$dataProviderConv=$this->loadconv();

        $this->render('convList', array(

                'dataProviderConv'=>$dataProviderConv,

				'data' => $data, 

        ));

view виджета (convView):


<?php 

		echo CHtml::textField(

			'search_input_field',

			'start typing to filter...',

			array(

				'id'=>'search_input',

				'class'=>"searchbox_text_tt",

				'onkeyup'=>CHtml::ajax(

					array(

						'type'=>'POST',

						'url'=> Yii::app()->createUrl('conv/UpdateAjax'),

						'update'=>'#data',

						'data'=>'js:jQuery(this).serialize()'

					)

				), 

			)

		);

		?>

<div id="data" style='clear:both;'>

<?php 

$this->render('_ajaxContent', array(

	'myValue'=>$myValue,

	'dataProviderConv'=>$dataProviderConv,

	)); 

?>

</div>

в контроллере conv:


public function actionUpdateAjax()

{

	$sstr=$_POST['search_input_field'];

		$dataProviderConv=$this->loadconv($sstr);

//тут я рендэрю вид контроллера _ajaxContent (аналогичный одноимённому виду виджета)

	$this->renderPartial('_ajaxContent', array(

		'dataProviderConv'=>$dataProviderConv,

		'data' => $data, 

        )

		, false, true);

}

но получается, что по ссыдке http://domain.com/conv/UpdateAjax будет доступен вид _ajaxContent Это как-то неаккуратненько.

Вижу такое решениие проблемы:

  1. из контроллера отрендерить не view контроллера, а view виджета _ajaxContent (чтобы не плодить одинаковые view). Как это сделать ? Как отрендерить вид виджета из контроллера ?

  2. как-то запретить напрямую через ссылку (http://domain.com/conv/UpdateAjax) обращаться к actionUpdateAjax контроллера. Такое можно сделать? Как сделать, чтобы действие контроллера было доступно из виджета, но не доступно по ссылке?

можно разрешить только ajax запросы

Для участка кода


if(Yii::app()->request->isAjaxRequest){/*видно только через ajax*/}

Для всего действия


class AjaxController extends CController {

    public function filters() {

        return array(

            'ajaxOnly + index',

        );

    }

    

    public function actionIndex() {

        

    }


}

Спасибо!

Осталось понять, как из контроллера рендерить view виджета?

Пытался делать так:


$path=this->CWidget('ConvList')->getViewPath();//но эта строчка в контроллере не только возвращает путь, но и отрисовывает сам виджет

this->renderPath($path, ...);

не прокатило - получил виджет внутри виджета после того как отработал ajax :)

Зачем контроллеру рендерить представление виджета? Пусть он сам рендерит :)




$this->widget(<тут можно передать любые параметры, которые могут повлиять на логику работы виджета>);



если я правильно понял, автору хочется сохранить целостность кода виджета, а не раскидывать его функционал по разным контроллерам и действиям, никак не относящимся к самому виджету.

Всё правильно, но контроллер не может никак не относиться к виджету, потому что виджет создается именно контроллером. Сейчас в Yii нет возможности набрать в строке браузера что-то типа index.php?r=widget, при этом сам виджет обработает запрос, вытащит данные из БД и отправит ответ. Вообще говоря, делать такую штуку надо осторожно, если вообще в будущем такая возможность планируется.

Логично:) Вызвать в контроллере виджет…вечерком попробую!!!

У меня view виджета содержит:

  1. input (который ajax’ом передаёт текст в контроллер, в контроллере этот текст учавствует в создании фильтра в dataProvider)

  2. div в который ajax’ом возвращается результат из контроллера.

Получается мне нужен либо ещё один виджет, который рендерит только то, что нужно в div поместить

Либо в моём виджете в renderContent делать ветвление (и рендерить либо весь view виджета, либо только то что нужно в div передать)

Или есть другой способ?

я хотел бы чтобы в контроллере виджет вызывался и дальше он был автономным (т.е. чтобы сам отрабатывал взаимодействие с моделью и через ajax вытаскивал данные). А сейчас приходится ajax обрабатывать в контроллере, т.к. CHtml::ajax для работы нужен ‘url’ по которому он заберёт данные и поместит их в <div id=“data”> </div>


>CHtml::ajax(

                                        array(

                                                'type'=>'POST',

                                                'url'=> Yii::app()->createUrl('conv/UpdateAjax'),

                                                'update'=>'#data',

                                                'data'=>'js:jQuery(this).serialize()'

                                        )



Может есть ещё какой способ в виджете ajax’ом забрать данные из модели?

Мне больше нравится ветвление, тем более даже в самом view виджета можно выводить html по условию Yii::app()->request->isAjaxRequest.

Я думаю, что в идеале должен быть экшн, который занимается ajax запросами, вызывая нужный виджет и ничего более. Это вполне реализуемо, но лично я таким не занимался, пока… :)

То что надо :)

Но дальше больше:

если в контроллере




 public function actionUpdateAjax()

    {

		$sstr=$_POST['search_input_field'];

		$dataProviderConv=$this->loadconv($sstr);

		

		$this->widget('ConvList', array(

		  'filter' => $sstr,

		), false, true);  

     }



вызываю виджет:




protected function renderContent()

{	

		$dataProviderConv=$this->loadconv($this->filter);

		$this->render('convList', array(

                'dataProviderConv'=>$dataProviderConv,

        )

		,false, true);

}



view виджета:




<?php if (Yii::app()->request->isAjaxRequest): ?>

	echo 'ajax';

<div id="data" style='clear:both;'>

<?php 

$this->render('_ajaxContent', array(

	'dataProviderConv'=>$dataProviderConv,

	)

	, false, true); 

?>

</div>

<?php else: ?>

                echo CHtml::textField(

                        'search_input_field',

                        'start typing to filter...',

                        array(

                                'id'=>'search_input',

                                'class'=>"searchbox_text_tt",

                                'onkeyup'=>CHtml::ajax(

                                        array(

                                                'type'=>'POST',

                                                'url'=> Yii::app()->createUrl('conv/UpdateAjax'),

                                                'update'=>'#data',

                                                'data'=>'js:jQuery(this).serialize()'

                                        )

                                ), 

                        )

                );

                ?>

<div id="data" style='clear:both;'>

<?php 

$this->render('_ajaxContent', array(

        'myValue'=>$myValue,

        'dataProviderConv'=>$dataProviderConv,

        ), false, true); 

?>

</div>

<?php endif; ?>




, то ломаются ссылки в пагинаторе:

Изначально виджет запускается в Post/Index

и ссылки в пагиаторе виджета имеют вид /post/Index?Conv_page=2

но после обновления через ajax т.к. вызывается виджет в другом контроллере/действии, то ссылки получаются "кривые" (/сonv/UpdateAjax?Conv_page=2)

Как сделать, чтобы ссылки в пагинаторе возвращаемого (ajax’ом) ClistView не менялись на /сonv/UpdateAjax ?

http://www.yiiframework.com/doc/api/1.1/CDataProvider#pagination-detail

http://www.yiiframework.com/doc/api/1.1/CPagination#route-detail

Надеюсь, это то, что нужно. А нужно задать конкретное значение controllerId/actionId.

Cпасибо, сработало!!!!

Сделал вот так во view (_ajaxContent.php подгружаемая аяксом) :




//_ajaxContent.php

<?php 

	$dataProviderConv->setPagination(array(

		'route'=>$route, //сюда я передал строку вида 'controller/view'

		

	));

	$this->widget('zii.widgets.CListView', array(

	'dataProvider'=>$dataProviderConv,

	'itemView'=>'_convview',

	//'enableSorting'=>true,

	'ajaxUpdate' => true,

	'template'=>"{items}\n{pager}", //this remove: Displaying #... of ... result

	'cssFile'=> Yii::app()->theme->baseUrl.'/style.css',

	'pager' => array(

                'header' => '',

				//'route'=>'post/index',

        ),  


));

?>



но возникает новая проблемка (когда ж они кончаться то ? :))

после того как подгружаю ajax’ом вью _ajaxContent то получается странность:

CListView должен работать через аякс (‘ajaxUpdate’ => true), но из-за того что в setPagination $route не полнностью равен текущему url (т.к. не передаю некоторые переменные), то первое перелистывание пагинатора происходит не через аякс.

Если передавать пустой route, то проблема не решается.

Сейчас попробую передавать в $route полный текущий url (controller/action?var=1&…) тогда судя по экспериментам пагинатор в CListView должен правильно отработать (т.е. сразу листать аяксом)

Мда… получается запутанное решение (к тому же как я понял нельзя просто получить список переменных в текущем урле, надо самому их вытаскивать из GET используя foreach). Было бы удобно если бы в Yii существовал быстрый способ получить все текущие переменные из урла (задача эта часто всплывала когда на одном view есть два контроллера, например, два обычных пагинатора и листая один - сбиваешь другой, если не передавать в урле его параметры).

Решил описать по крупному используемый алгоритм (может у кого будут другие идеи как реализовать :)

[b]Задача:

[/b]на странице вывести:

  1. Постраничный вывод постов (с пагинацией)

  2. Постраничный вывод постов #2 (пагинация+фильтрация: должна быть фильтрация на аяксе… пользователь вводит слово и в списке отображаются только те посты, в которых это слово содержится… фильтруется по мере ввода слова…т.е. CgridView не подойдёт)

[b]Реализация:

[/b]На странице выводится:

  1. список постов (CListView с пагинацией не через аякс)

  2. виджет (input и ClistView подгружаемый аяксом в зависимости от значения input, в самом ClistView пагинация работает на аяксе)

Виджет(V) устроен так:

[list=1]

[*]в post/index вставлен Виджет(V), он рендерит input и div №1 (в котором находится список CListView)

[*]в input пользователь вводит СЛОВО

[*]СЛОВО передаётся аяксом в контроллер в actionUpdateAjax

[*]в actionUpdateAjax опять заупускается виджет(V) но ему передаётся параметр СЛОВО

[*]виджет(V) видит что параметр не пустой и рендерит только div №2 в котором CListView сформирован теперь уже с фильтрацией по СЛОВО

[*]этот div №2 возвращается аяксом в виджет(V) и заменяет изначальный div №1 (и вот тут то как раз и проблема - ссылка в пагинации в div №2 кривая; имеет вид controller/UpdateAjax т.к. виджет вызывался из UpdateAjax

[/list]

Мда…не взлетело. Если менять controller/action для виджета через setPagination то листалка аяксом работает не сразу

Всё, не могу больше мучаться, проще чуть чуть изменить ТЗ, чем делать это.

[b]По сему резюмирую:

Сделать в боковой колонке виджет, который по мере набора пользователем возвращает аяксом CListView (с пагинацией на аяксе) не реально (либо как-то очень мудрёно) - пагинация в CListView работает неверно[/b]

если кто-то подкинет идейки, то буду благодарен.

А вообще можно ли на одну страничку поместить view двух разных контроллеров? сейчас я так понял единственный вариант - это виджет?

Можно не делать два отдельных action’а для обычного запроса и ajax запроса, а обработать оба варианта в одном методе, что-то типа этого:




public function actionIndex() {

    if (Yii::app()->request->isAjaxRequest) {

        //виджет внутри тоже проверяет тип запроса и по ajax-запросу выводит

        //только необходимую часть (или вообще может возвращать json)

        $this->widget(...);

    } else {

        //выводим полное представление страницы

        $this->render(...);

    }

}



Таким образом и изначальное отображение страницы и ajax обновление будут происходить из одного и того же action’а, поэтому проблем с неправильными ссылками не будет.

Я так хотел сделать, но похоже тоже не сработает:

изначально виджет вызывается в layout/column2

а этот слой используется многими контроллерами (и даже многими action’ами одного контроллера), тогда получается в каждом из контроллеров (в каждом методе), который использует этот слой нужно делать ветвеление if (Yii::app()->request->isAjaxRequest) {…}

Вообще проблема решаема - у нас в текущем проекте сейчас есть супер-универсальный-контроллер, через который работает все приложение, вообщем-то оно у нас и построено на отдельных самостоятельных виджетах.

Вначале такой подход выглядел привлекательно, но походу написания оказалось тоже не очень удобно - у нас есть виджет верхнего уровня "страница" внутрь которой уже вставляются другие виджеты. Так вот на нее пришлось переложить массу функций контролеера - настройка прав доступа, настройка кеширования и т.д и сейчас хочется вернуться назад, ближе к "родному" подходу Yii.

Аякс-обновление виджетов у нас работает таким образом: у каждого виджета есть уникальное имя, которое отправляется как параметр аякс-запроса с клиента, по этому имени мы находим массив с конфигурацией виджета и выводим только его. Так как все работает через один контроллер, то и проблем со ссылками не возникает.

В качестве возможных альтернатив можно подумать в сторону фильтров (CFilter / CFilterWidget) - может быть, вместо добавления ветвлений if (Yii::app()->request->isAjaxRequest) {…вывести виджет…} в каждый action, можно сделать один фильтр, который и будет выводить виджет при аякс-запросе? А потом сконфигурировать соответствующим образом нужные контроллеры.

И вот еще интересная возможность есть в виджете. Судя по описанию, виджет может быть провайдером action’ов для контроллера, но на практике я это еще попробовать не успел.