问题
In my application I've got a couple of form fields with many options. The problem I experienced is similar to this question: getting and parsing all options at every page load is expensive (Twig renders all options over and over again while no client side caching possible). That problem made me create a way to send the options via AJAX to the browser. Fairly simple approach:
- Get all options (key-value) via AJAX (for example by getting /countries.json) and cache if possible. (in this case it's not very likely country names change very often)
- Use selectize, select2 or a similar plugin to insert the options into the DOM.
- Enjoy a faster Form :-)
To prevent Symfony from querying all options (not necessary: they're loading via AJAX) I added setMaxResults(0) to the QueryBuilder when the form is loaded (by adding an option via the controller). Yes, that's kludge. When submitting a form it will still perform a query, because it has to verify if the selected option exists (and check for Constraints).
I would like to create a custom Form Field Type that adds this functionality to the current EntityType: don't load the options while rendering the form, but still check if the selected option exists. I found many examples related to dynamically modifying a form, but I haven't found examples related to modifying just one form field, independently of it's parent form.
How do I create a form field type like this? What's a good starting point? Extend EntityType, ChoiceType or an other approach?
I'm already using Symfony 3.1, so using lazy loading of form choices (New in Symfony 3.2) won't be a problem. Not sure if this new feature is related to my problem.
回答1:
I wrote a bundle (Alsatian/FormBundle), which does what you want on the server side.
How to avoid loading each entities by each form rendering :
abstract class AbstractExtensibleChoicesType extends AbstractRoutableType { public function configureOptions(OptionsResolver $resolver) { $resolver->setDefault('choices',array()); } }How to populate the form field with cached content :
That's your own logic, I would suggest : create a controller which only returns (as HTML) :
<option value="1">Option 1</option>
<option value="2">Option 2</option>
In the controller set Maxage :
/*
* @Route(...)
* @Cache(maxage=64000)
*/
public function getOptionsAction(Request $request) // Home
{
$choices = $this->getDoctrine()->getManager()->getRepository //....
return $this->render(/*...*/);
}
Use javascript to load this url and to put the html result in your select field.
If you are using something like Select2 : Your Controller can also return the options as a JSONReponse(), then you can load this JSON directly from the select2 ajax option (see bundle documentation, that's how I use it).
- And the most difficult : How to make the submitted choice valid :
Get the sumitted choices in a Form::PRE_SUBMIT event (also PRE_SET_DATA if you use your form to edit), and reinject these choices to the field.
回答2:
Autocomplete with an Ajax controller option looks nice to me, but heres' another( maybe quicker?) option: render your form through hinclude.
hinclude is a JS library used to "defer" load of parts of a page, thought Ajax. Symfony comes with integrated support (official documentation).
How yo use it:
- move your form render to another controller action, let's call it
formAction - include hinclude.js on your page (cf official Github)
use this code to render your form:
{{ render_hinclude(controller('...::form'), {'default': 'Loading...'}) }}
you will probably want to keep your form handling in your original controller action, so modify the "action" of the generated form like this:
$form = $this->createForm(new FormType(), $obj, array( 'action' => $this->generateUrl('original_form_action')));
回答3:
Considering your usecase, The best way would be to use an Autocomplete. Here is a Symfony bundle I have been using and its awesome. I did some overwrite its javascript (autocompleter) to enhance few functionality as per my case.
来源:https://stackoverflow.com/questions/38576990/creating-a-ajaxified-form-field-type