i'm having problem with forms helper in laravel 5.3.
I have a foreach that shows my data in a form.
Everythins work fine, until i submit a form and validation fails.
Therefore, when validation fails, the input text in my form not show the previus data, but only the data which were submitted.
Here the code:
#foreach($cars as $car)
{!! Form::model($car, ['route' => ['cars.update', $car->id], 'method' => 'PUT']) !!}
<div class="col-md-2">
{{ Form::label('name', $car->name) }}
{{ Form::text('name', $car->name, ['class' => 'editbox-normal']) }}
</div>
<div class="col-md-2">
{{ Form::label('phone', 'Phone') }}
{{ Form::text('phone', null, ['class' => 'editbox-normal']) }}
</div>
<div class="col-md-2">
{{ Form::label('license_plate', 'License plate') }}
{{ Form::text('license_plate', null, ['class' => 'editbox-normal']) }}
</div>
<div class="col-md-2">
{{ Form::label('total_km', 'KM') }}
{{ Form::text('total_km', null, ['class' => 'editbox-normal']) }}
</div>
<div class="col-md-1 col-md-offset-3 flex-space-around">
{{ Form::submit('Submit', ['class' => 'btn btn-edit']) }}
</div>
{!! Form::close() !!}
#endforeach
If i submit one of those forms and validation fails, for example name is required and name input is empty, laravel flash $error, but every input of each form displays the data of the submitted form. In this example, every name input will be empty, and phone, license_plate and total_km will have the same value.
Instead, if i use pure html code for the input, everythinks work fine.
In addition, if you notice, the fist label has the value set to $car->name, the same of the text input, but in the label it is shows correctly, in the input not.
Anyone knows why?
Thanks in advance.
This is actually intended behaviour. This way, when you e.g. forget to enter your name, the rest of the data you entered isn't lost.
In your use case, however, this leads to unintended results (because you have multiple forms on the same page). I would advise splitting the forms up to different pages :)
Related
I am working on an existing Symfony 3.4 based project and trying to add and render a new form. Although the 'label' => false option was used, the fields are rendered including a label. Why?
// Symfony
class SomeController extends Controller {
public function userListAction(Request $request) {
$users = $someService->getUsers();
$formBuilder = $this->createFormBuilder()
->add('users', EntityType::class, [
'label' => false, // also tested '' and 'someLabel'
'class' => 'AppBundle:User',
'choices' => $users,
'multiple' => true,
'expanded' => true,
]);
$variables = array(
'form' => $formBuilder->getForm()->createView(),
);
return $this->render('AppBundle:Pages:user_list.html.twig', $variables);
}
}
// Twig
{% extends 'AppBundle::layout.html.twig' %}
{% block page_content %}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
{% endblock %}
This shows a list of checkboxes for all users including the username as label.
Where does Symfony get the information to use the username as label? As far as I know no custom form widget was defined for the User class. Is there any way to check this for sure? Maybe there is something hidden in the vendor bundles like FOSUserBundle?
Why is the 'label' => false option ignored?
Edit:
Different ways of rendering the form does not solve the problem:
{{ form_start(form) }}
{{ form_row(form) }}
{{ form_end(form) }}
Result:
<div id="form_users">
<div class="form-group">
<div class="checkbox">
<label for="form_users_547">
<input type="checkbox" id="form_users_547" name="form[users][]" value="547">
someUserName
</label>
</div>
</div>
</div>
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
Result:
<div id="form_users">
<div class="checkbox">
<label for="form_users_547">
<input type="checkbox" id="form_users_547" name="form[users][]" value="547">
someUserName
</label>
</div>
</div>
{{ form_start(form) }}
{% for userFormView in form.users %}
{{ form_row(userFormView) }}
{% endfor %}
{{ form_end(form) }}
Result:
Basically the same as before with form_row
You need to use ‘choice_label’ => ‘YOUR PROPERTY PATH’ in the field options.
Pretty match is written in the docs: https://symfony.com/doc/current/reference/forms/types/entity.html#choice-label
If the entity class cast to string then is used if is not it will throw an exception. It looks like your entity User cast to the user name and that’s why it works.
You should try to use {{ form_row(form) }} which should render the whole field correctly.
Using:
->add('name', TextType::class, [
'label' => 'Name',
'required' => true,
'attr' => [
'class' => 'myclass'
],
])
I'm adding the class "myclass" to the <input> element, getting:
<div class="form-group">
<label ...>Name</label>
<input type="text" required="required" class="myclass" ...>
</div>
What if I wanted to add the "myclass" class not to the input itself, but to its <div class="form-group"> container, in order to get:
<div class="form-group myclass">
<label ...>Name</label>
<input type="text" required="required" ...>
</div>
What's the best way to accomplish that?
I actually found a way of doing it in PHP (Symfony 4.4.2). Class has to go into 'row_attr' instead of 'attr'. For the given example it'd be:
->add('name', TextType::class, [
'label' => 'Name',
'required' => true,
'row_attr' => [
'class' => 'myclass'
],
])
related docs: https://symfony.com/doc/current/reference/forms/types/text.html#row-attr
In my case I needed to override the form_row block:
{% block form_row -%}
<div class="form-group myclass{% if (not compound or force_error|default(false)) and not valid %} has-error{% endif %}">
{{- form_label(form) -}}
{{- form_widget(form) -}}
{{- form_errors(form) -}}
</div>
{%- endblock form_row %}
as suggested, the docs helps to understand why.
As #Yoshi pointed out, in the form definition itself you cannot add classes to the container div.
The right way to customize form rendering is to override the base block via Twig as explained here: http://symfony.com/doc/current/cookbook/form/form_customization.html#how-to-customize-an-individual-field (BTW I suggest you to read carefully the whole article to know everything you need to know about customizations).
In the overridden block you have also the widget data if you want e.g. to add a class with the field id:
{% block _product_name_widget %}
<div class="text_widget {{ id }}">
{{ block('form_widget_simple') }}
</div>
{% endblock %}
in this example above, {{ id }} will be replaced with the widget (field) id.
I have a form with a collection and I can see it properly when I use the following syntax:
form_row(form.items)
Then when I try to render it by myself, it is duplicated.
Here is my code in my twig file:
{{ form_errors(form) }}
{{ form_start(form) }}
... some fields here ...
<div id="{{ form.items.vars.id }}" class="collection items" data-prototype="{{ form_widget(form.items.vars.prototype)|e }}">
<h3>{{ form_label(form.items) }}</h3>
Add
{% for item in form.items %}
{{ form_row(item) }}
{% endfor %}
</div>
... some other fields here ...
{{ form_end(form) }}
Here is my code in my form object:
$builder->add('items', 'collection', array(
'type' => new ItemType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'required' => true,
));
Add here is my output:
<!-- This is the one I need, I removed the prototype for the sake of clarity -->
<div id="bundle_data_items" class="collection items" data-prototype="...">
<h3>
<label class="required">Items</label>
</h3>
Add
</div>
<!-- This one is automatically added. The prototype is empty -->
<div>
<label class="required">Items</label>
<div id="bundle_data_items" data-prototype=""></div>
</div>
Do you have any ideas why this happens?
I followed that documentation. Did I miss something?
Edit:
This happens only when my collection is empty
I found the solution to that.
It turns out that when the collection is empty, the rendering of it never occurs. I could use sjagr solution but as there is some fields I want to render automatically, I cannot use it.
So my solution is the following:
{{ form_errors(form) }}
{{ form_start(form) }}
... some fields here ...
<div id="{{ form.items.vars.id }}" class="collection items" data-prototype="{{ form_widget(form.items.vars.prototype)|e }}">
<h3>{{ form_label(form.items) }}</h3>
Add
{% for item in form.items %}
{{ form_row(item) }}
{% else %}
{% do form.items.setRendered %}
{% endfor %}
</div>
... some other fields here ...
{{ form_end(form) }}
When your collection is empty, the for loop in {% for item in form.items %} never gets to execute, hence form_row(item) never happens. This means, to Twig/Symfony, you never actually outputted the field.
Then you do form_end at the end of the form. This is not usually a big deal, but from the docs:
This helper also outputs form_rest() unless you set render_rest to false
So, you must simply pass a false value for render_rest:
{{ form_end(form, {'render_rest': false}) }}
I'm not sure how form_widget did fix your problem, but I'm also not sure how you tried that alternative - perhaps you ran it outside of the for loop.
As an aside, you should consider making your own form.html.twig template file so you can reuse the markup format you've chosen for your collection. It will allow you to simply do form_widget(...) without having to break apart the pieces and your form_end will always consider the field to be outputted even if the collection is empty.
Unfortunately i cannot comment yet (no 50 rep), but perhaps this is caused the browser itself. A rendering issue due to markup someplace? See if the raw http response has the same thing. Since it works with form_row(form.items) but not your own, it "could be" that.
I would check if it's empty also before outputting it.
Similar thing happened to me, label of CollectionType was rendering twice when Collection is empty, but it was rendering once when there are data.
This is my configuration:
$builder->add('items', CollectionType::class, [
'entry_type' => NumberType::class,
'required' => true,
'label' => 'Fields',
'entry_options' => [
'label' => false,
],
'prototype' => true,
])
and in Twig I was displaying Collection label in this way:
{{ form_label(form.items) }} and rendering field widgets in somewhere else. In this case label was displayed twice, once where this code is placed, and the other at the end of form.
Solution is to change it to:
{{ form_row(form.items) }}
or if you need custom rendering for form widgets like in my case, following code will print only labels:
{% if 0 < (form.items|length) %}
{{ form_label(form.items) }}
{% else %}
{{ form_row(form.items) }}
{% endif %}
So I have this form and I want to limit the user input on the text boxes to only 12, so here is the code
<div class="form-group">
{{ Form::label('Bname', 'Breed Name:') }}
{{ Form::text('bname', null, 12, ['class' => 'form-control']) }}
</div>
<div class="form-group">
{{ Form::label('Bname','Breed Name:')}}
{{Form::text('bname',null,12,['class' => 'form-control', 'maxlength' => '12'])}}
</div>
I have a two entities: Products and Categories.
Each entity renders a custom form using the Symfony Form Builder .
I'm trying to include the Categories form inside the Products form using the collection field type. The include line looks like the following:
$builder->add('idCategory', 'collection', array('type' => new CategoryType());
It's working fine (according to the Symfony documentation). But I don't want to create the form with JavaScript, I want to have it rendered with the rest of the form without changing the twig template:
<form action="{{ path('product_create') }}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form) }}
<button type="submit">Create</button>
</form>
Is it even possible? Any thoughts?
Do you know the number of categories you want to include in the products?
I found a workaround for this since it seems to be impossible to auto render the form without using JavaScript.
Instead of rendering the whole form at once, using {{ form_widget(form) }}, I'm rendering field by field and using {{ form_widget(form.categoy.vars.prototype) }} to render the Category collection. The template code looks like the following:
<div id="form">
{{ form_start(form) }}
{{ form_errors(form) }}
...
{{ form_row(form.name) }}
{{ form_row(form.price) }}
...
<h2>Categories</h2>
{{ form_row(form.idCategory) }}
{{ form_widget(form.idCategoryNew.vars.prototype) }}
...
<button type="submit">Create</button>
{{ form_end(form) }}
</div>
And the ProductType:
$builder
->add('name'))
->add('price'))
...
->add('idCategory', 'entity', array('class' => 'MyBundle\Entity\Category'))
->add('idCateogryNew', 'collection', array('type' => new CategoryType(), 'allow_add' => true))
...
EDIT:
I found a better workaround which consist in passing the collection form to the twig template. This way is easy to handle the post request after the form submission. If anyone want the code, please ask :)
EDIT 2:
Per #agapitocandemor request:
Inside your MainEntityController (ProductController in my case), search the method that renders the form (newAction in my case) and create the Entity form that you want to render:
public function newAction()
{
$entity = new Product();
$entityform = $this->createForm(new ProductType(), $entity);
$subentityform = $this->createForm(new CategoryType(), new Category);
return $this->render('MyBundle:Product:new.html.twig', array(
'entity' => $entity,
'entityform' => $entityform->createView(),
'subentityform' => $subentityform->createView()
));
}
Finally, in order to render the subentityform you just need to call {{ form_widget(subentityform) }} from the new template of your main entity.