Symfony Forms: HTML5 datalist - html

How can be implemented HTML5 datalist with values from the database (Doctrine)?
Purpose: replace selects with many options to inputs with autocompletion.

First, add your new FormType for the field:.
<?php
// src/Acme/Form/Type/DatalistType
namespace Acme\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
class DatalistType extends AbstractType
{
public function getParent()
{
return TextType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired(['choices']);
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
$view->vars['choices'] = $options['choices'];
}
public function getName()
{
return 'datalist';
}
}
In services.yml:
form.type.datalist_type:
class: Acme\Form\Type\DatalistType
tags:
- { name: form.type, alias: datalist }
Do you have a form theme? If yes, skip to the next step, if no, create a new one in app/Resources/views/Form/fields.html.twig and change your default Twig theme to it:
# app/config/config.yml
twig:
form_themes:
- ':Form:fields.html.twig'
Now define a template for your new field in the form theme:
{% block datalist_widget %}
<input list="{{ id }}_list" {{ block('widget_attributes') }}{% if value is not empty %}value="{{ value }}"{% endif %}>
<datalist id="{{ id }}_list">
{% for choice in choices %}
<option value="{{ choice }}"></option>
{% endfor %}
</datalist>
{% endblock %}
Use your field in FormType:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('country', DatalistType::class, ['choices' => ['a', 'b']]);
}
Instead of ['a', 'b'] You need to load your choices from DB somehow, I'd suggest passing them in form options as the easiest solution.

I spent some time trying to solve this issue, and there is a quite simple solution which solves Konstantin's issue with database access. It is to create a new form type which has EntityType as it's parent.
class DatalistType extends AbstractType
{
public function getParent() {
return EntityType::class;
}
}
You can then create a new template for this widget:
{# views/form/fields.html.twig #}
{% block datalist_widget %}
<input {{ block('widget_attributes') }} list="{{ form.vars.id }}_list" value="{{ form.vars.value }}" />
<datalist id="{{ form.vars.id }}_list">
{% for choice in choices %}
<option>
{{ choice.label }}
</option>
{% endfor %}
</datalist>
{% endblock %}
Finally, in the buildForm function of the form you are building, you can add your form element using the DatalistType, and using the EntityType options.
$builder->add('fieldName', DatalistType::class ,
array('required' => false,
'label' => 'fieldLabel',
'class' => 'AppBundle\Entity\EntityName',
'choice_label' => 'entityField'))

In your formType :
->add('commerciaux', TextType::class,
[
'label' => 'Apporteur d\'affaire*',
'attr' =>
[
'placeholder' => 'Apporteur d\'affaire',
'list' => 'bieres'
]
]
)
In your View :
{{ form_row(formulaire.commerciaux) }}
<datalist id="bieres">
<option value="Meteor">
<option value="Pils">
<option value="Kronenbourg">
<option value="Grimbergen">
</datalist>

In your formType :
->add('commerciaux', TextType::class,
[
'label' => 'Apporteur d\'affaire*',
'attr' =>
[
'placeholder' => 'Apporteur d\'affaire',
'list' => 'bieres'
]
]
)
Declare ( controller or View)
'bieres' => $array_bieres
In your View :
{{ form_row(formulaire.commerciaux) }}
<datalist id="bieres">
{% for biere in bieres %}
<option value="{{ biere }}">
{% endfor %}
</datalist>

Related

Django Crispy Form & <select> tag

Is it possible to use crispy-form for < select > tags?
"|as_crispy_field" doesn't help here
template
<select id="food" name="food">
<option value="" selected="selected">---------</option>
{% for object in form.fields.food.choices %}
<option value="{{ object.0 }}" class="{{ object.1 }}">{{ object.2 }}</option>
{% endfor %}
</select>
edit: added Form.py
class MealForm(ModelForm):
def __init__(self, *args, **kwargs):
super(MealForm, self).__init__(*args, **kwargs)
self.fields['food'].choices = [(food.id, food.food_number, food) for food in Food.objects.all()]
self.fields['drink'].choices = [(drink.id, drink.pid, drink.doi, drink.type) for drink in Drink.objects.all()]
class Meta:
fields = (
'title',
'description',
'food',
'drink',
)
model = Meal
you can render individual fields with crispy forms with the crispy_field tag
{% crispy_field form.food %}
or the as_crispy_field filter
{{ form.food|as_crispy_field }}
If you want to pass id and values than you can do like this
FOOD_OPTIONS = [(food.id, food) for food in Food.objects.all()]
class MealForm(ModelForm):
class Meta:
model=Meal
food = forms.ChoiceField(choices = FOOD_OPTIONS, widget=forms.TextInput(attrs={'class': "form-control"}))) # here you can specify class to your select tag or remove it
and instead of looping in template just do like this
{{ form.food|as_crispy_field }}

Select the default choice field in a select in symfony and html

Here, I have in my database an insurance table where I store the insurance (with their name and logo) to which doctors can be affiliated.Now the doctor has the possibility to choose one, two or no insurance in his doctor space.But I have a problem, when the doctor is not affiliated with any insurance, I want to choose a default value showing that this doctor does not have insurance yet, but whatever I choose that is not insurance does not take effect.
Controller
public function editAction(Request $request)
{
$user = $this->getUser();
if (null === $user) {
throw new NotFoundHttpException('Utilisateur Inexistant');
}
$em = $this->getDoctrine()->getManager();
$repo = $em->getRepository('DoctixMedecinBundle:Medecin');
$specialiteRepo = $em->getRepository('DoctixAdminBundle:Specialite');
$cliniqueRepo = $em->getRepository('DoctixAdminBundle:Clinique');
$assuranceRepo = $em->getRepository('DoctixMedecinBundle:Assurance');
$object = $assuranceRepo->findOneBy(['id' => $request->get('assurance')]);
$medecin = $repo->findOneBy([
'user' => $user,
]);
if ($request->isMethod('POST')) {
if ('' != ($pass = $request->get('pass'))) {
$medecin->getUser()->setSalt('');
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($medecin->getUser());
$password_encoder = $encoder->encodePassword($pass, $medecin->getUser()->getSalt());
$medecin->getUser()->setPassword($password_encoder);
}
$medecin->setSpecialite($specialiteRepo->find($request->get('specialite')));
$medecin->setClinique($cliniqueRepo->find($request->get('clinique')));
$medecin->getUser()->setAdresse($request->get('adresse'));
$medecin->getUser()->setNumTel($request->get('telephone'));
$medecin->setQuartier($request->get('quartier'));
$medecin->setNumOrdre($request->get('numordre'));
if ($object) {
$medecin->addAssurance($object);
}
$em->persist($medecin);
$em->flush();
// redirection avec le status http 301 ::)))))
$url = $this->generateUrl('medecin_parametre');
return $this->redirect($url, 301);
} else {
return $this->render('DoctixMedecinBundle:Medecin:editProfile.html.twig', [
'medecin' => $medecin,
'specialites' => $specialiteRepo->findAll(),
'cliniques' => $cliniqueRepo->findAll(),
'assurances' => $assuranceRepo->findAll(),
]);
}
}
Twig
<div class="col-md-6">
<div class="form-group">
<label for="assurance" class="control-label">Assurance </label>
<select id="assurance" class="form-control" placeholder="Assurance" name="assurance" multiple>
<option value="Pas d Assurance" selected="Pas encore">Pas d'Assurance</option>
{% for assurance in assurances %}
<option value="{{ assurance.id }}"
{% for it in medecin.assurance %}
{{ it.id== assurance.id ?
'selected' : '' }}
{% endfor %} >{{ assurance.nom|upper }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="row">
<div class="col-md-offset-3 col-md-3">
<input type="submit" class="btn_1 btn-success medium" form="editProfil" value="Valider">
Annuler
</div>
</div>
twig view where will be displayed or not the insurance to which the doctor is affiliated
<div class="col-md-6">
<div class="form-group">
<label>Vos Assurances</label>
{% for item in medecin.assurance %}
{% if item is not empty %}
<input type="text" class="form-control" readonly
placeholder="{{ item.nom|upper }} ">
{% else %}
<input type="text" class="form-control" readonly
placeholder="Vous n'avez pas d'assurance ">
{% endif %}
{% endfor %}
</div>
</div>
Now how to display like a paragraph saying No insurance when the doctor does not have insurance ?
Thanks.
To check if the medicin have an any assurances you'll have to check if the array medecin.assurance is empty or not
Something like this:
{% if medecin.assurance is empty %}
{# This medecin has no assurances, act accordingly #}
{% else %}
{# Display medecin's assurances #}
{% for item in medecin.assurance %}
{# Display each assurance #}
{% endfor %}
{% endif %}

gulp-nunjucks-render and adding data from single file

How can I include data to gulp-nunjucks template from separate file?
//template/data/data.html
{% set
list = [
{
title: 'Item1'
},
{
title: 'Item2'
}
]
%}
This simple solution doesn't work.
{% include "data/json.html" %}
This should work if you use import instead of include, https://mozilla.github.io/nunjucks/templating.html#import
Try this (I used .njk extensions, but you can use .html, it won't matter):
//template/data/data.njk
{% set list = [
{
title: 'Item1'
},
{
title: 'Item2'
}] %}
And in the file where you want to use the {{ list }} variable:
//template/other-file.njk
{% from 'data/data.njk' import list %}
{{ list }}
Any top-level variables that are {% set %} or any macros that are defined are available via import.

How to display a zend-form element select in twig template

How to display a zend-form element select in twig template?
Do as follows:
Form:
$this->add([
'name' => 'parent',
'type' => 'Zend\Form\Element\Select',
'options' => [
'label' => 'Принадлежность',
'empty_option' => 'Выберите категорию',
'value_options' => [
'0' => 'French',
'1' => 'English',
'2' => 'Japanese',
'3' => 'Chinese',
],
],
]);
Twig:
<div class="form-group select">
<label for="{{ form.get('parent').name }}">{{ form.get('parent').label }}</label>
<select class="form-control" type="{{ form.get('parent').attributes.type }}" name="{{ form.get('parent').name }}">
<option>{{ form.get('parent').options.value_options }}</option>
</select>
</div>
You should be able to achieve it with something like this:
<div class="form-group select">
<label for="{{ form.get('parent').name }}">{{ form.get('parent').label }}</label>
<select class="form-control" type="{{ form.get('parent').attributes.type }}" name="{{ form.get('parent').name }}">
{% for option in form.get('parent').options.value_options %}
<option>{{ option }}</option>
{% endfor %}
</select>
</div>
Since I haven't used twing I can't be sure.
Read more about twig for loop here.

Including novalidate attribute into Symfony2 twig template

I am trying to disable the HTML5 validation of my form and I have seen I can include the novalidate into the form tag however I am using
{{ form_start(contact) }}
{{ form_end(contact) }}
to create my forms.
Now from the what I have been reading I should be able to include an attribute into the form_start such that the code would give me this
{{ form_start(contact, {'attr' : {'novalidate'}})
This however is not working...does anyone have any ideas?
You need a key/value pair:
{{ form_start(contact, {attr: {novalidate: 'novalidate'}}) }}
If you want to add novalidatein all the forms of your app, create a twig template like this:
{% extends 'form_div_layout.html.twig' %}
{# FORM START #}
{% block form_start %}
<form action="{{ action }}"{% for attrname, attrvalue in attr %} {{ attrname }}="{{ attrvalue }}"{% endfor %}{% if multipart %} enctype="multipart/form-data"{% endif %} novalidate>
{% endblock form_start %}
Documentation: Form Theme
You can set form novalidate attribute to symfony 2 form object like this
$form = $this->createForm(new ClientType(), $clientEntity, array(
'action' => $this->generateUrl('client_create'),
'method' => 'POST',
'attr'=>array('novalidate'=>'novalidate') //add novalidate attribute to form
));
You can use function createFormBuilder()->setAttributes() to set any attributes to the whole form, including novalidate:
$form = $this->createFormBuilder($task)
->add('value', IntegerType::class)
->add('save', SubmitType::class, array('label' => 'Save',))
->setAttributes(array(
'novalidate'=>'novalidate',
))
->getForm();