Set the class of all Input Boxes in Yii2 - css

I have a web application designed in Yii2. The client wants all the text boxes to be shorter/smaller. In bootstrap, in addition to "form-control", I'd need to add the "input-sm" class as well.
However, as far as Yii2 is concerned, the form-control class is set in the model. Example: ActiveField model's has
'inputOptions' => [
'class' => 'form-control',
],
However, I am using various widgets and extensions and need a supported solution to set the class such that with a few edits, I can make all text boxes and other controls look smaller. I don't want to mention a custom inputOptions in every form.
One solution could be to extend all the models I use and setting the class (input-sm) there, but would prefer a CSS or simpler solution.

Extend Your Active Field Class as:-
<?php
namespace auction\widgets;
use yii\helpers\ArrayHelper;
class ActiveField extends \yii\widgets\ActiveField{
//Error Options For Active Field Error Tag
public $errorOptions= ['class' => 'error', 'tag' => 'span'];
public function init(){
//Changing Input Options Merge with form-class
$this->inputOptions = ['placeHolder' => $this->model->getAttributeLabel($this->attribute), 'class' => 'input-sm'];
parent::init();
}
/**
* #param null $label Setting Label Value to false
* #param array $options
*/
public function label($label = null, $options = [])
{
$this->parts['{label}'] = '';
}
}
now use ActiveForm As:-
<?php $form = ActiveForm::begin([
'id' => 'login-form',
'fieldClass' => 'auction\widgets\ActiveField',
'successCssClass' => false,
'options'=> ['role' => 'form']]); ?>

Related

Symfony 4. How do I extend the DateType to be HiddenType insted

I have created a data transformation string to date, but I really wanted to try and extend the DateType as it has all the logic for handling dates, has anyone done this before or should I just use a DataTransformer (which is what I have implemented)?
I tried doing the following:
Class HiddenDateType extends DateType {
public function getParent() {
return HiddenType::class;
}
}
But this didn't work, when I looked into the DateType Class it was a little more complicated than I was expecting and didn't know where to hook into the returned type.
I found a few solutions but most of them where pretty hacky, ie; Hiding the entire field via CSS, or changing the type inside the twig template.
This is my transformer, it's VERY redemantry, its frustrating that there is code RIGHT inside the DateType but my skills are lacking :(
class StringToDateTransformer implements DataTransformerInterface
{
/**
* #param \DateTime|null $value
* #return string
*/
public function transform($value)
{
if(null === $value) {
return '';
}
return $value->format('Y-m-d');
}
/**
* #param string $value
* #return \DateTime
*/
public function reverseTransform($value)
{
try {
return new \DateTime($value);
} catch (\Exception $e) {
throw new TransformationFailedException(sprintf(
'Invalid Format for %s format yyyy-mm-dd',
$value
));
}
}
}
On the actual rendered page there is a single div which has a daterangepicker javascript event (on click) is attached to it, this event is triggered when clicked which allows the user to select a start and end date.
The javascript will then populate two hidden DateType fields (startDate, endDate) within the form.
This is the div that the daterangepicker picker is attached to.
<div id="selectedRange">
<i class="fa fa-calendar"></i>
<span></span> <i class="fa fa-caret-down"></i>
</div>
You should be able to do this in your formType class buildForm() method
// ./src/form/yourType.php
$builder->add('hidden_date_field_name', DateType::class, [
'attr' => [
'hidden' => 'hidden',
'disabled' => 'disabled',
],
]);
You should be fine using a DateType or DateTimeType if you do not plan to use something like bootstrap timepicker or any other JS modification of your input.
However, if that's the case and you want to make a better looking date input, the simple way to do it is to use it as a string like so :
$builder->add('date', TextType::class, array(
'required' => true,
'label'=>'Date',
'attr' => array(
'class' => 'datetimepicker',
'data-provide' => 'datepicker',
'data-format' => 'dd-mm-yyyy HH:ii'
),
));
And be sure to add your ModelTransformer to the input in your FormType like so:
$builder->get('date')
->addModelTransformer(new StringToDateTransformer());
Note that you can specify the date format for your js component in the input attribute.

Symfony 4: pass custom data to Form Collection prototype

CollectionType field has special 'prototype' variable when 'allow_add' option is set to true. This variable can be used to render prototype html like this:
data-prototype="{{ form_widget(form.collectionfieldname.vars.prototype)|e('html_attr') }}"
It looks like 'prototype' is simply an instance of collection children FormView built with partial data (e.g. name is set to "__name__" while most other vars are left blank).
Where all this magic happens? Is it possible to modify what data is passed to prototype view while building form? For example, I would like to change default value of "value" variable from blank to "__val__" (outside of Twig template).
Answer to own question - values defined in "entry_options" setting are used to build prototype. It is possible to pass these values to form builder like this:
$builder
->add('email', CollectionType::class, array(
...
'entry_options' => array(
'someoption' => 'somevalue',
),
...
))
If this is not enough, default behaviour can be modified by overriding "buildForm" method in "CollectionType" class which is responsible for collecting options and building prototype:
class CollectionType extends AbstractType
{
...
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($options['allow_add'] && $options['prototype']) {
$prototypeOptions = array_replace(array(
'required' => $options['required'],
'label' => $options['prototype_name'].'label__',
), $options['entry_options']);
if (null !== $options['prototype_data']) {
$prototypeOptions['data'] = $options['prototype_data'];
}
$prototype = $builder->create($options['prototype_name'], $options['entry_type'], $prototypeOptions);
$builder->setAttribute('prototype', $prototype->getForm());
}
...
}
...
}

Symfony 2.8 - how to create "terms & conditions" check-box which isn't mapped to the underlying model

I'm using Symfony 2.8 and I'm trying to create a registration form containing a "terms & conditions" check-box which isn't mapped to the underlying data model.
I've followed this cookbook article:
How to Implement a Simple Registration Form
Everything in the form validation works, except for the the "terms & conditions" check-box. After submitting the form, the check-box validation doesn't get triggered.
This is my code:
namespace Likestripe\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
use Symfony\Component\Validator\Constraints\IsFalse;
use Symfony\Component\Validator\Constraints\IsTrue;
class RegistrationFormType extends AbstractType { //BaseType
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
// call parent constructor
//parent::buildForm($builder, $options);
// add your custom fields
$builder->add('salutation', ChoiceType::class, array('choices' => array('Herr' => 0, 'Frau' => 1), 'choices_as_values' => true));
$builder->add('firstname', TextType::class, array());
$builder->add('lastname', TextType::class, array());
$builder->remove('username');
$builder->add('company', new CompanyFormType());
$builder->add('conditions', CheckboxType::class, array('mapped' => false, 'constraints' => new IsTrue(array("message" => "Bitte akzeptieren Sie noch die AGB und Nutzungsbedingungen."))));
$builder->add('submit', SubmitType::class, array('label' => 'Registrieren'));
} // end function
public function getParent() {
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
// Or for Symfony < 2.8
// return 'fos_user_registration';
}
/**
* function deprecated since 2.8 https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.8.md#form
*
* #return string
*/
public function getName() {
//return 'likestripe_user_registration';
return $this->getBlockPrefix();
} // end function
public function getBlockPrefix() {
return 'likestripe_user_registration';
}
} // end class
I can't see any difference between my code and the code demonstrated in the cookbook article.
Screen capture of my Symfony debug console:
UPDATE:
As Kamil proposed, I've checked if the 'conditions' check-box form parameter gets posted after submitting the form.
The param gets posted als long as the check-box is checked, but if not, the "conditions" form parameter doesn't get posted at all... this behavior reminds me of this case.
I'm still wondering why the official Symfony documentation proposes a isTrue Validator which doesn't seem to be the solution to check for an unchecked check-box, any suggestions how to fix this?
Checkbox checked:
Checkbox unchecked:
Thanks in advance for your help
ninsky
If you let the 'required' of your CheckboxType to true, the constraints isTrue is not useful because the checkbox will always be true !
If change change that to :
$builder->add('conditions', CheckboxType::class, array('mapped' => false, 'required' => false, 'constraints' => new IsTrue(array("message" => "Bitte akzeptieren Sie noch die AGB und Nutzungsbedingungen."))));
With this configuration you can submit the form and if the box is not checked the constraints will send your message.
Hope this help ...
As a workaround, you can add a form listener to make sure you submit the value. Add following to the bottom of your buildForm() function:
$builder->addEventListener(FormEvents::PRE_BIND, function (FormEvent $event) {
$data = $event->getData();
if (!isset($data['conditions'])) {
$data['conditions'] = false;
}
$event->setData($data)
});
As another workaround, you can perform a check in controller and show flash message if request headers do not contain 'conditions'.
Symfony docs mention it:
$builder
->add('email', EmailType::class);
// ...
->add('termsAccepted', CheckboxType::class, array(
'mapped' => false,
'constraints' => new IsTrue(),
))
);
Well, I don't see why is not working for you so in the meantime I give you an alternative way :)
$builder->add('conditions', CheckboxType::class , array('mapped' => false, 'required' => true, 'attr'=>array('onchange'=>"try{setCustomValidity('')}catch(e){}",'oninvalid'=>"setCustomValidity('Bitte akzeptieren Sie noch die AGB und Nutzungsbedingungen.')")));
Hope this work for you.
I've run your code with everything but the CompanyFormType line and it is working as expected.
Could you remove this CompanyFormType line and give it a try ?
You have to set the validation group Overriding Default FOSUserBundle Forms
By default, the Registration validation group is used when validating
a new user registration. Unless you have overridden this value in the
configuration, make sure you add the validation group named
Registration to your name property.
How to do that you can find out here: Overriding Default FOSUserBundle Validation

Symfony2 dynamically assign attr to form field

I'm in a situation where I want to be able to dynamically set the required=true/false option or the array(...other stuff..., 'class' => ' hidden') of a form field.
The context is the following: there is a "Project" entity with some fields. When I create other entities with forms I want to check some attributes of the Project entity and make decisions on the visibility/requiredness of certain fields of the entity I'm creating.
For example if a Project is with attribute "timePressure=high" then a field of a given entity is not required (but is visible). If there are other conditions it becomes invisible etc...
So basically I was hoping to call a function inside each ->add() of the form builder to spit out the relevant portions (e.g. that function would return a string with "required=true" or the other related to the hidden class). The thing is that the function should take as arguments: the projectID (ok, this can be passed as options of the form builder), the class and the field we are talking about to decide. I was envisioning something like:
->add('background', 'textarea', array('attr' => array('rows' => 4, functionToDecideIfInvisible($projectId)), functionToDecideRequiredness($projectId)))
The two function would return the string 'class' => ' hidden' and required=true (or false)
I'd like to avoid to having to specify the field name (in this case background) to avoid code repetition.
Can this be done?
Other suggestions on how to solve the thing?
Thank you!
SN
What you need are Form Events: http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html#cookbook-form-events-underlying-data
They allow you to modify your form based on your data.
You create you project form in the controller:
$form = $this->createForm(new ProjectType(), $project, array(
'action' => $this->generateUrl('project.edit'),
'method' => 'POST',
));
Then you add the FormEvents::PRE_SET_DATA listener:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$project = $event->getData();
$form = $event->getForm();
// check timePressure on the Project object
if ('high' === $project->timePressure) {
$form->add('timePressure', 'text', array(
'required' => false
);
}
});
}
I found a way to do it.
My add would be like:
->add('background', 'textarea', array_merge($this->vr->fieldReq(get_class($this),
'background', $projectID), array('attr' => array_merge(array('rows' => 4, ),
$this->vr->fieldCssClass(get_class($this), 'background', $projectID) ) )))
To do that I had to define the form as service, plus I created another class as service (the one which holds the two methods I need).
This is the class:
class FieldReqAndCssClass
{
public function fieldReq($parentEntity, $field, $projectID)
{
$required = array();
$required['required'] = false; //do stuff on the database instead of setting it to false hardcoded
return $required;
}
public function fieldCssClass($parentEntity, $field, $projectID)
{
$cssClass= array();
//do stuff on the database instead of setting the class as hidden hardcoded
//$cssClass['class'] = ' hidden';
$cssClass['class'] = '';
return $cssClass;
}
}
Of course in my form I had to:
public function __construct(\AppBundle\Util\FieldReqAndCssClass $fieldReqAndCssClass)
{
$this->vr = $fieldReqAndCssClass; // vr stands for visibility and requiredness
}
And these are the two services:
app_bundle.field_req_and_css_class:
class: AppBundle\Util\FieldReqAndCssClass
arguments: []
app_bundle.form.type.projectdetail:
class: AppBundle\Form\Type\ProjectDetailFormType
arguments: [#app_bundle.field_req_and_css_class]
tags:
- { name: form.type, alias: ProjectDetail }
Of course here in the first service I'll need to inject the entity manager and add it to the construct, and probably also in the form service, but the basic skeleton is working :)
I'm a happy man :)
EDIT
The only problem of the above is that it makes hidden the widget but not the label. To fix it:
->add('background', 'textarea', array_merge($vr->fieldReq($myClass,
'background', $project), array('label_attr' => $vr->fieldCssClass($myClass,
'background', $project),'attr' => array_merge(array('rows' => 4, ),
$vr->fieldCssClass($myClass, 'background', $project) ) )))
Obviously before I have to declare:
$myClass = get_class($this);
$vr = $this->vr;

Add CSS attributes to Symfony form labels?

I try to add some css attributes to labels in my custom sfForm but I can't achieve it.
In my custom class myForm extends sfForm, I create all textfields dynamically:
public function configure()
{
$widgetFixtures = array();
foreach ($fixtures as $fixture) {
$widgetFixtures[$fixture->getId()] = new sfWidgetFormInputText(
array('label' => $fixture->getTeamNameDom()),
// I would like to add something like: array('class' => $fixture->getCSS()),
array('value' => $fixture->getScore1(), 'readonly' => 'readonly')
);
}
$this->setWidgets($widgetFixtures);
}
I tried to format the rendering with setFormFormatterName but without success.
Note: I can't use renderLabel($value, $attributes = array()) in the template because I get the CSS class from the DB (as you may have seen, I have to use: $fixture->getCSS()).
Could someone shed my light?
Many thanks.
Here is how I solved it.
I took both suggestions from johnwards and richsage and put them together :
"This sort of stuff should be handled in the view/action."
"Access to the options/attributes passed to the Widget itself."
First, I add the CSS class to the input itself (even if I will not use it).
In my custom class myForm extends sfForm,
foreach ($fixtures as $fixture) {
$widgetFixtures[$fixture->getId()] = new sfWidgetFormInputText(
array('label' => $fixture->getTeamNameDom()),
array('value' => $fixture->getScore1(),
'readonly' => 'readonly',
'class' => $fixture->getCSS())
);
}
Then, in the template, instead of using echo $form;, I add the CSS class to the label as shown below:
foreach ($form as $widgetId => $widget) {
...
$labelClass = $widget->getWidget()->getAttribute('class');
echo '<td>'.$widget->renderLabel(null, array('class' => $labelClass)).'</td>';
...
}
Maybe it is not the best way to solve this issue but it works.
Thanks all for your feedback!
What you're trying to do seems possible but your greyed out syntax seems a little off. Try this:
$widgetFixtures[$fixture->getId()] = new sfWidgetFormInputText(
array('label' => $fixture->getTeamNameDom()),
array('class' => $fixture->getCSS()),
array('value' => $fixture->getScore1(), 'readonly' => 'readonly')
);
... or check "The sfWidgetForm Base Class" section here: http://www.symfony-project.org/forms/1_4/en/A-Widgets
The class attribute needs to be passed in as an array in the second parameter for all form widgets.
$this->setWidget('foo', new sfWidgetFormInputText(array(),array('class','fooclass'));
You could try overriding the widget class if you want to add CSS to labels - specifically the methods that render the label. You could then do something like
$foo = new myWidgetFormInputText(array("myCSSClass" => $fixture->getCSS()));
and then override your renderLabel() method or similar in your widget. Your widget will have access to the options you pass in - in the above example myCSSClass is the option key. You can then apply this class value to your widget's label.
It's much easier than that. Just apply CSS to the label for tag...
label[for=payment_cardNumber]
{
margin-top: 20px;
color: red;
}

Resources