i have an ChoiceType::class input field in my form with, now just as an example, two choices:
'choices' => ['type1' => '1', 'type2' => '2']
now when the user select type2 i want to add an exta TextType::class inputfield to the form.
But i dont want to show the input field before and i want it to be required if selected type2 and not if selected type1.
I hope it make sense, i try it to to with javascript and set the attribute to hidden or not, but
then the form is not been send because of the required attribute.
I tried it with form events but did not get it to work in that way.
Thanks
You were on the right way, you have to do it in Javascript. You just need to manage the attr required in Javascript so that the form does not block you with something like this:
Remove the required attribute from a field: document.getElementById("id").required = false;
Make a field required : document.getElementById("id").required = true;
And you can check if the form can be sumitted with : document.getElementById("idForm").reportValidity();.
I using implementation of conditional fields with data-attributes, e.g.:
->add('typeField', EnumType::class, [
'label' => 'Type',
'class' => MyTypeEnum::Class,
])
->add('someField', TextField::class, [
'data-controller' => 'depends-on',
'data-depends-on' => 'my_form_typeField',
'data-depends-value' => MyTypeEnum::OTHER->value,
])
On frontend JS stimulus controller show/hide someField depend on typeField value.
And validation() function in object ('data_class' in formType) make custom validation, e.g.:
/**
* #Assert\Callback
*/
public function validate(ExecutionContextInterface $context)
{
if ($this->typeField !== MyTypeEnum::OTHER) {
$context->buildViolation('message')->atPath('typeField')->addViolation();
}
}
Related
so I Have this Symfony Form field :
$builder
->add('field1', CheckboxType::class, [
'required' => false,
])
->add('field2', NumberType::class, [
'disabled' => !$entite->getField1(),
'required' => $entite->getField1(),
]);
When the form is build, the field2 attributes are set depending on the entity field1 value.
Then, in the form, field2 HTML attributes are dynamically changes using javascript : if field1 value change, when add or remove 'disabled' / 'required" attributes accordingly.
The problem is, if the field2 is disabled when the form is build and then when enabled id with js, if we submit the form, in PHP the field2 is still disabled.
So I also change attributes in php, by adding this in the FormType:
...
$builder->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'preSubmit']);
....
public function preSubmit(FormEvent $event)
{
$data = $event->getData(); // here $data['field2'] has the correct value
$form = $event->getForm(); // here $form->get('field2') value is null and disabled is true
// trying to remove the field
$form->remove('field2');
// And then re-add it with new attributes
$form
->add('field2', NumberType::class, [
'data' => $data['field2'],
'disabled' => !$data['field1'],
'required' => $data['field1'],
]);
}
So is there a bettey way to change attributes fields after submission (without removing and adding the field with new attributes) ??
Edit
So this works until I set an incorrect value type for field2.
field2 is a NumberType, and is I set an alpha value (like 'azerty')
instead of returning the formview with the fild error message, I get a Symfony Error :
A cycle was detected. Listeners to the PRE_SET_DATA event must not call getData() if the form data has not already been set. You should call getData() on the FormEvent object instead.
How I can prevent that and get the field error message instead.
Thanks.
I have a form generated using YII2 ActiveForm. there are some field I need to be on the if I select certain options , or need to have them removed if I select some other option.
For e.g. I Have a dropdown AccountType, with two options "individual" and "company".
If the user selects "individual" some fields on the form needs to go away say company name, and some other fields need to appear such as First name, last name. Initially when the display the form , only the Account Type field is there.
below is the code I have at the moment
<?php
$form = ActiveForm::begin(['id' => 'account-setup-form']); ?>
echo $form->field($modelAccMain, 'account_type')
->widget(Select2::classname(), [
'data' => $accountTypeArray,
'options' => ['placeholder' => 'Select account type'],
]);
echo $form->field($modelUsers, 'firstname')->textInput()
->hint('')->label('First Name');
echo $form->field($modelUsers, 'lastname')->textInput()
->hint('')->label('Last Name');
<?php ActiveForm::end(); ?>
Any help is greatly appreciated.
You can use scenarios for that, first define them in your model and than you can use a if statement in your view
if ($model->isAttributeActive('attribute_name')) {
But like #nterms wrote, if you want the user to be able to switch on the client side, javascript would be better.
Defining scenarios also helps with the validation (only active attributes will be validated).
p.s. Don't forget to set the scenario in your controller
$model = new MyModel(['scenario'=>'my_scenario']);
The way i would handle it is with jquery hide and show using the change event of the dropdown,
In your javascript
Assuming that the data in the select 2 widget is in the form of array
eg:
[1=>"first-item",2=>"second-item",...]
$(document).ready(function(){
var id= //check the id of the select2
on the inspect element id using chrome;
$("#id").on("change", function(){
if(id.value==1){
//show a div
}else{
//hide a div
}
//for multiple values better use switch
like this
switch(id){
case 1:{
$("#divid").show();
......
}
}
})
})
I hope you get the idea,
For the select 2 id you can set it via
echo $form->field($modelAccMain, 'account_type')
->widget(Select2::classname(), [
'data' => $accountTypeArray,
'options' => ['placeholder' => 'Select account type',"id"=>"mypreffereid"],
]);
How can I change the attributes of a input form?
I create with this a input (productform.php):
$this->add(array(
'name' => 'categoryId',
'attributes' => array(
'id' => 'categoryId',
'type' => 'hidden',
'value' => '',
),
));
In a previous page I link to the form and set the special value in the url (....com/form/3).
In the indexcontroller.php I get the form with $form = new ProductForm(); and want edit the value and set the special value from the url.
My idea was the $form->setAttribute('categoryId', 'value'); but that not working.
Thanks.
indexcontroller.php
...
$form = new ProductForm();
$form->setHydrator(new CategoryHydrator());
$form->bind(new Product());
$form->setAttribute('categoryId', 'value');
....
productform.php
...
class ProductForm extends Form
{
public function __construct()
{
parent::__construct('productForm');
$this->setAttribute('action', 'newproduct');
$this->setAttribute('method', 'post');
$this->add(array(
........
$form->get('categoryId')->setValue("value");
Update
So if you just want to fill input, you mean placeholder attribute in html. You can use setAttribute method.
$form->get('categoryId')->setAttribute('placeholder', 'text to show');
The form view helper will not allow arbitrary HTML attributes to be set on the form. This is because it would result in invalid HTML.
If you take a look at Zend\Form\Helper\AbstractHelper there are two properties $validGlobalAttributes and $validTagAttributes which define the allowed tags.
In the case of the form view helper (Zend\Form\View\Helper\Form) The 'valid tag attributes' will be the method, action etc
As you require something custom (for JS possibly?); I would change it to a data- attribute.
$form->setAttribute('data-categoryId', 'value');
The data- is a valid HTML5 attribute which is useful for adding 'domain data' to HTML elements and is really the 'correct' way to do what you require.
Background: In Drupal 7, I have created a form with CCK (aka the Field UI). I used the Field group module to create a fieldgroup, but I need it to be conditional, meaning it will only display depending on a previous answer.
Previous research: To create a conditional field, you can use hook_form_alter() to edit the #states attribute like so:
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'person_info_node_form') {
// Display 'field_maiden_name' only if married
$form['field_maiden_name']['#states'] = array(
'visible' => array(
':input[name="field_married[und]"]' => array('value' => 'Yes'),
),
);
}
}
However, there seems to be no way to use the States API for fieldgroups. One thing to note is that, while fields are stored in $form, fieldgroups are stored in $form['#groups'] as well as in $form['#fieldgroups']. I don't know how to distinguish between these, and with this in mind, I have tried to apply a #states attribute to a fieldgroup in the same manner as above. However, it only produces server errors.
Question: Is there a way to make a fieldgroup display conditionally using the States API or some alternative approach?
you have to use the hook_field_group_build_pre_render_alter()
Simply :
function your_module_field_group_build_pre_render_alter(&$element) {
$element['your_field_group']['#states'] = array(
'visible' => array(
':input[name="field_checkbox"]' => array('checked' => TRUE),
),
);
}
This works perfecly. If the group A is in an another group, do this
$element['groupA']['groupB']['#states'] etc....
You may need to add an id attribute if none exists:
$element['your_field_group']['#attributes']['id'] = 'some-id';
$element['yout_field_group']['#id'] = 'some-id';
Here's the simplest solution I came up with. There are essentially 2 parts to this: (1.) programmatically alter the display of the form, and (2.) use the GUI to alter the display of the content.
(1.) First, I used hook_form_alter() to programmatically create the conditional fieldset and add existing fields to it. The code is shown below.
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'FORM_ID_node_form') {
// programmatically create a conditional fieldset
$form['MYFIELDSET'] = array( // do NOT name the same as a 'Field group' fieldset or problems will occur
'#type' => 'fieldset',
'#title' => t('Conditional fieldset'),
'#weight' => intval($form['field_PARENT']['#weight'])+1, // put this fieldset right after it's "parent" field
'#states' => array(
'visible' => array(
':input[name="field_PARENT[und]"]' => array('value' => 'Yes'), // only show if field_PARENT == 'Yes'
),
),
);
// add existing fields (created with the Field UI) to the
// conditional fieldset
$fields = array('field_MYFIELD1', 'field_MYFIELD2', 'field_MYFIELD3');
$form = MYMODULE_addToFieldset($form, 'MYFIELDSET', $fields);
}
}
/**
* Adds existing fields to the specified fieldset.
*
* #param array $form Nested array of form elements that comprise the form.
* #param string $fieldset The machine name of the fieldset.
* #param array $fields An array of the machine names of all fields to
* be included in the fieldset.
* #return array $form The updated form.
*/
function MYMODULE_addToFieldSet($form, $fieldset, $fields) {
foreach($fields as $field) {
$form[$fieldset][$field] = $form[$field]; // copy existing field into fieldset
unset($form[$field]); // destroy the original field or duplication will occur
}
return $form;
}
(2.) Then I used the Field group module to alter the display of the content. I did this by going to my content type and using the 'Manage display' tab to create a field group and add my fields to it. This way, the fields will appear to be apart of the same group on both the form and the saved content.
Maybe you can try to look at the code of this module to help you find an idea.
In my Zend framework I have two row one rows contains state dropdown with label state and the other contains a text box with label other state. Below is the code:
'state' => array('select', array(
'required' => true,
'decorators' => $elementDecorators,
'label' => 'State:',
'multiOptions' => $values["state"]
)),
'other_state' => array('text', array(
'required' => true,
'filters' => array('StringTrim'),
'decorators' => $elementDecorators,
'label' => 'Other State:',
'class' => 'other_state',
))
Here the other state is set as required. I need it required only when the user select "Other" value from the state drop down.
Client Side:
jQuery solution:
Showing your HTML output would have been a help here. But the following will add the attribute required if other is selected - this will also enable the input and disable it so the user can only enter something in other state, if they select other:
$("#state").change(function(){
if ($(this).val() == "other"){
$("#other_state").removeAttr("disabled");
$("#other_state").attr("required", "required");
}
else {
$("#other_state").removeAttr("required");
$("#other_state").attr("disabled", "true");
}
});
See a demo here
The above will do the validation on the clients side - with jQuery, however if the user has javascript turned off, it would allow the user to select other and leave other_state blank!
Server Side:
Zend solution:
What you should also do is add some validation to the zend_form. However, you can't add them the normal way - if you added a validator to say other_state can't be empty - you would have an error when a state is selected and you want it to be empty.
In your form class you could override the isValid call to add your custom validation, see the discussion here: There is another example on how to do this here
/**
/* override the isValid function of Zend_Form
/* to set a required field based on a condition
*/
public function isValid($value) {
// Check the key exists in the stack, and if its set to other:
if (array_key_exists('state', $value) && $value['state'] == 'other') {
// It is so make sure other_state is a required field:
$this->other_state->setRequired(true);
}
parent::isValid($value);
}