Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
563 views
in Technique[技术] by (71.8m points)

ajax - Adding JQuery Autocomplete in Symfony2 Entity field

I have a city and voters entity in a one to many relationships.I want to convert the entity field(thousands of records in a dropdown) to a text input so that I can implement the JQuery autocomplete when users start typing 2 letters.After almost two weeks,I successfully created the DataTransformer which transform entity field in to a text input.Now my problem is I am still learning the JQuery/Ajax and I am confused how to implement it in Symfony2 forms.

//formtype.php


private $entityManager;

public function __construct(ObjectManager $entityManager)
{
  $this->entityManager = $entityManager;
}
$builder
        ->add('address', null, array(
        'error_bubbling' => true
      ))
        ->add('city', 'text', array(
        'label' => 'Type your city',
        //'error_bubbling' => true,
        'invalid_message' => 'That city you entered is not listed',
      ))
 $builder->get('city')
      ->addModelTransformer(new CityAutocompleteTransformer($this->entityManager));

//datatransformer.php

class CityAutocompleteTransformer implements DataTransformerInterface
{
private $entityManager;

public function __construct(ObjectManager $entityManager)
{
    $this->entityManager = $entityManager;
}

public function transform($city)
{
    if (null === $city) {
        return '';
    }

    return $city->getName();
}

public function reverseTransform($cityName)
{
    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;
 }
}

//controller.php

public function createAction(Request $request)
{
    $entity = new Voters();
    $form = $this->createCreateForm($entity);
    $form->handleRequest($request);

    $validator = $this->get('validator');
    $errors = $validator->validate($entity);
    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($entity);
        $em->flush();


        $this->addFlash('danger', 'You are successfully added!, Welcome to the growing Supporters, dont forget to share and invite this to your friends and relatives, click share buttons below, have a magical day!');

        //return $this->redirect($this->generateUrl('voters_show', array('id' => $entity->getId())));
        return $this->redirect($this->generateUrl('voters_list'));
    } else {

        $this->addFlash('danger', 'Oppss somethings went wrong, check errors buddy!');

        return $this->render('DuterteBundle:Voters:neww.html.twig', array(
            'entity' => $entity,
            'form'   => $form->createView(),
        ));
    }
}

/**
 * Creates a form to create a Voters entity.
 *
 * @param Voters $entity The entity
 *
 * @return SymfonyComponentFormForm The form
 */
private function createCreateForm(Voters $entity)
{   
    $entityManager = $this->getDoctrine()->getManager();
    $form = $this->createForm(new VotersType($entityManager), $entity, //here i passed the entity manager to make it work
array(
        'action' => $this->generateUrl('voters_create'),
        'method' => 'POST',
    ));

    $form->add('submit', 'submit', array(
        'label' => 'I Will Vote Mayor Duterte'
    ));

    return $form;
}

With this code, I can successfully create a new voter and will throw validation errors(invalid_message in formtype) when a user entered a city name that does not match those already saved in the database.What I am lacking now is I want to implement the JQuery autocomplete when the user type enter at least two letters

The Twig part

//twig.php

  {{ form_start(form, {attr: {novalidate: 'novalidate'}} ) }}
        {{ form_errors(form) }}
        {{ form_row(form.comments,{'attr': {'placeholder': 'Why You Want '}}) }}
        {{ form_row(form.email,{'attr': {'placeholder': 'Email is optional, you may leave it blank.But if you want to include your email, make sure it is your valid email '}}) }}
        {{ form_end(form) }}    

enter image description here

As you can see, the form itself is consist of many fields, aside from the city field.Here, the city field is a dropdown consists of more than one thousand entries from database.I can successfully convert this dropdown into textfield by using DataTransformer.So the problem here is how to implement JQuery Autocomplete inside this form with many fields.

Any help is appreciated

Update

Based on user Frankbeen's answer, I Added an action inside my controller

public function autocompleteAction(Request $request)
{
    $names = array();
    $term = trim(strip_tags($request->get('term')));

    $em = $this->getDoctrine()->getManager();

    $entities = $em->getRepository('DuterteBundle:City')->createQueryBuilder('c')
       ->where('c.name LIKE :name')
       ->setParameter('name', '%'.$term.'%')
       ->getQuery()
       ->getResult();

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

    $response = new JsonResponse();
    $response->setData($names);

    return $response;
}

And also the js file

{% block javascripts %}
{{ parent() }}
<script src="//code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<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.id :
                "Nothing selected, input was " + this.value );
            }
        });
    });
</script>
{% endblock %}

In this case, the

  $( "#project_bundle_dutertebundle_voters_city").autocomplete({

part is actually the default id of city field provided by Symfony2 when rendering the form.The JQuery autocomplete now is working , but the problem is , I cannot save the selected option, the invalid_message validation I created inside FormType.php is triggered as well as the JQuery script when submit button is clicked

Selected: Basista (Pangasinan Province) aka undefined

which tells that the Id of the selected value is undefined

$( "#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 );
            }
        });
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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 AppBundleController;

use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SymfonyBundleFrameworkBundleControllerController;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationJsonResponse;

/**
 * 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...