Пример будет реализован на Yii2 basic, для внедрения его на Yii2 advanced необходимо сменить локацию файлов соответствующую шаблону advanced и namespace (особых трудностей это вызвать не должно).
Устанавливаем Yii2 basic, Как установить Yii 2 basic, краткая инструкция.
Форма обратной связи во всплывающем окне будет работать на каждой странице сайта, поэтому реализуем виджет который будет инициализировать модель ContactForm, отправлять сообщение в случае правильного заполнения формы и сообщать об отправке пользователю (посетителю сайта).
Создаем класс виджет app/components/FBFWidget.php со следующим содержимым:
<?php namespace app\components; use Yii; use yii\base\Widget; use app\models\ContactForm; class FBFWidget extends Widget { public function run() { $model = new ContactForm(); if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) { Yii::$app->session->setFlash('contactFormSubmitted'); } return $this->render('fbfWidget', [ 'model' => $model, ]); } }
Содержимое класса виджета FBFWidget - простой виджет с кодом инициализации, проверки и отправки формы обратной связи, код практически идентичен содержанию метода (экшена) actionContact класса (контроллера) SiteController (app/controllers/SiteController.php).
Далее создадим представление (view) для виджета app/components/views/fbfWidget.php:
<?php use yii\helpers\Html; use yii\bootstrap\ActiveForm; use yii\captcha\Captcha; ?> <?php if (Yii::$app->session->hasFlash('contactFormSubmitted')) { ?> <?php $this->registerJs( "$('#myModalSendOk').modal('show');", yii\web\View::POS_READY ); ?> <!-- Modal --> <div class="modal fade" id="myModalSendOk" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="myModalLabel">Feedback form</h4> </div> <div class="modal-body"> <p>Thank you for contacting us. We will respond to you as soon as possible.</p> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> </div> </div> </div> </div> <?php } ?> <!-- Modal --> <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div class="modal-dialog" role="document"> <div class="modal-content"> <?php $form = ActiveForm::begin(['id' => 'contact-form']); ?> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> <h4 class="modal-title" id="myModalLabel">Feedback form</h4> </div> <div class="modal-body"> <?= $form->field($model, 'name')->textInput(['autofocus' => true]) ?> <?= $form->field($model, 'email') ?> <?= $form->field($model, 'subject') ?> <?= $form->field($model, 'body')->textarea(['rows' => 6]) ?> <?= $form->field($model, 'verifyCode')->widget(Captcha::className(), [ 'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>', ]) ?> </div> <div class="modal-footer"> <button type="button" class="btn btn-default" data-dismiss="modal">Close</button> <?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?> </div> <?php ActiveForm::end(); ?> </div> </div> </div>
Представление содержит два простых модальных окна bootstrap. Первое содержит сообщение об успешной отправки и автоматически выводится при отправке, второе содержит саму форму обратной связи (код формы идентичен форме из представления contact (app/views/site/contacts.php)).
Далее в основном макете (app/views/layouts/main.php) добавляем инициализацию виджета и ссылку (или кнопку) для вызова всплывающего (модального) окна с формой обратной связи.
Добавляем в use
виджет с формой обратной связи:
use app\components\FBFWidget;
Добавляем (меняем) ссылку для вывода всплывающего окна с формой обратной связи в меню (Nav::widget
):
['label' => 'Contact', 'url' => '#', 'options' => ['data-toggle' => 'modal', 'data-target' => '#myModal']],
Вызываем виджет со всплывающим (модальным) окном, которое содержит форму обратной связи:
<?= FBFWidget::widget([]) ?>
В итоге файл app/views/layouts/main.php будет выглядеть следующим образом (изменения в строках: 7, 38, 71):
<?php use yii\helpers\Html; use yii\bootstrap\Nav; use yii\bootstrap\NavBar; use yii\widgets\Breadcrumbs; use app\assets\AppAsset; use app\components\FBFWidget; AppAsset::register($this); ?> <?php $this->beginPage() ?> <!DOCTYPE html> <html lang="<?= Yii::$app->language ?>"> <head> <meta charset="<?= Yii::$app->charset ?>"> <meta name="viewport" content="width=device-width, initial-scale=1"> <?= Html::csrfMetaTags() ?> <title><?= Html::encode($this->title) ?></title> <?php $this->head() ?> </head> <body> <?php $this->beginBody() ?> <div class="wrap"> <?php NavBar::begin([ 'brandLabel' => 'My Company', 'brandUrl' => Yii::$app->homeUrl, 'options' => [ 'class' => 'navbar-inverse navbar-fixed-top', ], ]); echo Nav::widget([ 'options' => ['class' => 'navbar-nav navbar-right'], 'items' => [ ['label' => 'Home', 'url' => ['/site/index']], ['label' => 'About', 'url' => ['/site/about']], ['label' => 'Contact', 'url' => '#', 'options' => ['data-toggle' => 'modal', 'data-target' => '#myModal']], Yii::$app->user->isGuest ? ( ['label' => 'Login', 'url' => ['/site/login']] ) : ( '<li>' . Html::beginForm(['/site/logout'], 'post') . Html::submitButton( 'Logout (' . Yii::$app->user->identity->username . ')', ['class' => 'btn btn-link logout'] ) . Html::endForm() . '</li>' ) ], ]); NavBar::end(); ?> <div class="container"> <?= Breadcrumbs::widget([ 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], ]) ?> <?= $content ?> </div> </div> <footer class="footer"> <div class="container"> <p class="pull-left">© My Company <?= date('Y') ?></p> <p class="pull-right"><?= Yii::powered() ?></p> </div> </footer> <?= FBFWidget::widget([]) ?> <?php $this->endBody() ?> </body> </html> <?php $this->endPage() ?>
Результат:
Форма обратной связи во всплывающем окне для Yii2 готова, пример с исходным кодом можно скачать с github: https://github.com/devreadwrite/yii2-fbf-in-modal.
Комментарии
В чём может быть проблема?
ОтветитьСделал всё по статье. При подключении use app\components\FBFWidget; Yii2 не видит сomponents и phpStorm подсвечивает его красным. Соответственно ошибка Не вижу представление. Может его ещё где то нужно прописать ?
Шаблон basic? Если advanced то пути будут примерно такие use frontend\components
ОтветитьВиджеты не нужно прописывать в конфиге. Их нужно просто подключить на той странице, на который Вы их вызываете. В конфиге подключаются компоненты, модули, но не виджеты
ОтветитьСделал все как сказано, а окно не открывается. Хоть код подгружается. В чем может быть проблема?
ОтветитьСкорее всего чего-то не хватает в js bootstrap. Для начала можно проверить работу модальных окон стандартным кодом из примера bootstrap (https://getbootstrap.com/javascript/#modals):
Если модальное окно не открывается, то разобраться почему. Если открывается, то возможно id всплывающего окна из примера уже где-то встречается по коду и его нужно заменить.Ответить
Отличный урок 2 часа возни и все работает, большой респект автору я бы поучился у вас на создание приложений на yii2
ОтветитьДля полноты картины осталось добавить авторизацию/регистрацию/восстановление пароля во всплывающем окне
ОтветитьПредлагаю улучшение. Добавить отправку данных через AJAX и сменить капчу на reCAPTCHA (Добавляем reCAPTCHA от Google на сайт). Будет вообще гуд.
ОтветитьПоддерживаю, плюс добавить поле телефон и валидацию к нему
ОтветитьТолько отправку данных AJAX или валидацию тоже?
ОтветитьСпасибо за урок, попробовал на advanced, работает и письмо отправляется. Единственное подскажите с проблемой, у меня не происходит замена содержания модального окна в случае успешной отправки, т.е. окошко просто исчезает и окна myModalSendOk не получаю, и форма не сбрасывается после отправки.
ОтветитьПроверьте работает ли:
Попробуйте сделать вызов в другом месте и проверьте само модальном окно.Ответить
Заменил эту строчку $('#myModalSendOk').modal('show'); на alert('ok'); и при отправке алерт всплыл, означает ли это, что должно работать?
ОтветитьДобавьте это в body:
Показывает модальное окно?Ответить
Странно, алерт показало, а модальное окно нет
ОтветитьТогда проверьте еще раз все что связано с модальным окном, id, место вызова (может оно вызывается до инициализации bootsеrap) и т.д., как только оно будет выведено с помощью кода выше, сможете его вывести после успешной отправки ФОС
ОтветитьСпасибо за помощь, оказывается дело было в неправильном подключении bootstrap.css
Ответитьподелитесь, что сделали, столкнулся с той же проблемой :)
ОтветитьПредлагаю улучшения:
Ответить1. Виджет вынести из папки component в папку widgets где ему и место
2. В yii2 интегрирован bootstrap, так почему бы не использовать встроеный инструментрий для создания модальных окон?
Спасибо за урок! все получилось ,кроме того, что письмо на ящик не приходит ( В чем может быть проблема?
ОтветитьПосле успешной отправки формы попробовал обновить страницу...Происходит повторная отправка
Ответить<?php
Ответитьnamespace app\components;
use Yii;use yii\base\Widget;use yii\helpers\Html;use app\models\AskForm;
class AskFormWidget extends Widget { public $model; public function init() { parent::init(); }
public function run() { $model = new AskForm(); if ($model->load(Yii::$app->request->post()) && $model->validate()) { if ($model->sendAsk()) { Yii::$app->session->setFlash('successAskForm', Yii::t('app', $model->name)); //$model->unsetAttributes(); } else { Yii::$app->session->setFlash('errorAskForm', Yii::t('app', $model->name)); } Yii::$app->getResponse()->redirect( Yii::$app->request->url )->send(); Yii::$app->end(); } return $this->render('ask_form', [ 'model' => $model, ]); }}
добавьте в вид формы<?= Html::submitButton('Отправить',['class'=>'btn btn-success', 'Id'=> 'button'])?>
Ответить<div class="result">
<span id="answer"></span>
<span id="loader" style="display:none"></span>
</div>
и напишите ajax
var $form = $('#contact-form');
button = $('#button'),
answer = $('#answer'),
loader = $('#loader');
$form.on('beforeSubmit', function() {
var data = $form.serialize();
$.ajax({
url: $form.attr('action'),
type: 'POST',
data: data,
success: function (data) {
loader.fadeOut(300, function() {
answer.text('форма отправлина');
});
},
error: function(jqXHR, errMsg) {
alert(errMsg);
},
complete: function(){
$('#button').attr("disabled", true);
},
});
return false;
});
Доброе утро.
ОтветитьСкажите, а как сделать такой же виджет, но с возможностью редактировать данные?
Я пытаюсь сделать виджет подачи объявления.
С первой частью проблем нет. Создал виджет, подключил вид формы, создал пустой экземпляр модели.
Форма работает, объявление записывается в базу.
А вот с редактированием проблема.
Никак не могу понять, как при клике по ссылке "редактировать" передать заполненный экземпляр модели в модальное окно?
Как вариант можно сделать еще одну форму (или использовать туже). При нажатии на редактирование js должен делать ajax запрос на получения данных по id и заполнять форму. Только не забудте кнопку сохранить поправить таким образом, чтобы она не добавляла новые данные, а редактировала старые.
ОтветитьПоправить это значит на нужный action отправить форму?
Вот тут и возникает проблема, выдаётся ошибка , что вид не найден. И путь, где yii ищет вид, указывает в таком видеХотелось бы использовать одну и туже форму.
Я переделываю форму подачи объявления, предыдущий разработчик так и сделал, получал данные по id и в success ajax запроса заполнял форму.
Но форма очень объёмная и не все данные прописал он.
Я хочу упростить этот вариант. Получить только заполненную модель и передать в вид, ну как обычно это делается.
Но опять же, у меня возникает трудность с передачей в вид.
Саму форму widget-а я расположил в frontend/widget/sale_form/views/index.php.
В ajax действии я без проблем получаю данные по id, но вот вернуть результат через renderAjax не получается.
Вот как вернуть в данном случае вид с заполненной моделью?
Ответить
А если так: return renderAjax('//widget/sale_form/views/index, ['model' => $model);
ОтветитьИли так: return renderAjax('@frontend/widget/sale_form/views/index, ['model' => $model);
ОтветитьС двумя слешами не пробовал, сейчас проверю, а с @frontend ошибка, что не найден вид.
ОтветитьC @frontend
с двумя слешамиОтветить
Блин, чуть мозгом не потёк)))
ОтветитьТакой вариант работает
Или можно как-то передать заполненную модель как параметр виджета, что-то похожее на вот это
ОтветитьSaleForm::widget('model' => $model)
Сама форма подключается в layouts/main.php