IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Symfony PHP Discussion :

[Symfony 2.7] Formulaire imbriqué [2.x]


Sujet :

Symfony PHP

  1. #1
    Invité
    Invité(e)
    Par défaut [Symfony 2.7] Formulaire imbriqué
    Bonjour,

    J'ai un soucis concernant les formulaires imbriqués. Je me suis basé sur un exemple trouvé ici pour développer mon formulaire.
    Si quelqu'un a quelque chose de mieux, je suis preneur!

    En fait, j'ai un bug dans la gestion de mes éléments.
    Par exemple, lorsque j'ajoute 5 éléments et que je fais exprès de faire une erreur de saisie et que je valide, j'ai bien le message d'erreur. Je supprime le 4ème élément et j'en insère un autre et je valide toujours avec une erreur. L'ancien 5ème élément, qui est devenu le 4ème suite à la suppression, a disparu...
    En débugant, j'ai compris le problème, mais je ne sais pas le corriger.
    En fait, quand je calcul l'index, je me base sur le nombre d'élément dans le formulaire, mais l'ancien 5ème élément portait le numéro 5 et porte toujours le numéro 5 après suppression du 4ème. Sauf que le nouvel élément que je créé porte aussi le numéro 5, donc il y a conflit.
    Savez vous comment régler le problème?

    Voici mon code :

    Dans le formulaire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    ->add('hausses', 'collection', array(
        'type' => new HausseType($builder->getData()),
        'allow_add' => true,
        'allow_delete' => true,
        'label' => false,
        'prototype' => true,
        'prototype_name' => 'hausse__name__',
        'options' => array(
            // options on the rendered HausseTypes
        ),
    Dans la vue :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
     
    {# src/KG/BeekeepingManagementBundle/Resources/views/Visite/add.html.twig #}
     
    {% extends "KGBeekeepingManagementBundle::layout.html.twig" %}
     
    {% block stylesheets %}
        {{ parent() }}
        {% stylesheets filter='cssrewrite' 'css/bootstrap_switch/bootstrap-switch.css' %}
            <link rel="stylesheet" href="{{ asset_url }}" type="text/css">
        {% endstylesheets %} 
        {{ form_stylesheet(form) }}
    {% endblock %}
     
    {% block javascripts %}
        {{ parent() }}
        {% javascripts 'js/bootstrap_switch/bootstrap-switch.js'
                        '@KGBeekeepingManagementBundle/Resources/public/js/visite/add.js'%}
            <script src="{{ asset_url }}" type="text/javascript"></script> 
        {% endjavascripts %}
        {{ form_javascript(form) }}
    {% endblock %}
     
    {% block title %}{{ parent() }} - Créer une visite{% endblock %}
     
    {% form_theme form "KGBeekeepingManagementBundle::form_layout.html.twig" %}
     
    {% macro widget_prototype(widget, remove_text) %}
        {% if widget.vars.prototype is defined %}
            {% set form = widget.vars.prototype %}
            {% set name = widget.vars.prototype.vars.name %}
        {% else %}
            {% set form = widget %}
            {% set name = widget.vars.full_name %}
        {% endif %}
     
        <div data-content="{{ name }}">
            <a class="btn-remove" data-related="{{ name }}">{{ remove_text }}</a>
            <li class="list-group-item">
                {{ form_errors(form) }} 
                <div class="form-group">
                    {{ form_label(form, "Cadres plein :", {'label_attr': {'class': 'col-sm-5 col-md-4 control-label'}}) }}
                    <div class="col-sm-5 col-md-5">
                        {{ form_widget(form) }}
                    </div>
                </div>
            </li>         
        </div>
    {% endmacro %}
     
    {% block ruchers %}{{ colonie.ruche.emplacement.rucher.nom }}{% endblock %}
     
    {% block panel_title %}
        <div class="row">
            <div class="col-xs-12">
                <h4>Création d'une visite</h4>
            </div>
        </div>
    {% endblock %}
     
    {% block form_error %}
        {{ form_errors(form.date) }}   
        {{ form_errors(form.activite) }}
        {{ form_errors(form.pollen) }}   
        {{ form_errors(form.reine) }}
        {{ form_errors(form.celroyales) }}   
        {{ form_errors(form.etat) }}
        {{ form_errors(form.agressivite) }}
        {{ form_errors(form.nourrissement) }}
        {{ form_errors(form.traitement) }}
        {{ form_errors(form.observations) }}
        {{ form_errors(form.nbnourriture) }}
        {{ form_errors(form.nbcouvain) }}
        {{ form_errors(form) }}   
    {% endblock %}
     
    {% block panel_body %}           
        {{ form_start(form, {'attr': {'class': 'form-horizontal'}}) }}
     
            <fieldset>
                <legend>Généralités :</legend>         
                <div class="form-group">
                    {{ form_label(form.date, "Date :", {'label_attr': {'class': 'col-sm-5 col-md-4 control-label'}}) }}
                    <div class="col-sm-5 col-md-5">
                        {{ form_widget(form.date, {'attr': {'class': 'form-control'}}) }}
                    </div>
                </div>   
     
                <div class="form-group">
                    {{ form_label(form.activite, "Activité :", {'label_attr': {'class': 'col-sm-5 col-md-4 control-label'}}) }}
                    <div class="col-sm-5 col-md-5">
                        {{ form_widget(form.activite, {'attr': {'class': 'form-control'}}) }}
                    </div>
                </div>
     
                <div class="form-group">
                    <label class="col-sm-5 col-md-4 control-label" for="checkboxes">Stockage de pollen :</label>
                    <div class="col-sm-5 col-md-5">
                      {{ form_widget(form.pollen, {'attr': {'class': 'form-control', 'data-on-text': "Oui", 'data-off-text': 'Non'}}) }}
                    </div>
                </div>  
     
                <div class="form-group">
                    <label class="col-sm-5 col-md-4 control-label" for="checkboxes">Reine vue :</label>
                    <div class="col-sm-5 col-md-5">
                      {{ form_widget(form.reine, {'attr': {'class': 'form-control', 'data-on-text': "Oui", 'data-off-text': 'Non'}}) }}
                    </div>
                </div>                         
     
                <div class="form-group">
                    <label class="col-sm-5 col-md-4 control-label" for="checkboxes">Cellules royales :</label>
                    <div class="col-sm-5 col-md-5">
                      {{ form_widget(form.celroyales, {'attr': {'class': 'form-control', 'data-on-text': "Oui", 'data-off-text': 'Non'}}) }}
                    </div>
                </div>
     
                <div class="form-group">
                    {{ form_label(form.etat, "Etat :", {'label_attr': {'class': 'col-sm-5 col-md-4 control-label'}}) }}
                    <div class="col-sm-5 col-md-5">
                        {{ form_widget(form.etat, {'attr': {'class': 'form-control'}}) }}
                    </div>
                </div>                     
     
                <div class="form-group">
                    {{ form_label(form.agressivite, "Agressivité :", {'label_attr': {'class': 'col-sm-5 col-md-4 control-label'}}) }}
                    <div class="col-sm-5 col-md-5">
                        {{ form_widget(form.agressivite, {'attr': {'class': 'form-control'}}) }}
                    </div>
                </div>                         
     
                <div class="form-group">
                    {{ form_label(form.nourrissement, "Nourrissement :", {'label_attr': {'class': 'col-sm-5 col-md-4 control-label'}}) }}
                    <div class="col-sm-5 col-md-5">
                        {{ form_widget(form.nourrissement, {'attr': {'class': 'form-control'}}) }}
                    </div>
                </div>     
     
                <div class="form-group">
                    {{ form_label(form.traitement, "Traitement :", {'label_attr': {'class': 'col-sm-5 col-md-4 control-label'}}) }}
                    <div class="col-sm-5 col-md-5">
                        {{ form_widget(form.traitement, {'attr': {'class': 'form-control'}}) }}
                    </div>
                </div>     
     
                <div class="form-group">
                    {{ form_label(form.observations, "Observations :", {'label_attr': {'class': 'col-sm-5 col-md-4 control-label'}}) }}
                    <div class="col-sm-5 col-md-5">
                        {{ form_widget(form.observations, {'attr': {'class': 'form-control'}}) }}
                    </div>
                </div>                           
            </fieldset>   
     
            <fieldset>
     
                <legend>Corps :</legend>
                    <div class="form-group">
                        {{ form_label(form.nbnourriture, "Cadres de nourriture :", {'label_attr': {'class': 'col-sm-5 col-md-4 control-label'}}) }}
                        <div class="col-sm-5 col-md-5">
                            {{ form_widget(form.nbnourriture, {'attr': {'class': 'form-control'}}) }}
                        </div>
                    </div>
     
                    <div class="form-group">
                        {{ form_label(form.nbcouvain, "Cadres de couvain :", {'label_attr': {'class': 'col-sm-5 col-md-4 control-label'}}) }}
                        <div class="col-sm-5 col-md-5">
                            {{ form_widget(form.nbcouvain, {'attr': {'class': 'form-control'}}) }}
                        </div>
                    </div>
            </fieldset>      
     
            <fieldset>
                <legend>Hausses :</legend>
     
                <div class="row">
                    <div class="col-md-offset-3 col-sm-offset-3 col-md-6 col-sm-6">
                        <ul class="list-group">               
                            <div id="post_hausses" data-prototype="{{ _self.widget_prototype(form.hausses, 'Supprimer hausse')|escape }}">
                                {% for widget in form.hausses.children %}
                                    {{ _self.widget_prototype(widget, 'Supprimer hausse') }}
                                {% endfor %}
                            </div>
                        </ul>
                    </div>
                </div>
     
                <a class="btn-add btn-default" data-target="post_hausses">Ajouter hausse</a>
            </fieldset>                   
     
        {{ form_rest(form) }}
     
        <div class="form-group">
            <div class="col-md-12">
                <button type="submit" class="btn btn-primary pull-right">
                    <i class="glyphicon glyphicon-floppy-disk"></i>
                    Sauvegarder
                </button>
            </div>       
        </div>
     
        {{ form_end(form) }}    
    {% endblock %}
    Dans le javascript :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     
    $("[name='kg_beekeepingmanagementbundle_visite[reine]']").bootstrapSwitch();
    $("[name='kg_beekeepingmanagementbundle_visite[celroyales]']").bootstrapSwitch();
    $("[name='kg_beekeepingmanagementbundle_visite[pollen]']").bootstrapSwitch();
     
    jQuery(function($) {
        $(document).on('click', '.btn-add[data-target]', function(event) {
            var collectionHolder = $('#' + $(this).attr('data-target'));
            if (!collectionHolder.attr('data-counter')) {
                collectionHolder.attr('data-counter', collectionHolder.children().length);
            }
     
            var prototype = collectionHolder.attr('data-prototype');
            var form = prototype.replace(/__name__/g, collectionHolder.attr('data-counter'));
            collectionHolder.attr('data-counter', Number(collectionHolder.attr('data-counter')) + 1);
            collectionHolder.append(form);
            event && event.preventDefault();
        });
     
        $(document).on('click', '.btn-remove[data-related]', function(event) {
            var name = $(this).attr('data-related');
            $('*[data-content="'+name+'"]').remove();
            event && event.preventDefault();
        });
    });
    Merci pour votre aide!

  2. #2
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Hello, au lieu de te baser sur le nombre d'élément (length) et de faire +1 pour le nouvel index, essaye plutôt de récupérer l'index du dernier élément (qui sera toujours le plus élevé) et de faire +1.
    De cette manière, tu peux supprimer et ajouter autant que tu veux, tu n'auras jamais deux fois le même index.

  3. #3
    Invité
    Invité(e)
    Par défaut
    Apparemment c'est un problème assez commun : https://github.com/symfony/symfony/issues/7468

  4. #4
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Nico_F Voir le message
    Hello, au lieu de te baser sur le nombre d'élément (length) et de faire +1 pour le nouvel index, essaye plutôt de récupérer l'index du dernier élément (qui sera toujours le plus élevé) et de faire +1.
    De cette manière, tu peux supprimer et ajouter autant que tu veux, tu n'auras jamais deux fois le même index.
    Salut,

    Merci pour ta réponse. Sais tu comment je peux faire pour récupérer l'index du dernier élément. Je suis débutant en javascript.

    Merci pour ton aide.

  5. #5
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Moi aussi mais un petit tour sur la doc de jquery ne prend pas beaucoup de temps ;-)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    <ul>
      <li data-index="1">...</li>
      <li data-index="3">...</li>
      <li data-index="4">...</li>
      <li data-index="7">...</li>
    </ul>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    var last_index = $("ul li:last-child").attr('data-index');
    console.log(last_index); // renvoie 7

  6. #6
    Invité
    Invité(e)
    Par défaut
    Merci.
    En fait, je te posais la question car je ne sais pas comment insérer data-index="1" sur la balise li via le javascript.

    Tu peux m'expliquer ce que ça fait cette commande $("ul li:last-child") ? Je ne comprends pas la syntaxe avec le $...
    Dernière modification par Invité ; 07/10/2015 à 13h51.

  7. #7
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Les attributs que tu places dans le HTML de ton template dépendent de la manière dont tu fais le rendu du ton formulaire.
    C'est la partie form rendering qu'il faut étudier => http://symfony.com/doc/current/cookb...omization.html

    $("ul li:last-child") Cette ligne va récupérer le dernier élément <li> se trouvant à l'intérieur d'un <ul> sur ta page.

    Tu utilises déjà le $ dans ton code à plusieurs reprises, si tu utilises jQuery tu risques de le retrouver souvent ce symbole.
    Là encore pour ce genre de question une recherche google t'aurait fait gagner du temps.
    http://stackoverflow.com/questions/1...nswer-10787372

  8. #8
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Nico_F Voir le message
    Les attributs que tu places dans le HTML de ton template dépendent de la manière dont tu fais le rendu du ton formulaire.
    C'est la partie form rendering qu'il faut étudier => http://symfony.com/doc/current/cookb...omization.html
    J'ai lu la doc mais je ne comprends pas en quoi cela m'aide. Mes éléments sont structurés comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
        <li class="list-group-item" data-content="{{ name }}">
            {{ form_errors(form) }}  
            <div class="form-group">
                {{ form_label(form, "Cadres plein :", {'label_attr': {'class': 'col-sm-5 col-md-4 control-label'}}) }}
                <div class="col-sm-5 col-md-5">
                    {{ form_widget(form) }}
                    <a class="btn-remove" data-related="{{ name }}">{{ remove_text }}</a>                
                </div>
            </div> 
        </li>
    Je sais qu'il faut que je mette data-index dans la balise li, mais je ne sais pas comment. Via le javascript ou directement dans ce code?
    Je me dis que le rajouter dans ce code n'est pas la solution car ça voudrait dire que je dois mettre une valeur dedans alors que je ne la connais pas puisque c'est le JS qui l'a calcule.

    Autre question : je souhaitais mettre {{ form_widget(form, {'attr': {'class': 'form-control'}}) }} à la place de {{ form_widget(form) }} mais ça ne fait pas du tout ce que je voudrais.
    Le code source de ma page apparaît comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    <div id="post_hausses" data-prototype="                        
        <li class="list-group-item" data-content="__name__">
     
            <div class="form-group">
                                <label class="col-sm-5 col-md-4 control-label required">Cadres plein :</label>
                <div class="col-sm-5 col-md-5">
                    <div id="kg_beekeepingmanagementbundle_visite_hausses___name__" class="form-control">    
    			<div>    
    				<input type="text" id="kg_beekeepingmanagementbundle_visite_hausses___name___nbplein" name="kg_beekeepingmanagementbundle_visite[hausses][__name__][nbplein]" required="required" />
    			</div>
    		</div>
                    <a class="btn-remove" data-related="__name__">Supprimer hausse</a>                
                </div>
            </div> 
        </li>          
    ">
    Je ne comprends pas pourquoi il m'a rajouté des divs... J'applique la même syntaxe sur d'autres pages et le résultat est celui-ci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
                    <div class="form-group">
                                        <label class="col-sm-5 col-md-4 control-label required" for="kg_beekeepingmanagementbundle_visite_nbcouvain">Cadres de couvain :</label>
                        <div class="col-sm-5 col-md-5">
                            <input type="number" id="kg_beekeepingmanagementbundle_visite_nbcouvain" name="kg_beekeepingmanagementbundle_visite[nbcouvain]" required="required" pattern=".{1,}" class="form-control" value="5" />
                        </div>
                    </div>
    Comme tu peux le voir, le form-control est dans l'input et non dans un div supplémentaire... C'est assez incompréhensible!

  9. #9
    Membre expérimenté Avatar de Nico_F
    Homme Profil pro
    Développeur Web
    Inscrit en
    Avril 2011
    Messages
    728
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Avril 2011
    Messages : 728
    Points : 1 310
    Points
    1 310
    Par défaut
    Chaque fois que tu rajoute un élément à la collection, du HTML est généré : ce HTML il vient du data-prototype présent dans la div dont l'id est "post_hausses".
    Dans le prototype on peut déjà y trouver data-content="__name__" (ou tu remplace __name__ par une valeur dans le javascript). Tu pourrais rajouter un data-index utilisant le même système.

  10. #10
    Invité
    Invité(e)
    Par défaut
    Bon, ça m'a gonflé, surtout que c'est ce qui s'appelle réinventer la roue ce genre de code...

    Du coup, j'ai installé BraincraftedBootstrapBundle et ça marche super bien. En plus, je n'ai plus aucun problème de mise en page que je pouvais rencontrer avant en mettant du bootstrap dans les vues twig.

    Merci quand même pour tes explications.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [2.x] [Symfony 2] Upload image dans formulaire imbriqué
    Par pixel016 dans le forum Symfony
    Réponses: 7
    Dernier message: 09/01/2015, 16h23
  2. [2.x] [Symfony 2] Formulaire imbriqué + Check & combobox
    Par REF26 dans le forum Symfony
    Réponses: 1
    Dernier message: 19/12/2013, 13h56
  3. formulaire imbriquer + popup
    Par luan220 dans le forum Général JavaScript
    Réponses: 4
    Dernier message: 09/10/2007, 12h11
  4. Probleme Formulaire imbriques
    Par gloglo dans le forum Struts 1
    Réponses: 10
    Dernier message: 30/08/2006, 20h30
  5. formulaire imbriqué
    Par jani dans le forum Langage
    Réponses: 9
    Dernier message: 18/01/2006, 15h40

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo