Snippets

Create an account or login to be able to add, comment and rate snippets.

Navigation

Form validation in AJAX

The example is for a blog. The page that displays a post also proposes an AJAX form to add a comment. We want that when the validation of this form fails, it displays again in the page with an error message, and when the validation succeeds, the form is replaced byu the comment just posted.

The idea is to take advantage of the way the update option of the form_remote_tag() helper works. It accepts an associative array, where you can specify different zones to update in case of success and failure. The only problem is that for Prototype, a failure is a return code other than 2XX. So when we return the form showing the error message again, we need to set the status code to 404, for instance, for Prototype to choose to update the correct zone.

That, plus the usual use of partials here and there, and you have a working solution:

in modules/post/actions/action.class.php

// Display the form
public function executeShow()
{
  $this->post = PostPeer::retrieveByPk($this->getRequestParameter('post_id'));
}

in modules/post/templates/showSuccess.php

// Display question detail here
...
// Beginning of Comment zone
<div id="added_comment" style="display_none"> </div>
<div id="add_comment">
  <?php include_partial('comment/add_comment', array('post' => $post)) ?>
 </div>

in modules/comment/templates/_add_comment.php

<?php use_helper('Javascript', 'Validation') ?>
<?php echo form_remote_tag(array(
  'url'     => 'comment/add',
  'update'  => array('success' => 'added_comment', 'failure' => 'add_comment'),
  'script'  => true,
  'loading' => "Element.show('indicator')",
  'success' => "Element.hide('indicator');Element.show('added_comment');Element.hide('add_comment');",
  'failure' => "Element.hide('indicator');",
)) ?>
  <?php echo input_hidden_tag('post_id', $post->getId()) ?>
  <?php echo form_error('body') ?>
  <label for="body">Your comment</label>
  <?php echo textarea_tag('body') ?>
  <?php echo submit_tag('Send') ?>
</form>

in modules/comment/validate/add.yml

methods:
  post: [body]
 
fillin:
  activate:        Yes
 
names:
  body:
    required:      Yes
    required_msg:  You must provide a comment
    validators:    spamValidator
 
spamValidator:
   class:          sfRegexValidator
   param:
     match:        No     
     pattern:      /http.*http/
     match_error:  Do not provide more than one URL - It is considered Spam

in modules/comment/actions/action.class.php

public function handleErrorAdd()
{
  $this->post = PostPeer::retrieveByPk($this->getRequestParameter('post_id'));
  $this->getResponse()->setStatusCode(404);
  return sfView::ERROR;
}
 
public function executeAdd()
{
  $post = PostPeer::retrieveByPk($this->getRequestParameter('post_id'));
  $this->forward404Unless($post);
  $comment = new Comment();
  $comment->setPost($post);
  $comment->setAuthor($this->getUser()->getAuthor());
  $comment->setBody($this->getRequestParameter('body'));
  $comment->save();
  $this->comment = $comment;
}

in modules/comment/templates/addError.php

<?php include_partial('comment/add_comment', array('post' => $post)) ?>

in modules/comment/templates/addSuccess.php

Your comment has been added:
<div class="comment">
  <?php echo $comment->getBody() ?>
</div>

As a bonus, the form is still there after a successful submission (but hidden), so with a few more lines of code, you can still provide a Digg-like "edit comment for the next 60 seconds" feature.

by Francois Zaninotto on 2006-10-16, tagged ajax  forms  validation 

Comments on this snippet

gravatar icon
#1 Frank Stelzer on 2006-10-23 at 03:20

I did everything like in the snippet, but the comment form will not be hidden, if the action was successful. Any ideas, why this happens? There are no javascript errors on my page.

gravatar icon
#2 Frank Stelzer on 2006-10-23 at 04:43

Okok, i found my mistake. I forgot to include the indicator element and so nothing at all happend after the successful action.

gravatar icon
#3 Pierre Minnieur on 2006-11-07 at 05:38

I think it's better to let the added comment appear 'after' the last comment in the current list, and not display it in a custom layer. Just a suggestion ;)

gravatar icon
#4 Jordi Backx on 2007-01-16 at 12:07

Don't forget to remove any global layout in the addError and addSuccess ( the resulting success/error templates corresponding to the url parameter in form_remote_tag())templates to prevent loading the layout content twice into each other.

In the view.yml of the module or app: addSuccess: has_layout: no

addError: has_layout: no

gravatar icon
#5 omoratin on 2007-03-06 at 08:40

Thanks for this code. I used it for my stuff and did some modifications. I didn't want to redisplay the form but keep it on screen and just get the error messages. To do this I only get the form errors in the Error action. To do this I wrote a small helper that creates JS code on the fly and display the errors for the fields having issues and hiding the ones for which there is no problem.

For example, in my actionError.php I have

<?php use_helper('jsErrorDisplay'); js_error_display($sf_request); ?> <div class="form_incomplete"> Please verify your information. </div>

My helper is

function js_error_display($request) {

$jscode = &#039;function jsErrorDisplay() {&#039;;
foreach($request-&gt;getParameterHolder()-&gt;getAll() as $param =&gt; $value) {
 
    if ($param != &#039;module&#039; &amp;&amp; $param != &#039;action&#039;) {
 
        if ($request-&gt;getError($param)) {
            $jscode .= &#039;document.getElementById(&quot;error_for_&#039; . $param . &#039;&quot;).innerHTML=&quot;&#039; . $request-&gt;getError($param) . &#039;&quot;;&#039;;
            $jscode .= &#039;document.getElementById(&quot;error_for_&#039; . $param . &#039;&quot;).style.display=&quot;block&quot;;&#039;;
        } else {
            $jscode .= &#039;document.getElementById(&quot;error_for_&#039; . $param . &#039;&quot;).innerHTML=&quot;&quot;;&#039;;
            $jscode .= &#039;document.getElementById(&quot;error_for_&#039; . $param . &#039;&quot;).style.display=&quot;none&quot;;&#039;;         
        }
 
    }
}
$jscode .= &#039;}&#039;;
$jscode .= &#039;jsErrorDisplay();&#039;;
 
use_helper(&#039;Javascript&#039;);
echo javascript_tag($jscode);

}

gravatar icon
#6 jignesh vishavadia on 2007-07-26 at 11:50

Thanks

gravatar icon
#7 Antonio Fijo on 2007-07-30 at 06:24

Hi, I want a mixed behaviour of that. I mean, when there's an error I want the form in the screen and also the validation error messages. I have added this code at the begining of the form partial file.

<?php if ($sf_request->hasErrors()): ?>
<div id="errors" style="padding:10px;"> Errores: <ul> <?php foreach ($sf_request->getErrors() as $error): ?> <li><?php echo $error ?></li> <?php endforeach; ?> </ul> </div> <?php endif; ?>

The thing is that the validation is done, no submit is done but I can't see the errors on the screen. Any useful tip?

Thanks in advance.

You need to create an account or log in to post a comment or rate this snippet.