CGridView и обновление через jQuery AJAX

от автора

Недавно столкнулся с проблемой, когда мне понадобилось при обновлении CListView через AJAX совершить дополнительные манипуляции.

Самый простой 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *