symfony2中的Ajax表单提交,没有javascript的用户优雅降级

时间:2011-10-16 08:53:18

标签: php ajax forms symfony

我正在构建一个涉及表单的Web应用程序。正在使用的框架是symfony2。我想让一切为没有javascript的用户工作,然后逐步增强启用javascript的人的体验。我打算用JQuery作为我的javascript库。

如果用户没有javascript并且请求不是ajax请求,我希望有提交的表单并使用FlashMessage组件显示消息。

对于有javascript支持的用户,如果提交成功,我想将消息返回给他们。然后,javascript应立即在div中显示该消息。

与此同时,我需要处理错误消息。对于没有javascript的用户,整个表单将被重新呈现,并显示错误消息。

对于使用javascript的用户,ajax响应应该是一个包含输入组件的id和错误消息的数组。然后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进入,则此方法不起作用。

有没有更好的方法来做到这一点?

3 个答案:

答案 0 :(得分:24)

另一种选择是让控制器返回响应之外的其他内容,并使用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()));
        }
    }
}

答案 1 :(得分:5)

您可以像往常一样返回响应并决定视图是否要通过扩展不同的树枝布局来进行部分渲染或渲染整个页面。

在您的控制器中:

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>

(您可能希望将Flash消息部分放在宏或包含...中)

现在只需将ajax请求的返回值呈现给您要放置的标记:

$.ajax({
  type: "POST",
  url: "tasks/new",
  data: {task: foo, dueDate: bar},
}).done(function( msg ) {
  $('#content').html(msg);
});

答案 2 :(得分:2)

只要您可以概括非ajax表单和ajax表单所需的响应类型,您可以将ajax / non-ajax的决定权转移到另一个方法,然后只需:

return handleResponseType(whateverInfoIsNeededToHandleIt)

然后,您可以完全控制传递给决策函数的内容。