Symfony 3.4, MoneyType, and Bootstrap - php

I'm having a bit of difficulty styling a MoneyType form input the way I want. I want to use Bootstrap's input-group-addon class to prepend a dollar sign to the field, but what's actually happening is that two dollar signs are being rendered... one automatically generated by the MoneyType, and one that I manually wrote in my template:
Template:
<div class="form-group">
{{ form_label(form.canonicalPrice) }}
{{ form_errors(form.canonicalPrice) }}
<div class="input-group">
<span class="input-group-addon">$</span>
{{ form_widget(form.canonicalPrice, { 'attr': {'id': 'price', 'class': 'form-control'} }) }}
</div>
</div>
MoneyType definition in my form type class:
->add('canonicalPrice', MoneyType::class, array('label' => 'Price', 'currency' => 'USD'))
Screenshot:
So, is there a way for me to either:
Hide the automatic currency label that Symfony adds with a MoneyType field? Not specifying any currency defaults to a Euro rather than dollar, which isn't helpful.
Style the automatic label the way I want?
Note: I'm not using Symfony's Bootstrap form themes because I like having complete control over my templates. The fact that MoneyType fields default to showing a currency is very annoying.

Style the automatic label :
The dollar sign that is prepend to the input is from the money_pattern attribute of MoneyType
So as the doc on MoneyType says you can :
Modify in it the view :
{{ form_row(form.value, { 'money_pattern': '{{ widget }} $' }) }}
or like this
{% block money_widget %}
{%- set type = type|default('number') -%}
{{ parent() }}
{% endblock %}
Modify it in the builder :
Use money_pattern in the FormType and set the pattern you want.

Related

Symfony Assert doesnt working with form

I used Assert to validate fields like
#Assert\NotBlank(message="this field cannot be empty")
$private title;
#Assert\NotBlank(message="this field cannot be empty")
$private description;
#Assert\NotBlank(message="this field cannot be empty")
$private price;
Now, when Im using form in html.twig
{% body block %}
{{ form(form, {"attr": {"novalidate": "novalidate"}}) }}
{% endblock %}
everything is allright, if I have an empty field I got my message, but when Im trying to divide this form like
{% body block %}
{{ form_start(form, {"attr": {"novalidate": "novalidate"}}) }}
{{ form_widget(form.title) }}
{{ form_widget(form.description) }}
{{ form_widget(form.price) }}
{{ form_rest(form) }}
{{ form_end(form, {"attr": {"novalidate": "novalidate"}}) }}
{% endblock %}
I'm getting something like default message that I cannot add an advert, but there are no messages next to my fields. What am I doing wrong?
I've tried use novalidate attribute in every form field but it still doesn't working
You should use {{ form_row(form.title) }} instead {{ form_widget(form.title) }} and so on. Alternatively add {{ form_error(form.title) }} to every {{ form_widget(form.title) }} and so on.
Explanation: form_widget render only form control ie. input box or drop-down. form_row render: form_label - the title of field, form_widget - the control, form_error if needed - the errors attached to field. It also wrap everything on nice div to group related parts.

Symfony3 form builder form field in span instead of div

I have Symfony3 app and I am making a simple form the code in the twig is as follows
{{ form_start(edit_form) }}
{{ form_widget(edit_form) }}
<input type="submit" value="Edit" />
{{ form_end(edit_form) }}
Pretty simple. What this code creates is a form and each form field is within it's own <div> which is fine, but if the type is date here is what the generated html looks like
<div>
<label class="required">Term</label>
<div id="appbundle_project_term">
<select id="appbundle_project_term_year" name="appbundle_project[term][year]"></select>
<select id="appbundle_project_term_year" name="appbundle_project[term][month]"></select>
<select id="appbundle_project_term_year" name="appbundle_project[term][day]"></select>
</div>
</div>
What bugs me is the inner div created for the date type field. Is there a way in the FormBuilder to keep the type date but remove this inner div without using javascript to handle it or in the twig template. Simply to say - "inner tag => span".
This is pretty generic question as I am looking for a way to usually change the auto generated tags, but if needed here is how this form field is created in form builder
add('term',DateType::class, array(
'widget' => 'choice',
'label'=>"Term",
'data'=>$project->getTerm()
))
You can override form rendering, there are few ways.
The simplest one is overriding form theme widget block (in this case date_widget) and setting form_theme to _self.
Basic example:
{% form_theme form _self %}
{% block date_widget %}
<span>
{% if widget == 'single_text' %}
{{ block('form_widget_simple') }}
{% else %}
{# rendering 3 fields for year, month and day #}
{{ form_widget(form.year) }}
{{ form_widget(form.month) }}
{{ form_widget(form.day) }}
{% endif %}
</span>
{% endblock %}
{% block content %}
{# ... form rendering #}
{{ form_row(form.someDateField) }}
{% endblock %}

How to render same form multiple times in Symfony2?

I have a template with multiple carts. There can be a variable amount of carts, there's no fixed limit.
In each cart I want to have a form where the user can select a country. If he submits the form the shipping costs should be established.
Now I'm doing the following to achieve it in twig:
{% for cart in carts %}
{# Some template stuff #}
{{ form_start(form) }}
<div class="form-input">
<label for="country" class="middle-color">Country <span class="active-color">*</span></label>
{{ form_widget(form.country) }}
{{ form_end(form) }}
{% endfor %}
This is my form builder:
$form = $this->createFormBuilder()
->add('country', 'choice', array('choice_list' => $choiceList, 'label' => 'country',
'attr' => array('class' => "custom-selectbox dark-color light-gradient")))
->getForm();
Now the problem is that this logic works fine for the first cart, but there's no form displayed for further carts. How can I deal with this?
I came across this and another question about the similar issue. You can find my first answer for a solution here.
To wrap it up, I did not call the createView() function on the form in the controller, as usually done when passing the form to the view, but in the twig view itself.
E.g. in your controller action you do return the form object itself:
return $this->render('AppBundle:Cart:list.html.twig', ['formObject' => $form];
and in your view you would set the form in each loop:
{% for cart in carts %}
{# Some template stuff #}
{% set form = formObject.createView %}
{{ form_start(form) }}
<div class="form-input">
<label for="country" class="middle-color">Country <span class="active-color">*</span></label>
{{ form_widget(form.country) }}
{{ form_end(form) }}
{% endfor %}
You should use collection form type. Here is a guide to start with How to Embed a Collection of Forms
P.S. Notice that after rendering a form widget Form component marks it as rendered and does not render once more.

Symfony form collection rendering

I'm using symfony 2.3
I have form with field of type "collection"
Visualization code in twig:
{% for field in form.fields %}
{{ form_row(field.name) }}
{% endfor %}
Everything work, expect when form.fields is empty.
Then nothing is visualized in twig loop, witch is fine.
But at the end of the form there is "label" for the element "form.fields". Only label.
Workaround:
{% for field in form.fields %}
{{ form_row(field.name) }}
{% endfor %}
<div class="hidden">
{{ form_row(form.fields) }}
If there are elements, they will be rendered in the loop.
{{ form_row }} will be empty, because all elemets are iterated in the loop above.
But if form.fields is empty then there is "hidden" (in the div) label.
What I'm missing !? Why this is happening !?
Hidden div content:
<div class="form-group"><label class="col-sm-2 control-label required">name</label><div class="col-sm-10"><div id="my-id" data-prototype=""></div></div></div>
Builder config:
$builder->add(
'fieldDataMappers',
'collection',
array(
'type' => new FieldDataType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
)
);
As you have correctly guessed, the Symfony TwigBridge keep track of what is rendered and what is not. This is useful, since there is a function called form_rest(form), which is especially useful for printing hidden form field, and to prevent the "great jupiter! I forgot to print that field!" moments. :) You often find form_rest at the end of the form, just before the submit button.
Also consider that the collection IS a composite form type, which contains a variable list of child form. When the for loop is not triggered, since the form type is empty, the call to {{ form_row(form.fields) }} print out the collection form type. By default, this will print (you've guessed it) the collection label and an empty div. On the other hand, when the collection is not empty, Symfony will consider the collection as rendered, since all children are already rendered (see FormView::isRendered)
You can take a look into Symfony standard theme form_div_layout.html.twig, especially the blocks form_row (which show label printing) and form_widget_compound (the div and the for loop).
So, if you just need to hide the label (quick and dirty, some div are still there), just use:
$builder->add(
'fieldDataMappers',
'collection',
array(
'type' => new FieldDataType(),
'label' => false,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
)
);
Or better, simply output the whole collection widget, without row:
{{ form_widget(form.fieldDataMappers) }}
Or even better, you print the whole collection with:
{{ form_row(form.fieldDataMappers) }}
...and then add a Twig theme to customize the collection output with something like (note the name syntax, and the missing form_label call):
{% block collection_row -%}
<div>
{{- form_errors(form) -}}
{{- form_widget(form) -}}
</div>
<div class="hidden">Something here?</div>
{%- endblock collection_row %}
Hope this help!
{# src/Acme/TaskBundle/Resources/views/Task/new.html.twig #}
{# ... #}
{{ form_start(form) }}
{# render the task's only field: description #}
{{ form_row(form.description) }}
<h3>Tags</h3>
<ul class="tags">
{# iterate over each existing tag and render its only field: name #}
{% for tag in form.tags %}
<li>{{ form_row(tag.name) }}</li>
{% endfor %}
</ul>
{{ form_end(form) }}
{# ... #}
Symfony2 cookbook
http://symfony.com/doc/current/cookbook/form/form_collections.html
Also the field of the collection is named fieldDataMappers not field.
So i think it should be
{% for field in form.fieldDataMappers %}
{{ form_row(field.name) }}
{% endfor %}
{{ form_label(form.emails) }}
<ul id="email-fields-list"
data-prototype="{{ form_row(form.emails.vars.prototype)|e }}"
data-widget-tags="{{ '<ol></ol>'|e }}"
data-widget-counter="{{ form.emails|length }}">
{% for email in form.emails %}
<ol>
{{ form_errors(email) }}
{{ form_row(email) }}
</ol>
{% endfor %}
</ul>
<button type="button" class="add-another-collection-widget" data-list-selector="#email-fields-list">Add email</button>
{{ form_widget(form.emails) }}
I just add {{ form_widget(form.emails) }} after block thant handles adding to collection and no more label on the end of form.
Cheers
I solved this with :
{{ form_label(form.collection) }}
{% for element in form.collection %}
{{ form_widget(element) }}
{% else %}
{{ form_widget(form.collection) }}
{% endfor %}
(a bit late, I know, but still a problem with Symfony 5)

Twig/Symfony not rendering form_widget

I have a twig template (home.twig), and I'm using
{{ render(controller('WebsiteUserBundle:Registration:register',{ 'template': 'popup'} )) }}
inside that template to render another template (login.twig), which is used to login the user from a popup.
The problem is that the form_widget isn't rendered, but the form_label is.
This is part of my login template:
<div class="row-fluid">
{{ form_label(form.email, 'Email:')}}
{{ form_widget(form.email, { 'attr': {'class': 'span12'} }) }}
</div>
And by "it's not rendered", I mean that there isn't even an empty div or input next to the label element in the DOM.
Why is this happening?
I had the same issue and was because I had the follow sentence before the form_widget
{{ form_rest(form) }}
Check that the form_rest be after of all your form_widget.
if you get only form_widget without extra params, like this :
{{ form_widget(form.email) }}
do you have more informations or any output ?

Categories