Забегая вперёд скажу, что в результате должно получиться вот такое поле, выпадающее меню и кнопка справа для выбора нужных данных.
Контекстно-зависимое окошко выбора:
После выбора:
Почему был выбран вариант с модальным окном? Это позволяет упростить выбор нужного элемента. В модальном окне можно выводить CGridView, при этом можно сортировать, искать и фильтровать данные, что очень удобно, если выбирать приходится из большого количества элементов.
Итак, ингредиенты для приготовления этой Yiiшницы:
- Одно Yii
- Расширение NLSClientScript
- Bootstrap по вкусу (расширение для Yii)
Теперь, наберемся терпения и начнем приготовление.
Допустим, у нас есть контроллер menuController.php, view файл _form.php и модель Menu.php. Не буду останавливаться на том, кто есть кто, всё это стандартный, автоматически сгенерированный код Gii, Гы.
Модель (Menu.php)
Сначала уточним, как работает модель. Будем считать, что у нас есть связка из типа данных и ID, которое привязано к типу данных. В модели (так же и в БД) должны присутствовать поля type и data_id, оба целочисленные. Для удобства при загрузке модели, будем загружать наименование объекта, на который он ссылается (понадобится при редактировании объекта). Допустим в поле data_name. Провернём нужные махинации в функции afterFind(). Так же сделаем список типов данных более динамичным. Определим статический массив в классе, таким образом, если нам нужно будет расширять или же сужать варианты типов, мы будем редактировать только класс модели.
Т.к. мы используем ActiveRecord то, по сути, необходимо знать только название модели и вьюшки для работы с конкретно взятым типом данных.
static $types = array( 1 => array("name" => "Страница", "model" => "Pages", "view" => "pages_grid"), 2 => array("name" => "Документ", "model" => "Docs", "view" => "docs_grid"), 3 => array("name" => "Папка", "model" => "Cats", "view" => "cats_grid"), ); public static function getSimpleTypes() { // возвращает список типов. Нужен для DropDownList $st = array(); foreach (Menu::$types as $key => $value) $st[$key] = $value["name"]; return $st; } var $data_name; public function afterFind() { $dataModel = Menu::$types[$this->type]['model']::model()->findByPk($this->data_id); $this->data_name = $dataModel->title; // Присваиваем переменной $data_name название выбранного элемента parent::afterFind(); }
На этом рассмотрение модели закончим.
View формы (_form.php)
Смотрим view файл нашей формы _form.php
<!-- Создаем стандартную форму --> <?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm',array( 'id'=>'menu-form', 'enableAjaxValidation'=>false, 'type' => 'horizontal', )); ?> <!-- Здесь могут быть Ваши поля формы --> <?php echo $form->errorSummary($model);?> <?php echo $form->textFieldRow($model,'name');?> <!-- Скрытое поле, в котором лежит data_id --> <?php echo $form->textField($model,'data_id',array('class'=>'hide')); ?> <!-- Сами пишем обертку будущего поля согласно правилам bootstrap--> <div class="control-group"> <div class="control-label"> <?=$form->labelEx($model,'type')?> </div> <div class="controls"> <div class="input-append"> <?php echo $form->dropDownList($model,'type',Menu::getSimpleTypes());?> <button class="btn" id="data-select-btn" data-loading-text="..." type="button"><i class="icon-list"></i></button> </div> </div> </div> <!-- Уведомление о том, что элемент был выбран. Крайне необходим для действия Update, чтобы было видно, что выбрано--> <div id="data-info" class="alert alert-success controls <?if($model->isNewRecord):?>hide<?endif;?>"> <i class="icon-file"></i> <span class="info"> <?if(!$model->isNewRecord) echo $model->data_name?> </span> </div> <!-- Кнопка создания/сохранения--> <div class="form-actions"> <?php $this->widget('bootstrap.widgets.TbButton', array( 'buttonType'=>'submit', 'type'=>'primary', 'label'=>$model->isNewRecord ? 'Create' : 'Save', )); ?> </div> <?php $this->endWidget(); ?> <!-- Модальное окошко для выбора нужного материала--> <?php $this->beginWidget('bootstrap.widgets.TbModal', array('id'=>'dataModal')); ?> <div class="modal-header"> <a class="close" data-dismiss="modal">×</a> <h4><?=Yii::t("menu", "Выберите материал")?></h4> </div> <div class="modal-body"></div> <div class="modal-footer"> <?php $this->widget('bootstrap.widgets.TbButton', array( 'label'=>Yii::t("menu", "Отмена"), 'url'=>'#', 'htmlOptions'=>array('data-dismiss'=>'modal'), )); ?> </div> <?php $this->endWidget(); ?> <script> // Функция для вызова из модального окошка function selectData(id, name) { $("#Menu_data_id").val(id); $("#data-info .info").html(name); $("#data-info").show(); $('#dataModal').modal("hide"); } // Обнуляем data_id если меняем тип данных $('#Menu_type').change(function(){ $("#Menu_data_id").val(""); $("#data-info").hide(); }) // Функция которая показывает модальное окно с данными для выбора, полученными через AJAX $('#data-select-btn').click(function(){ var buttn = this; $(buttn).button('loading'); $.ajax({ url: "<?php echo $this->createAbsoluteUrl('menu/loadData') ?>", cache: false, data: "type="+$("#Menu_type").val(), success: function(html){ $(".modal-body").html(html); $(buttn).button('reset'); $('#dataModal').modal().css({ width: 'auto', 'margin-left': function () { return -($(this).width() / 2); }, }); } }); }) </script>
Собственно это весь файл формы. Основные элементы тут, это наше поле и функция для AJAX запроса данных.
Как вы можете видеть, функция получения данных, обращается к экшну menu/loadData. Посмотрим, что он делает:
Контроллер (menuController.php)
public function actionLoadData($type) { $model_name = Menu::$types[$type]['model']; $model = new $model_name('search'); // создаем модель данных нужного типа if(isset($_GET[$model_name])) // чтобы работали функции поиска нужно передать параметры в модель $model->attributes=$_GET[$model_name]; $this->renderPartial(Menu::$types[$type]['view'],array( 'model'=>$model, ), false, true); // обязательно ставим $processOutput = true, чтобы работали скрипты в модальном окошке. }
Стоить отметить, что нужно установить для функции renderPartial параметр $processOutput = true, иначе не будут загружены скрипты, подключаемые виджетами в view файле.
Еще нужно уточнить очень важный момент, при установке параметра $processOutput, будут подгружены все файлы, включая и те, что уже были подключены на главной странице, что очень критично в случае, например, JQuery. Поэтому, советую установить расширение NLSClientScript, оно проконтролирует, чтобы все файлы подключались единожды.
View файл для модального окошка (docs_grid.php)
View файлы надо хранить в предназначенной для этого папке контроллера, т.е. в нашем случае это будет /protected/views/menu
Теперь разберем один из view файлов для модального окошка. Можно использовать любой удобный вид для выбора данных, главное чтобы он работал через объект модели. Мне нравится CGridView, т.к. в нём есть все необходимое: поиск, пагинация и сортировка.
$this->widget('bootstrap.widgets.TbGridView',array( 'id'=>'docs-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, 'columns'=>array( 'id', 'title', 'updated', array( 'class'=>'CButtonColumn', 'template' => "{insert}", 'buttons' => array( "insert" => array( 'label' => "выбрать", 'options' => array( "class" => "btn btn-mini btn-success", "onclick" => 'selectData($(this).parent().parent().children(":nth-child(1)").text(),$(this).parent().parent().children(":nth-child(2)").text());', ) ), ) ), ), ));
Тут ничего лишнего, один единственный грид с кнопками выбора элемента. В примере использован грид от bootstrap, но можно использовать стандартный CGridView.
Но тут есть одна хитрость. Как вы заметили, на кнопке выбора элемента висит невзрачный скрипт, выдирающий из таблицы id и title элемента. К сожалению, виджет GridView не позволяет оперировать данными отдельно взятой записи. Поэтому приходится таким вот образом получать нужные id и title, которые передаются функции selectData, описанной в view файле _form.php.
Вот собственно и всё. Для того чтобы добавить новый тип данных достаточно дописать еще одну строчку в массиве созданном в модели Menu.php и написать или сгенерировать, при помощи Gii, модель ActiveRecord для нового типа.
ссылка на оригинал статьи http://habrahabr.ru/post/178303/
Добавить комментарий