long question short, after researching about this a lot and finding quite some information on how to extend existing field types, or inherit from them, or change some things in the backend, but absolutely none for the actual rendering in the frontend, I'm coming here to ask the question.
Short explanation to the "issue" at hand: I need an EntityType field (ChoiceType - HTML Select) to use my own filtering logic and to dynamically pull results from an ajax call, instantly replacing the options listed in the dropdown.
Current code (works): in FormType.php
//in buildForm
{
$builder->add('trainer', EntityType::class, [
'class' => Trainer::class,
'choices' => $training->trainer_list ?? [],
'label' => 'seminar.trainer.form.trainer.label',
'placeholder' => 'form.trainer.placeholder',
'required' => false,
'attr' => ['class' => 'trainer2select'] // has no outcome whatsoever?!
])
$builder->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'onPreSubmit']);
}
function onPreSubmit(FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
$trainer = $this->em->getRepository(Trainer::class)->find($data['trainer']);
$form->add('trainer', EntityType::class, [
'class' => Trainer::class,
'data' => $trainer,
'label' => 'seminar.trainer.form.trainer.label',
'placeholder' => 'form.trainer.placeholder',
'required' => false,
]);
}
And in twig:
{% if field == 'trainer' %}
{{ form_row(attribute(form, field), {'id': 'trainer'}) }}
{% else %}
{{ form_row(attribute(form, field)) }}
{% endif %}
{% block javascripts %}
<script>
var lastTime;
var timeoutEvents = [];
$(document).ready(() => {
function trainer_changed() {
let input = event.target;
lastTime = Date.now();
timeoutEvents.push(setTimeout(() => {
if (Date.now() - lastTime < 150)
return;
jQuery.ajax({
url: '{{ path('trainer_select_ajax') }}',
type: 'GET',
data: {
search: input.value,
start: {{ seminar.event.start.date | date('Y-m-d') }},
end: {{ seminar.event.end.date | date('Y-m-d') }}
},
success: function (trainers) {
let trainer = $('#trainer');
trainer.get(0).options.length = 1; // reset all options, except for the default
trainers.forEach(tr => {
trainer.append(new Option(tr.text, tr.id));
});
let search = $($("input.select2-search__field").get(1));
if (search.get(0)) {
search.get(0).oninput = null; // detach our event handler so we don't loop
search.trigger('input'); // rebuild the dropdown choices
search.get(0).oninput = trainer_changed; // reattach our event handler
}
}
});
lastTime = Date.now();
timeoutEvents.forEach(e => {
clearTimeout(e);
});
}, 200));
}
function select_opened() {
let trainerinput = $('input.select2-search__field').get(1);
if (trainerinput) {
trainerinput.oninput = trainer_changed;
}
}
$('#select2-trainer-container').click(select_opened);
});
</script>
{% endblock %}
So, apparently the EntityType Field is rendered using the select2 extension. I can obviously replace the functionality with javascript, but I'd like to just define my own 'AjaxEntityType' that form_widget renders as I want it to. Something I can use within multiple projects, without using some stupid hacks like providing a default class name and invoking javascript changing that rendering after page load globally. So... how do?
Resources I've checked, that have proven mostly worthless to what I want to achieve: https://symfony.com/doc/current/form/form_customization.html, https://symfony.com/doc/current/form/form_themes.html and plenty more.
Edit for clarification: What I'm looking for, optimally, is a minimalistic example of a custom FieldType always being rendered as <select id="FieldTypeWidget">, on which will always be invoked $('#FieldTypeWidget').select2({ajax: {foo}, searchFunction: {bar}});
at https://github.com/tetranz/select2entity-bundle I can find an example of how to provide this functionality in a bundle, but is there an easier way just within my app?