Самый простой use-case такого рода, это когда при снятии материла с публикации или его удалении вам необходимо вывести пользователю какой-нибудь notice.
У меня в приложении есть совсем такой миниатюрный стэк для сообщений. Пока я делаю свои дела в контроллере, я, в случае острой необходимости, запихиваю туда различные сообщения:
Yii::app()->addMessage('Добро пожаловать, '.Yii::app()->user->fullname);
после чего при рендере они попадают в виде скрипта в итоговый html:
$.pnotify.defaults.history = false; {% if Yii.app.messageStack.messages() %} {% for message in Yii.app.messageStack.messages() %} $.pnotify({ title: {{message.title?'"'~message.title~'"':'null'}}, text: "{{message.text}}", type: "{{message.type}}", hide: {{ message.hide?'true':'false' }}, sticker: false }); {% endfor %} {% endif %}
Страница загрузилась, пользователь проинформирован — всё круто.
В случае с ajax-вызовами тоже всё нормально — получил ответ от браузера, вставил его в DOM, jQuery выстрелил скриптами.
И вот тут мне понадобилось вывести пользователю сообщение о том, что удаление объекта успешно выполнено (любят люди, когда перед ними отчитываются, а мне не сложно), причем удаление инициируется из CGridView посредством его свойства updateSelector — я туда запихал css-селектор ссылки на удаление объекта:
updateSelector: '{page}, {sort}, .delete'
Для ajax-ответа у меня был специально заготовлен layout, я в него дописал кусок для вывода сообщений:
<script language="text/javascript"> {% for message in Yii.app.messageStack.messages() %} $.pnotify({ title: {{message.title?'"'~message.title~'"':'null'}}, text: "{{message.text}}", type: "{{message.type}}", hide: {{ message.hide?'true':'false' }}, sticker: false }); {% endfor %} </script> {% block ajax %} // Вместо этого блока в результат выводится CGridView {% endblock %}
Запускаю — запрос прошёл, сообщений нет. После недолгих раскопок стало ясно почему — jQuery исполняет скрипты только при вставке их в DOM, а CGridView умный, он умеет после одного запроса обновлять несколько DOM-элементов, и контент для обновления принудительно выбирает из ответа по ID, вследствие чего наш скрипт просто-напросто игнорируется.
Есть два решения этой проблемы. Первый — использовать механизм CGridView. Мы можем сказать ему, какие DOM-элементы обновлять при получении ответа:
ajaxUpdate: 'listing-grid,ajax-scripts',
Оборачиваем наши скрипты:
<div id="ajax-scripts"> <script language="text/javascript"> {% for message in Yii.app.messageStack.messages() %} $.pnotify({ title: {{message.title?'"'~message.title~'"':'null'}}, text: "{{message.text}}", type: "{{message.type}}", hide: {{ message.hide?'true':'false' }}, sticker: false }); {% endfor %} </script> </div>
После чего добавляем контейнер с ID=ajax-scripts в html и при обновлении таблицы компонент запихает в него содержимое этого контейнера, полученное с ответом, а послушный jQuery с радостью выполнит содержащиеся там скрипты.
Тут есть нюанс — у меня один и тот же layout отвечает за вывод ajax-ответов и от CGridView, и от моих контроллеров. Ответы от моих контроллеров я вставляю в DOM целиком, в результате чего в середину страницы вставится и
<div id="ajax-scripts">
И еще вам придется во всех вызовах createWidget для CGridView принудительно передать ему строку с ID DOM-элементов для обновления.
Второе решение, на мой взгляд, более лаконичное и универсальное:
$(document).ajaxSuccess(function(e, xhr, settings, data){ if(typeof data == 'string'){ $(data).filter('script').each(function(){ eval($(this).html()); }).replaceWith(''); } } );
Просто вырезаем из ответа все тэги script верхнего уровня, принудительно их выполняя. Дополнительно оборачивать скрипты уже не нужно.
Какое из них использовать, не дай бог у вас приключится такая же беда, как у меня, — решать вам 🙂
ссылка на оригинал статьи http://habrahabr.ru/post/161225/
Добавить комментарий