web-dev-qa-db-fra.com

Comment personnaliser l'attribut data-prototype dans les formulaires Symfony 2

Depuis plusieurs jours, je bloque un problème avec Symfony 2 et les formulaires.

J'ai obtenu une forme d'entités de sites Web. "Sites Web" est une collection d'entités de sites Web et chaque site Web contient deux attributs: "type" et "url".

Si je veux ajouter plusieurs sites Web dans ma base de données, je peux cliquer sur un lien "Ajouter un autre site Web", qui ajoute une autre ligne de site Web à mon formulaire. Ainsi, lorsque vous cliquez sur le bouton Soumettre, vous pouvez ajouter un ou X site (s) Web en même temps.

Ce processus pour ajouter une ligne utilise l'attribut data-prototype, qui peut générer le sous-formulaire du site Web.

Le problème est que je personnalise mon formulaire pour avoir un excellent rendu graphique ... comme ça:

<div class="informations_widget">{{ form_widget(website.type.code) }}</div>
<div class="informations_error">{{ form_errors(website.type) }}</div>
<div class="informations_widget">{{ form_widget(website.url) }}</div>
<div class="informations_error">{{ form_errors(website.url) }}</div>

Mais le prototype de données ne se soucie pas de cette personnalisation, avec les balises et propriétés HTML et CSS. Je garde le rendu Symfony:

<div>
<label class=" required">$$name$$</label>
<div id="jobcast_profilebundle_websitestype_websites_$$name$$">
<div>
<label class=" required">Type</label>
<div id="jobcast_profilebundle_websitestype_websites_$$name$$_type">
<div>
<label for="jobcast_profilebundle_websitestype_websites_$$name$$_type_code" class=" required">label</label>
<select id="jobcast_profilebundle_websitestype_websites_$$name$$_type_code" name="jobcast_profilebundle_websitestype[websites][$$name$$][type][code]" required="required">
<option value="WEB-OTHER">Autre</option>
<option value="WEB-RSS">Flux RSS</option>
...
</select>
</div>
</div>
</div>
<div>
<label for="jobcast_profilebundle_websitestype_websites_$$name$$_url" class=" required">Adresse</label>
<input  type="url" id="jobcast_profilebundle_websitestype_websites_$$name$$_url" name="jobcast_profilebundle_websitestype[websites][$$name$$][url]" required="required" value="" />
</div>
</div>
</div>

Quelqu'un a-t-il une idée pour faire ce hack?

53
j.2bb

Un peu vieux, mais voici une solution simple et mortelle.

L'idée est simplement de rendre les éléments de la collection via un modèle Twig, afin que vous ayez la possibilité de personnaliser le prototype qui sera placé dans votre data-prototype="..." tag. Comme si c'était une forme normale et habituelle.

Dans yourMainForm.html.twig:

<div id="collectionContainer"
     data-prototype="
         {% filter escape %}
             {{ include('MyBundle:MyViewsDirectory:prototype.html.twig', { 'form': form.myForm.vars.prototype }) }}
         {% endfilter %}">
</div>

Et dans MyBundle: MyViewsDirectory: prototype.html.twig:

<div>
    <!-- customize as you wish -->
    {{ form_label(form.field1) }}
    {{ form_widget(form.field1) }}
    {{ form_label(form.field2) }}
    {{ form_widget(form.field2) }}
</div>

Crédit: adapté de https://Gist.github.com/tobalgists/403221

73
Jivan

Je sais que cette question est assez ancienne, mais j'ai eu le même problème et c'est comme ça que je l'ai aimée. J'utilise un twig macro pour accomplir cela. Les macros sont comme des fonctions, vous pouvez les rendre avec différents arguments.

{% macro information_prototype(website) %}
    <div class="informations_widget">{{ form_widget(website.type.code) }}</div>
    <div class="informations_error">{{ form_errors(website.type) }}</div>
    <div class="informations_widget">{{ form_widget(website.url) }}</div>
    <div class="informations_error">{{ form_errors(website.url) }}</div>
{% endmacro %}

vous pouvez maintenant rendre cette macro où vous le souhaitez. Notez que information_prototype() n'est que le nom de la macro, vous pouvez la nommer comme vous le souhaitez. Si vous souhaitez utiliser la macro pour rendre les éléments donnés et le prototype de la même manière, faites quelque chose comme ceci:

<div class="collection" data-prototype="{{ _self.information_prototype(form.websites.vars.prototype)|e }}">
    {% for website in form.websites %}
        {{ _self.information_prototype(website) }}
    {% endfor %}
    <button class="add-collection">Add Information</button>
</div>

form.websites.vars.prototype Contient les données prototypes du formulaire avec le prototype_name Que vous avez spécifié. Utilisez _self.+macroname Si vous souhaitez utiliser la macro dans le même modèle.

Vous pouvez en savoir plus sur les macros dans la documentation Twig

47
Akkumulator

Vous l'avez probablement découvert depuis, mais voici la solution pour les autres.

Créez un nouveau modèle et copiez/collez ce code dedans: https://Gist.github.com/1294186

Ensuite, dans le modèle contenant le formulaire que vous souhaitez personnaliser, appliquez-le à votre formulaire en procédant comme suit:

{% form_theme form 'YourBundle:Deal:Theme/_field-prototype.html.twig' %}
25

Je sais que cette réponse est très tardive mais peut-être utile pour les visiteurs.

sur votre fichier de thème, vous pouvez simplement utiliser un bloc pour le rendu de chaque entrée de collection de widget de sites Web comme suit:

{% block _jobcast_profilebundle_websitestype_websites_entry_widget %}
     <div class="informations_widget">{{ form_widget(form.type.code) }}</div>
     <div class="informations_error">{{ form_errors(form.type) }}</div>
     <div class="informations_widget">{{ form_widget(form.url) }}</div>
     <div class="informations_error">{{ form_errors(form.url) }}</div>
{% endblock %}

créez également un bloc de thème pour votre ligne de widget de collection comme suit:

{% block _quiz_question_answers_row %}
     {% if prototype is defined %}
        {%- set attr = attr | merge({'data-prototype': form_row(prototype) }) -%}
    {% endif %}

     {{ form_errors(form) }}

     {% for child in form %}
         {{ form_row(child) }}
     {% endfor %}
{% endblock %}

maintenant le prototype et l'entrée de collection rendue seront les mêmes.

5
Karim

J'ai rencontré un problème similaire récemment. Voici comment vous pouvez remplacer le prototype de collection sans avoir à le définir explicitement dans le html:

{% set customPrototype %}
    {% filter escape %}
        {% include 'AcmeBundle:Controller:customCollectionPrototype.html.twig' with { 'form': form.collection.vars.prototype } %}
    {% endfilter %}
{% endset %}
{{ form_label(form.collection) }}
{{ form_widget(form.collection, { 'attr': { 'data-prototype': customPrototype } }) }}

Vous pouvez alors faire ce que vous voulez dans votre brindille personnalisée. Par exemple:

<div data-form-collection="item" data-form-collection-index="__name__" class="collection-item">
<div class="collection-box col-sm-10 col-sm-offset-2 padding-top-20">
    <div class="row form-horizontal form-group">
        <div class="col-sm-4">
            {{ form_label(form.field0) }}
            {{ form_widget(form.field0) }}
        </div>
        <div class="col-sm-3">
            {{ form_label(form.field1) }}
            {{ form_widget(form.field1) }}
        </div>
        <label class="col-sm-3 control-label text-right">
            <button data-form-collection="delete" class="btn btn-danger">
                <i class="fa fa-times collection-button-remove"></i>{{ 'form.collection.delete'|trans }}
            </button>
        </label>
    </div>
</div>

Utile lorsque vous ne devez le faire qu'à des endroits spécifiques et n'avez pas besoin d'un remplacement global applicable à toutes les collections.

5
Krzysztof Karski

J'ai eu un problème quelque peu similaire. Vous devrez peut-être modifier cela pour travailler pour votre cas, mais quelqu'un peut le trouver utile.

Créez un nouveau fichier de modèle pour contenir votre "thème" de formulaire personnalisé

./src/Company/TestBundle/Resources/views/Forms/fields.html.twig

Normalement, vous pouvez utiliser le form_row pour afficher l'étiquette, l'erreur et le widget d'un champ. Mais dans mon cas, je voulais juste afficher le widget. Comme vous le dites, l'utilisation de la fonctionnalité de prototype de données afficherait également l'étiquette, alors dans notre nouveau fields.html.twig, tapez votre code personnalisé pour savoir à quoi vous voulez que le champ ressemble:

{% block form_row %}
{% spaceless %}
        {{ form_widget(form) }}
{% endspaceless %}
{% endblock form_row %}

J'ai supprimé le div du conteneur, ainsi que l'étiquette et l'erreur, et je viens de quitter le widget.

Maintenant, dans le fichier twig qui affiche le formulaire, ajoutez simplement ceci après que {% se prolonge ...%}

{% form_theme form 'CompanyTestBundle:Form:fields.html.twig' %}

Et maintenant, le form_widget (form.yourVariable.var.prototype) ne rendra que le champ et rien d'autre.

2
targnation

Un thème de formulaire à l'échelle de l'application sera appliqué au prototype. Voir Faire des personnalisations à l'échelle de l'application

1
Lou Terrailloune

Voici exemple de code pour un prototype de données personnalisé:

{{ form_widget(form.emails.get('prototype')) | e }}

emails - votre collection.

1
Evgeny Smirnov

Si vous n'avez pas besoin de définir un modèle à l'échelle du système, définissez simplement un modèle dans votre modèle twig, et demandez à twig de l'utiliser).

{# using the profiler, you can find the block widget tested by twig #}
{% block my_block_widget %}
    <div >
      <p>My template for collection</p>
        <div >
          {{ form_row(form.field1)}}
        </div>
        <div>
          {{ form_row(form.field2)}}
        </div>
    </div>
{% endblock %}

{% form_theme form.my_collection _self %}

<button data-form-prototype="{{ form_widget(form.my_collection.vars.prototype) )|e }}" >Add a new entry</button>
0
Julien Fastré

Pour personnaliser différemment les éléments de collection existants VS prototype, vous pouvez remplacer collection_widget comme ceci:

{%- block collection_widget -%}
    {% if prototype is defined %}
        {%- set attr = attr|merge({'data-prototype': form_row(prototype, {'inPrototype': true} ) }) -%}
    {% endif %}
    {{- block('form_widget') -}}
{%- endblock collection_widget -%}

Et puis dans votre entrée personnalisée:

{% block _myCollection_entry_row %}

  {% if(inPrototype is defined) %}
      {# Something special only for prototype here #}
  {% endif %}
{% endblock %}
0
Leto