Adding JQuery Autocomplete in Symfony2 Entity field Adding JQuery Autocomplete in Symfony2 Entity field symfony symfony

Adding JQuery Autocomplete in Symfony2 Entity field


First you have to start creating a route and action that returns json data. JQuery's autocomplete remote gives you a $_GET variabele with the index 'term' and wants to receive JSON back. Here is an example that uses an Entity with the name City and a property $name

namespace AppBundle\Controller;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\HttpFoundation\JsonResponse;/** * City controller. * * @Route("/city") */class CityController extends Controller{    /**     * @Route("/autocomplete", name="city_autocomplete")     */    public function autocompleteAction(Request $request)    {        $names = array();        $term = trim(strip_tags($request->get('term')));        $em = $this->getDoctrine()->getManager();        $entities = $em->getRepository('AppBundle:City')->createQueryBuilder('c')           ->where('c.name LIKE :name')           ->setParameter('name', '%'.$term.'%')           ->getQuery()           ->getResult();        foreach ($entities as $entity)        {            $names[] = $entity->getName();        }        $response = new JsonResponse();        $response->setData($names);        return $response;    }}

Secondary you can make a twig view just like the source from jQuery's autocomplete. The only difference is the source variable in the autocomplete() function . There you have to specify te twig's path() function with your route key eg city_autocomplete.

(This view needs another route and another (normal) action.)

<!doctype html><html lang="en"><head>  <meta charset="utf-8">  <title>jQuery UI Autocomplete - Remote datasource</title>  <link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">  <script src="//code.jquery.com/jquery-1.10.2.js"></script>  <script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>  <link rel="stylesheet" href="/resources/demos/style.css">  <style>  .ui-autocomplete-loading {    background: white url("images/ui-anim_basic_16x16.gif") right center no-repeat;  }  </style>  <script>  $(function() {    function log( message ) {      $( "<div>" ).text( message ).prependTo( "#log" );      $( "#log" ).scrollTop( 0 );    }    $( "#birds" ).autocomplete({      source: "{{ path('city_autocomplete') }}",      minLength: 2,      select: function( event, ui ) {        log( ui.item ?          "Selected: " + ui.item.value + " aka " + ui.item.id :          "Nothing selected, input was " + this.value );      }    });  });  </script></head><body><div class="ui-widget">  <label for="birds">Birds: </label>  <input id="birds"></div><div class="ui-widget" style="margin-top:2em; font-family:Arial">  Result:  <div id="log" style="height: 200px; width: 300px; overflow: auto;" class="ui-widget-content"></div></div></body></html>

And finaly you can slightly change this view and use your own form.


Finally, after digging deeper in to my Symfony code, I finally found the solution.Using the code provided by user Frankbeen, I add some 'tweak' inorder for the JQuery to finally work.The culprit is in the controller.

$names[] = $entity->getName()."({$entity->getProvince()})";

The city entity is related to province entity in one to many relationship.Since the city entity has thousands of name(records) chances are some values will have same name,so appending related province is useful to avoid confusions in users

San Francisco(Russia Province), San Francisco(Chinese Province),San Francisco(Portugal Province)

enter image description here

Now since names where now 'different' in the names already saved in database, the invalid_message validation will trigger the errors.My solution is to 'clean' the submitted data by removing the appended provinces before comparing the user submitted values to the values in the database.Inside the DataTransformer, I added some code

public function reverseTransform($cityNameConcat){    $cityName = preg_replace("/\([^)]+\)/", "", $cityNameConcat);    if (!$cityName) {        return;    }    $city = $this->entityManager        ->getRepository('DuterteBundle:City')->findOneBy(array('name' => $cityName));    if (null === $city) {        throw new TransformationFailedException(sprintf('There is no "%s" exists',            $cityName        ));    }    return $city;}

Now the JQuery is finally working and saving to database is also successful.And lastly,

$( "#project_bundle_dutertebundle_voters_city").autocomplete({        source: "{{ path('city_autocomplete') }}",        minLength: 2,        select: function( event, ui ) {        log( ui.item ?            "Selected: " + ui.item.value + " aka " + ui.item.id ://this throw undefined            "Nothing selected, input was " + this.value );        }    });

Is changed to

<script>    $(function() {        function log( message ) {            $( "<div>" ).text( message ).prependTo( "#log" );            $( "#log" ).scrollTop( 0 );        }        $( "#project_bundle_dutertebundle_voters_city").autocomplete({            source: "{{ path('city_autocomplete') }}",            minLength: 2,            select: function( event, ui ) {            log( ui.item ?                "Selected: " + ui.item.value + " aka " + ui.item.label:                "Nothing selected, input was " + this.value );                $("#project_bundle_dutertebundle_voters_city").val(ui.item.label);                $("#project_bundle_dutertebundle_voters_city").val(ui.item.value);                return false;            },            change: function( event, ui ) {                $( "#project_bundle_dutertebundle_voters_city" ).val( ui.item? ui.item.value : 0 );}         });    });</script>

Now the 'Undefined' error is gone.


Here a solution to add a field regarding the response given by the Symfony Controller.In success, add fields you want by returning a object.Then, in the select, you can access it by ui.item.fields

$('#mySelector').autocomplete({        source : function(requete, reponse) {            lettre = {                lettre: $('#lettre').val()            };            $.ajax({                url: Routing.generate('chargementSource'),                dataType: 'json',                data : lettre,                success: function (donnee) {                                        reponse(                            $.map(donnee, function (objet) {                                return {                                   value: '' + objet.nom + ' ' + objet.prenom +', '+ objet.adresse +' '+ objet.codepostal +', '+ objet.ville + '',                                  id: objet.id                                }                            })                    );                }            });        },        select: function (event, ui) {            $('#myId').val(ui.item.id);            //alert(ui.item.id);            //$('#myId').val(ui.elem.value);            return false;        }    });