Yii: Выводим модальное окно (быстрая форма обратной связи) на CJuiDialog

Yii: Выводим модальное окно (быстрая форма обратной связи) на CJuiDialog C помощью CJuiDialog можно придать дополнительной функциональности любому сайту, а в Yii Framework это сделать особенно удобно, так как jQuery уже встроен в Yii. Диалоговые окна на сайте позволяют вывести, например, форму заказа обратного звонка, или форму отправки сообщения администратору сайта без перехода на отдельную страницу.

Для создания модальных (или диалоговых) окон в Yii есть удобный класс – CJuiDialog. Это окна, открывающиеся с помощью jQuery, когда посетитель сделает определённое вами действие, например, нажмёт по ссылке "Отправить заявку".

вывод модального окна в Yii с помощью класса CJuiDialog

Покажу как сделал вывод такого окна у нас на loco.ru. Хочется сказать спасибо Белякову Юрию за внятную статью Yii - Использование CJuiDialog для создания модальных окон. Я лишь конкретизирую её и дополню подключением стилей оформления формы в модальном окне для моего случая.

Итак, создаю модель /models/QuickForm.php

<?php  

class QuickForm extends CFormModel
{
    public $name;
    public $email;
    public $phone;
    public $message;
    public $current_url;
    public $verifyCode; 
  
    public function rules()
    {
        return array(
            array('name, email, message', 'required'),
            array('phone, current_url', 'safe'),
			array('verifyCode', 'captcha', 'allowEmpty'=>!extension_loaded('gd') /*'captchaAction' => 'site/captcha'*/),
        );
    }
  

    public function attributeLabels()
    {
        return array(
            'name'=>'Ваше имя',
            'phone'=>'Телефон',
            'email'=>'Email',
            'message'=>'Опишите задачу',
            'verifyCode'=>'Код проверки',
        );
    }
}
Дальше в представлении (удобно в layouts/main.php, чтобы можно было вызвать в любом месте сайта) добавляю само диалоговое окно в скрытом виде (опция 'autoOpen'=>false)
<?php $this->beginWidget('zii.widgets.jui.CJuiDialog', array(
'id' => 'mydialog',
        'options' => array(
            'title' => 'Отправить сообщение',
            'autoOpen' => false,
            'modal' => true,
            'resizable'=> false,
        ),
    ));
    $qForm = new QuickForm; 
    $form = $this->beginWidget('CActiveForm', array(
                'id' => 'quick-form',
                'enableClientValidation' => true,
                'clientOptions' => array(
                    'validateOnSubmit' => true,
                ),
'htmlOptions'=>array(
         'class'=>'form',
),
                'action' => array('site/quick'), // когда форма показывается и в других контроллерах, не только 'site', то я в каждый из этих контроллеров вставил actionQuick, a здесь указал — array('quick'); почему-то не получается с array('//site/quick')
            ));
    ?>
        <?php echo $form->errorSummary($qForm); ?>

            <?php echo $form->labelEx($qForm,'name'); ?>
            <?php echo $form->textField($qForm,'name', array('size'=>30)); ?>
            <?php echo $form->error($qForm,'name'); ?>

<?php echo $form->labelEx($qForm,'email'); ?>
            <?php echo $form->textField($qForm,'email', array('size'=>30)); ?>
            <?php echo $form->error($qForm,'email'); ?>

            <?php echo $form->labelEx($qForm,'phone'); ?>
            <?php echo $form->textField($qForm,'phone', array('size'=>30)); ?>
            <?php echo $form->error($qForm,'phone'); ?>

<?php echo $form->labelEx($qForm,'message'); ?>
<?php echo $form->textArea($qForm,'message',array('rows'=>6, 'cols'=>31)); ?>
<?php echo $form->error($qForm,'message'); ?>

            <?php echo CHtml::submitButton('Отправить'); ?>

    <?php $this->endWidget();
    $this->endWidget('zii.widgets.jui.CJuiDialog');

    ?>
Вызов действия 'action' => array('site/quick') у меня не срабатывал на страницах, представленных не с помощью контроллера 'site' (в котором и находится actionQuick), поэтому пришлось вставить этот экшн в те контроллеры, которые генерируют страницы с возможностью всплывающей формы. Это работает на loco.ru.
 
Для того, чтобы потом подключить css оформление формы, добавил в вызове виджета формы – 'htmlOptions' – класс 'form'. В приведенном коде у формы будет обращение action к контроллеру site, действию quick, поэтому создаём логику работы в этом действии: дописываем в контроллер /controllers/SiteController.php действие для отправки email администроатору сайта
public function actionQuick() { 
       $model=new QuickForm;
           $model->attributes=$_POST['QuickForm'];
           if($model->validate()) {
               $headers="From: $model->email\r\nReply-To: $model->email";
               $body = "\n\nОтправитель: ".$model->name."\t Телефон: ".$model->phone."\t Email: ".$model->email."\t Задача: ".$model->message;
               mail(Yii::app()->params['adminEmail'],'Письмо с сайта loco.ru от'.$model->name, $body, $headers);
           }
       $this->redirect(array('site/index'));
}   
Последнее, что остаётся для работы модального окна CJuiDialog, это добавить ссылку на вызов этого окна в том месте страницы, шаблона, или в любом каком захотите представлении.
<?php echo CHtml::link('Отправить заявку на разработку интернет сайта', '#', array(
'onclick'=>'$("#mydialog").dialog("open"); return false;',
'class'=>'g-button g-button-orange',
'title'=>'Отправить заявку на разработку интернет сайта',
));?>
После того, как пользователь нажмёт эту ссылку, ему покажется диалоговое окно с нашей формой, он заполнит её и отправит. Сработает действие в контроллере и нам на почту придёт email с данными из формы, а пользователя "редиректнит" на главную страницу сайта. Тут лучше сделать не редирект а вывод сообщения, что письмо было успешно отправлено, редирект не очень понятно воспринимается...
 
Для оформления формы я дублировал form.css, назвав её quickform.css и исправил везде div на form и изменив некоторые стили для особого оформления формы в диалоговом окне (потому что class="form" для диалогового окна относится к <form>, а не к <div>).
 
Дополнение 1 (19.12.2012):

Позиционирование CJuiDialog Опция 'position'.

На форумах встречаются вопросы про позиционирование диалогового окна в браузере. Вот список свойств Dialog Widget: http://api.jqueryui.com/dialog/#option-position. Среди них есть 'position' и вроде как можно его устанавливать как и другие, но указание его в массиве options
'options' => array(
            'title' => 'Отправить сообщение',
            'autoOpen' => false,
            'modal' => true,
            'resizable'=> false, 
'position'=> '{ my: "left top", at: "left bottom", of: button }',
        ),
не позиционирует диалог по левому краю. Не знаю почему. Но работает 'position'=>'left'. Ещё работает, когда указать это свойство на ссылке, по которой вызывается это диалоговое окно
...
'onclick'=>'$("#mydialog").dialog("option", "position", "left").dialog("open"); return false;',
...
Дополнение 2 (19.12.2012):

Вывод пользователю сообщения об успешной отправке его заявки.

Создаём представление /views/_dialog.php

<?php
if($flashes = Yii::app()->user->getFlashes()) {
    foreach($flashes as $key => $message) {
        if($key != 'counters') {
            $this->beginWidget('zii.widgets.jui.CJuiDialog', array(
                        'id'=>$key,
                        'options'=>array(
                            'show' => 'blind',
                            'hide' => 'explode',
                            'modal' => 'true',
                            'title' => $message['title'],
                            'autoOpen'=>true,
'dialogClass'=>$message['type'],
'buttons'=>array('Закрыть'=>'js:function() {$(this).dialog("close");}'),
'close'=>'js:function(e,ui){
                       $(this).dialog("destroy").remove();
                   }',
                        ),
'htmlOptions'=>array(
        'class'=>'flash-success',
),
                        ));

            printf('<span class="dialog">%s</span>', $message['content']);
            $this->endWidget('zii.widgets.jui.CJuiDialog');
        }
    }
}
В моделях создаём модель /models/Dialog.php
<?php
class Dialog {
    public static function Message($type, $title, $message, $id = 0) {
        if($id == 0)
            $id = rand(1, 999999);
        Yii::app()->user->setflash($id, array('type'=>$type,'title' => $title, 'content' => $message) );
    }
}
?>
И главный фокус - в layouts/main.php вызываем перед $content кусок _dialog:
<?php $this->renderPartial('//site/_dialog'); ?>
<?php echo $content; ?>
 
Теперь я в контроллере каком нужно пишу в нужном действии (здесь в actionQuick после отправки email)
Dialog::message('flash-success', 'Отправлено!', 'Спасибо, '.$model->name.'! Ваше письмо отправлено!');
//Yii::app()->user->setFlash('messageSent', 'Спасибо, '.$model->name.'! Ваше письмо отправлено!');
            $this->redirect($model->current_url); 
Закомментировал строку вместо которой теперь использую вызов модели Dialog. И затем редирект на ту страницу с которой пользователь нажал ссылку, выдавшую диалоговое окно - current_url - это скрытое поле в форме из диалогового окна, в которое вбирается текущий url (а он может быть произвольным в общем-то, где дали ссылку, там и выкинулось окошко диалоговое!), ведь потом вызывается конкретный контроллер //site/quick, которому надо знать на какое представление перекинуть пользователя.
 
Добавление 3.

Автоматическое закрытие диалогового окна CJuiDialog в Yii после 5 секунд.

Спасибо совету mdomda на форуме yiiframework.com. В options в вызове виджета CJuiDialog в /views/site/_dialog.php надо добавить опцию open с установкой setTimeout на 5 секунд. Век живи – век учись.
$this->beginWidget('zii.widgets.jui.CJuiDialog', array(
    'id'=>'dialogbox',
    // additional javascript options for the dialog plugin
    'options'=>array(
        'title'=>'Dialog box 1',
        'autoOpen'=>true,
        'open' => 'js:function(event, ui) {            
            setTimeout(function() {
                 $("#dialogbox").dialog("close");
            }, 5000);
        }',
    ),

));
Идентификаторы #dialogbox должны совпадать.
 
P.S. По дороге всплыл интересный конфликт между jquery скриптом для всплывающих картинок lytebox (мне он очень нравился!) и встроенным в Yii - jQuery. Пришлось отказаться от lytebox, видимо в него встроен jQuery (не нашёл как) и получался конфликт вызовов, вследствие чего диалог CJuiDialog не появлялся при его вызове. Colorbox же для своей работы требует именно дополнительного подключения jquery отдельной строкой в <head>, что очень нам удобно и на руку – (мы же в Yii, и это делать не надо – jquery уже встроен в Yii framework и мы преспокойно вызываем лишь скрипт colorbox). Может в будущих версиях lytebox предоставят возможность скачать скрипт без jQuery. А пока для Yii удобнее colorbox.
 

almix
Разработчик Loco, автор статей по веб-разработке на Yii, CodeIgniter, MODx и прочих инструментах. Создатель Team Sense.

Вы можете почитать все статьи от almix'а.



Другие статьи по этой теме:
Комментарии (4)     Подпишитесь на RSS комментариев к этой статье.

4 комментариев

#754
Александр говорит:
January 5, 2013 at 09:32 pm
К вопросу о позиционировании окна -  если строку:  'position'=> '{ my: "left top", at: "left bottom", of: button }      сделать так:  'position'=> array('my'=>'left top', 'at'=>'left bottom', 'of'=> 'button'),  - начинает работать
#1041
Степан говорит:
November 29, 2013 at 09:33 pm
Спасибо за статью, помогла быстро  разобраться 
#1097
WarGot говорит:
February 18, 2014 at 05:15 pm
Полезная статья. Помогло разобраться. Спасибо.
#1279
Andrey говорит:
November 19, 2014 at 09:46 am
Здорово, но у меня такая проблемка, 


Exception:

Не определено свойство "QuickForm.current_url".

Подскажите в чем причина может быть?

Не определено свойство "QuickForm.current_url".

almix: добавьте в модель QuickForm.php - public $current_url; (поправил код в этой статье)

Leave a Comment

Fields with * are required.

Картинка с кодом валидации
Пожалуйста введите символы с картинки. Регистр букв неважен.