0

I am using Symfony2 and perform an Ajax call to process a form. The problem I have is that by using the JsonResponse that returns me the driver tells me that the value is undefined. I wonder what I'm doing wrong to solve this problem and also if somehow could return errors to the form fields to validate from Ajax can show failures in the form without refreshing the page.

Controller:

public function createAction(Request $request){

$entity = new Student();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);

if ($form->isValid()) {
    $em = $this->getDoctrine()->getManager();
    $em->persist($entity);
    $em->flush();

    return new JsonResponse(array('message' => 'Success!'), 200);
 }

    return $this->render('BackendBundle:Student:new.html.twig', array(
    'entity' => $entity,
    'form'   => $form->createView(),
 ));
}

Ajax call:

$('.form_student').submit(function(event) {
 event.preventDefault();

  $.ajax({
  type: 'POST',
  url: Routing.generate('student_create'),
  data: $(this).serialize(),

  success: function(response) {

    alert(response.message);

  },
  error: function (xhr, desc, err){

    alert("error");
  }
 })
  return false;
});
Joseph
  • 859
  • 2
  • 13
  • 36
  • did you try to debug it? try doing `echo json_encode(['message' => 'success']; exit` in first line of createAction method. – Robert Dec 03 '15 at 15:20
  • Hi @Robert, forgive my ignorance because I am new to this, I put `echo json_encode(['message' => 'success'];` in the first line of createAction method and gives me error. Was it what you meant? – Joseph Dec 03 '15 at 15:29
  • no, he mean change `return new JsonResponse(array('message' => 'Success!'), 200);` to `echo new JsonResponse(array('message' => 'Success!'), 200); exit;`. and then is it correct your url `url: Routing.generate('student_create')`? – hendrathings Dec 03 '15 at 15:55
  • Hi @hendrathings, I tried what you say and the undefined value persists, and the AJAX call executes successfully but do not know because the value of the response is not obtained. The URL is correct. – Joseph Dec 03 '15 at 16:17

3 Answers3

1

with ajax, return will not work

return new JsonResponse(array('message' => 'Success!'), 200);

You should print the value using PHP echo to use it in an ajax success callback e.g.

echo new JsonResponse(array('message' => 'Success!'), 200);
SidOfc
  • 4,552
  • 3
  • 27
  • 50
Niranjan N Raju
  • 12,047
  • 4
  • 22
  • 41
  • 1
    This answer is good, so I won't make a new one. But you make you of the callback `done` to get the response – online Thomas Dec 03 '15 at 14:57
  • Hi @SidneyLiebrand, I still appears that the value is undefined. – Joseph Dec 03 '15 at 15:00
  • 1
    @Thomas, `success()` will also do the same. – Niranjan N Raju Dec 03 '15 at 15:02
  • 1
    @NiranjanNRaju almost the same, `success` only fires if the returned request has a http status of `200` otherwise `fail` will be called, `done` gets called at the end of every request regardless of failure or success. – SidOfc Dec 03 '15 at 15:03
  • This is incorrect. The action needs to return a response, not to echo the JSONResponse. Echoing it just means that it prints out the response (status code, headers and content) as a string and then carries on to return the latter response. – qooplmao Dec 03 '15 at 15:14
  • @qooplmao [check this](http://stackoverflow.com/questions/10107144/difference-between-php-echo-and-return-in-terms-of-a-jquery-ajax-call) – Niranjan N Raju Dec 03 '15 at 15:15
  • @NiranjanNRaju, symfony renders the JSON Response correctly when "returned", meaning the headers get set correctly (including the status code) and then the content gets rendered depending on the response object (in this case a json_encoded version of the data provided). – qooplmao Dec 03 '15 at 15:19
1

You need to handle the XMLHttpRequests differently to your regular HTML requests.

At present when an XMLHttpRequest is made but the form fails the whole page is rendered again (with a "success" status code), but you only want to return a response with a message and a "failed" status code.

The following should help you.

public function createAction(Request $request)
{
    // if request is XmlHttpRequest (AJAX) but not a POSt, throw an exception
    if ($request->isXmlHttpRequest() && !$request->isMethod(Request::METHOD_POST)) {
        throw new HttpException('XMLHttpRequests/AJAX calls must be POSTed');
    }

    $entity = new Student();
    $form = $this->createCreateForm($entity);
    $form->handleRequest($request);

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($entity);
        $em->flush();

        // if the form was successful and the call was an AJAX request
        // respond with a JSON Response (with a 201/created status code)
        if ($request->isXmlHttpRequest()) {
            return new JsonResponse(array('message' => 'Success!'), 201);
        }

        // If the form was successful and the call was HTTP
        // redirect to "show student"
        return $this->redirect('student_show', array('id' => $entity->getId()));
    }

    // if request was an AJAX call and (obviously) the form was not valid
    // return message about form failure
    // (with a 400/Bad Request status code)
    if ($request->isMethod(Request::METHOD_POST)) {
        return new JsonResponse(array('message' => 'failed due to form errors'), 400);
        // you could also loop through the form errors to create an array, use a custom 
        // form -> errors array transformer or use the @fos_rest.view_handler to output
        // your form errors
    }

    return $this->render('BackendBundle:Student:new.html.twig', array(
        'entity' => $entity,
        'form'   => $form->createView(),
    ));
}

Updated

The javascript should work like this.

$('.form_student').submit(function(event) {
    event.preventDefault();

    $.ajax({
        type: 'POST',
        url: Routing.generate('student_create'),
        data: $(this).serialize(),
        dataType: 'json',

        // if "student_create" returns a 2** status code
        success: function(response) {
            // should return "Success!"
            alert(response.message);
        },

        // if "student_create" returns a non-2** status code
        error: function (xhr, desc, err){
            // if the response was parsed to json and has a message key
            if (xhr.responseJSON && xhr.responseJSON.message) {
                alert(xhr.responseJSON.message);
            // otherwise use the status text
            } else {
                alert(desc);
            }
        }
    });

    return false;
});
qooplmao
  • 17,622
  • 2
  • 44
  • 69
  • Hi @qooplmao, first thanks for your answer. I understand everything you indicate to me, but the problem I have to use the AJAX response because I do not get the right message. Could you add me ajax code with an example to see what I can be failing please? – Joseph Dec 03 '15 at 16:48
  • I've annotated your code. Does that make more sense? – qooplmao Dec 03 '15 at 18:21
  • Hi @qooplmao, I've been doing try changing what you told me before, and I get first METHOD_POST error indicating that it is not defined. Error: Undefined class constant 'METHOD_POST' in / opt / lampp / htdocs / Symfony /src/BackendBundle/Controller/StudentController.php line 40 h2> Removing this code I managed to get the 400 error with the error message (I've seen on the console) with the way he has indicated to me now. – Joseph Dec 03 '15 at 18:35
  • Is the same $request->isMethod(Request::METHOD_POST) than $request->isMethod('POST')? I changed it so, but now always shows me the error message and never has successfully performed ajax call, although you enter the correct values in the form. – Joseph Dec 03 '15 at 19:02
  • Yeah, the `Request::METHOD_POST` just uses the constant, but it's only available from 2.6+ so you might not having depending on your version. I don't really know what the rest of your question means though. sorry. – qooplmao Dec 03 '15 at 19:14
  • According I use 2.3, so for now I'll leave it. The problem is that now not detect that the form is valid, and always returns the message that is invalid, anyway I look good in validations. One more thing, I could explain what that is found in the condition of the part of the error? That's all I do not understand, otherwise I understand perfectly, so thank you very much for everything. – Joseph Dec 03 '15 at 19:21
  • If the ajax call is a success it will be JSON. If it fails it might return JSON, although it may not for what ever reason. The condition checks for the a parsed JSON object (xhr.responseJSON) and the message key (xhr.responseJSON.message) then uses that but defaults to the status text returns by the failing page if they aren't present, it's just a fallback. – qooplmao Dec 04 '15 at 08:59
  • Ok, thank you very much for everything, it has served me very helpful. – Joseph Dec 04 '15 at 09:25
0

You need have 2 action:

1. Action show

Show action should handle interface of your form. for ex:

public function showAction(Request $request){

    $entity = new Student();
    $form = $this->createCreateForm($entity);

    return $this->render('BackendBundle:Student:new.html.twig', array(
        'entity' => $entity,
        'form'   => $form->createView(),
    ));
}

2. Action handle save

Action save should handle when request save data by ajax. for ex:

public function createAction(Request $request)
{
    //get all data post
    $data = $request->request->all();
    extract($data, EXTR_PREFIX_SAME, "wddx");
    // if you want to see the result uncoment below
    // dump($data); // uncomment this to see data

    //$id = $data['id'];
    //$name = $data['name'];

    $student = new Student();
    //$student->.... = ... // set your field
    //...
    //...


    try {
        // try save entity
        $em = $this->getDoctrine()->getManager();
        $em->persist($student);
        $em->flush();
    } catch (\Exception $e) {
        // set your error message ...
    }
}

NOTE:

remember change url: Routing.generate('student_create') point to save handle

hendrathings
  • 3,720
  • 16
  • 30