Złożenie formularza Ajax w symfony2 z gracją degradacji dla użytkowników bez javascript

Buduję aplikację internetową, która zawiera formularze. Używany framework to symfony2. Chciałbym, aby wszystko działało dla użytkowników bez javascript, a następnie stopniowo zwiększać doświadczenie dla osób z włączoną obsługą javascript. Planuję użyć JQuery jako mojej biblioteki javascript.

Chciałbym mieć formularze, w których wysyła i wyświetla wiadomość za pomocą komponentu FlashMessage, jeśli użytkownik nie ma javascript i żądanie nie jest Ajaxem Prośba.

Dla użytkowników z obsługą javascript, chciałbym zwrócić wiadomość do nich, jeśli zgłoszenie się powiedzie. Następnie javascript powinien natychmiast wyświetlić tę wiadomość w div.

W tym samym czasie, muszę radzić sobie z komunikatami o błędach. Dla użytkowników bez javascript, cały formularz zostanie ponownie wysłany z komunikatami o błędach w miejscu.

Dla użytkowników z javascript, odpowiedź ajax powinna być tablicą zawierającą id komponentu input i Komunikat o błędzie. Javascript powinien następnie wstawić je do formularza bez odświeżania.

Patrząc na Księgę symfony2, widzę, co następuje dla zgłoszeń, następnie zmodyfikowałem ją, aby sprawdzić, czy żądanie jest żądaniem 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'));
        }
    }

    // ...
}

Problem, który znajduję w tym podejściu jest to, że nie jest zbyt elegancki. Muszę dodać ten dodatkowy czek w każdym formularzu. Co więcej, ta metoda nie będzie działać, jeśli powiedzmy, że żądanie przychodzi przez Zend_AMF.

Czy są na to jakieś lepsze sposoby?
Author: F21, 2011-10-16

3 answers

Inną opcją jest zwrócenie przez kontroler czegoś innego niż odpowiedź i użycie zdarzenia kernel.view do przekształcenia tego w to, co chcesz. Byłoby to lepiej suche, ponieważ logika jest zamknięta w słuchaczu. Musisz tylko zdecydować, co chcesz, aby Twoje Kontrolery zwróciły.

if ($form->isValid()) {
    return new Something($redirectUrl, $ajaxData);
}

I w Twoim słuchaczu:

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()));
        }
    }
}
 24
Author: Kris Wallsmith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-10 15:09:23

Możesz zwrócić odpowiedź jak zwykle i zdecydować, czy chcesz wykonać częściowe renderowanie lub renderować całą stronę, rozszerzając różne układy gałązek.

W Twoim kontrolerze:

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');
}

I w Twoich poglądach:

{# 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>

(możesz umieścić część wiadomości flash w makrze lub nagłówku...)

Teraz po prostu wyrenderuj zwrot z żądania ajax do znacznika, który chcesz umieścić:

$.ajax({
  type: "POST",
  url: "tasks/new",
  data: {task: foo, dueDate: bar},
}).done(function( msg ) {
  $('#content').html(msg);
});
 5
Author: solarc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-11 12:48:22

Tak długo, jak można uogólnić rodzaj odpowiedzi potrzebnej dla formularzy innych niż ajax i dla formularzy ajax, można przenieść tę decyzję ajax / non-ajax do innej metody, a następnie po prostu mieć:

return handleResponseType(whateverInfoIsNeededToHandleIt)
Wtedy masz pełną kontrolę nad tym, co przechodzi do Twojej decydującej funkcji.
 2
Author: cdeszaq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-06 17:30:40