Я создаю веб-приложение, которое включает в себя формы. Используемая структура – symfony2. Я хотел бы иметь все, что работает для пользователей без javascript, а затем постепенно улучшать опыт для людей с включенным javascript. Я планирую использовать JQuery в качестве моей библиотеки javascript.
Я хотел бы иметь формы, в которых он отправляет и выводит сообщение с использованием компонента FlashMessage, если у пользователя нет javascript, и запрос не является ajax-запросом.
Для пользователей с поддержкой javascript я хотел бы вернуть сообщение им, если подача прошла успешно. Затем javascript должен немедленно отобразить это сообщение в div.
В то же время мне приходится иметь дело с сообщениями об ошибках. Для пользователей без javascript вся форма будет повторно передана с сообщениями об ошибках.
Для пользователей с javascript ответ ajax должен быть массивом, содержащим идентификатор входного компонента и сообщение об ошибке. Затем Javascript должен вставить их в форму без обновления.
Глядя на книгу symfony2, я могу видеть следующее для представлений, затем я изменил ее, чтобы проверить, является ли запрос ajax-запросом:
public function newAction(Request $request) { // just setup a fresh $task object (remove the dummy data) $task = new Task(); $form = $this->createFormBuilder($task) ->add('task', 'text') ->add('dueDate', 'date') ->getForm(); if ($request->getMethod() == 'POST') { $form->bindRequest($request); if ($form->isValid()) { // perform some action, such as saving the task to the database if ($this->request->isXmlHttpRequest(){ //return data ajax requires. } return $this->redirect($this->generateUrl('task_success')); } } // ... }
Проблема, которую я нахожу при таком подходе, заключается в том, что она не очень элегантна. Мне нужно добавить эту дополнительную проверку в каждую форму. Более того, этот метод не будет работать, если сказать, что запрос приходит через Zend_AMF.
Есть ли лучшие способы сделать это?
Другой вариант заключается в том, чтобы ваш контроллер возвращал что-то другое, кроме ответа, и использовал событие kernel.view
чтобы преобразовать это в то, что вы хотите. Это было бы лучше DRY, потому что логика инкапсулирована в слушателя. Вам просто нужно решить, что вы хотите, чтобы ваши контроллеры возвращались.
if ($form->isValid()) { return new Something($redirectUrl, $ajaxData); }
И в вашем слушателе:
public function onKernelView($event) { $request = $event->getRequest(); $result = $event->getControllerResult(); if ($result instanceof Something) { if ($request->isXmlHttpRequest()) { $event->setResponse(new Response(json_encode($result->getAjaxData()))); } else { $event->setResponse(new RedirectResponse($result->getRedirectUrl())); } } }
Вы можете вернуть ответ, как обычно, и принять решение о представлении, если хотите сделать частичный рендеринг или отобразить всю страницу, расширяя различные макеты ветвей.
В вашем контроллере:
if ($form->isValid()) { // perform some action, such as saving the task to the database $this->get('session')->setFlash('notice', 'Success!'); return $this->render('success.html.twig'); } else { $this->get('session')->setFlash('notice', 'Please correct the errors below'); }
И в ваших взглядах:
{# new.html.twig #} {% extends app.request.isXmlHttpRequest ? 'ajax.html.twig' : 'layout.html.twig' %} {% block content %} {% if app.session.hasFlash('notice') %} <div class="flash-notice">{{ app.session.flash('notice') }}</div> {% endif %} <form ...> {{ form_widget('form') }} <input type="submit" /> </form> {% endblock %} {# success.html.twig #} {% extends app.request.isXmlHttpRequest ? 'ajax.html.twig' : 'layout.html.twig' %} {% block content %} {% if app.session.hasFlash('notice') %} <div class="flash-notice">{{ app.session.flash('notice') }}</div> {% endif %} {% endblock %} {# ajax.html.twig #} {% block content %}{% endblock %} {# layout.html.twig #} <html> <body> normal page content <div id='content'> {% block content %}{% endblock %} </div> </body> </html>
(Возможно, вы захотите включить флэш-сообщения в макрос или включить …)
Теперь просто сделайте возврат из запроса ajax в тег, который вы хотите поместить:
$.ajax({ type: "POST", url: "tasks/new", data: {task: foo, dueDate: bar}, }).done(function( msg ) { $('#content').html(msg); });
До тех пор, пока вы можете обобщить вид ответа, необходимый для форм без аякса и для форм ajax, вы можете отменить это решение ajax / non-ajax другому методу, а затем просто:
return handleResponseType(whateverInfoIsNeededToHandleIt)
Затем у вас есть полный контроль над тем, что передается вашей решающей функции.