Consultez la FAQ sur le ZF avant de poster une question
Vous n'êtes pas identifié.
Bonjour,
je démarre un nouveau sujet pour être certain d'être lu.
J'essaie pour le projet en cours de faire un truc super clean avec des formulaires purement css et l'utilisation de Zend_Form.
J'ai changé les decorator des zones pour les mettre dans des balises <p>
Mais je suis incapable de mettre mes deux boutons 'envoyer' et 'Effacer' cote à cote.
Si j'essaye de jouer avec les décorators je me retrouve avec des choses du style
Envoyer : [ici un bouton vide a coté]
Enfin bref, après une demi journée la dessus, je veux bien un peu d'aide
Merci.
Hors ligne
J'ai encore un peu avancé :
// Submit $hotel_submit = new Zend_Form_Element_Submit('submit'); $hotel_submit->setLabel('Enregistrer'); $hotel_submit->removeDecorator('DtDdWrapper'); // Reset $hotel_reset = new Zend_Form_Element_Reset('reset'); $hotel_reset->setLabel('Effacer'); $hotel_reset->removeDecorator('DtDdWrapper');
Quel decorator ajouter et comment pour avoir mes boutons au centre ou à droite ?
Hors ligne
Arrête de penser décorateur
Les décorateurs sont la juste pour rajouter des balises si besoin.
Utilise le CSS.
Tu devrais regarder sur le net qq cours et astuces en CSS.
Si tu es dans un container en position relative, un coup de 'right: 0;' devrait placé tes boutons à droite.
Ou bien, float: right;
ou bien en jouant avec les marge
ou bien même un text-align: right peut suffire
...
Il y a plusieurs solutions selon la disposition de toute ta page.
Hors ligne
Pour mes feuilles de style c'est ok, tous mes champs sont correctement alignés avec ça :
$this->headStyle(' p { margin: 2px; } #form01 label { display: block; width: 25%; float: left; padding-right: 1%; text-align: right; } ');
Mais dans mon formulaire pour mes boutons j'ai :
// Submit $hotel_submit = new Zend_Form_Element_Submit('submit'); $hotel_submit->setLabel('Enregistrer'); // Reset $hotel_reset = new Zend_Form_Element_Reset('reset'); $hotel_reset->setLabel('Effacer');
Le code Généré est :
<dt> </dt><dd> <input type="submit" name="submit" id="submit" value="Enregistrer" /></dd> <dt> </dt><dd> <input type="reset" name="reset" id="reset" value="Effacer" /></dd>
Et dans ce cas je peux bien mettre tous les css du monde ça ne va pas me les mettre côte à côte et centrés.
Pour les champs du formulaire j'ai une classe qui hérite de Zend_Form_Decorator_Abstract et je surcharge render :
public function render($content) { $element = $this->getElement(); if (!$element instanceof Zend_Form_Element) { return $content; } if (null === $element->getView()) { return $content; } $separator = $this->getSeparator(); $placement = $this->getPlacement(); $label = $this->buildLabel(); $input = $this->buildInput(); $errors = $this->buildErrors(); $desc = $this->buildDescription(); $output = '<p>' . $label . $input . $errors . $desc . '</p>'; switch ($placement) { case (self::PREPEND): return $output . $separator . $content; case (self::APPEND): default: return $content . $separator . $output; } }
Mais ça ne convient pas aux boutons (les labels se trouvent à côté des boutons !)
si j'enlève les décorators avec : removeDecorator('DtDdWrapper'); j'ai bien mes deux boutons côte à côte, mais pas sous le formulaire.
Bref, seuls les CSS ne peuvent résoudre mon problème puisque les boutons ont des balises <DT> etc...
J'ai aussi essayé de créer un groupe avec les boutons dedans, mais après je n'arrive pas à définir une mise en forme juste au niveau du groupe.
Franchement, la doc sur Zend_Form devrait fournir un exemple CONCRET, donc avec 2 boutons (submit, erase) qui ne sont pas traités comme les champs (sinon on se retrouve avec les labels à côté des boutons) et que ces boutons puissent être personnalisés et mis l'un à coté de l'autre, donc avec les bonnes balises qui les sépare, ou bien un groupe pour les boutons...
Hors ligne
ok, tu enlèves les dd/dt
Par contre, je ne comprends pas pourquoi tes boutons sont avant ! Tu as surement un défaut dans la conception de ton formulaire pour qu'ils apparaissent avant.
Peut-on voir la façon dont tu construis ton formulaire en entier ?
As tu des displaygroup ? car je sais que des fois, ça peut perturber l'ordre des éléments si tu les rajoutes après un élément qui n'est pas dedans
chez moi :
... $this->addElement('text', 'expire_date', array( 'decorators' => $this->_standardElementDateTextDecorator, 'label' => 'Date d\'expiration (jj/mm/aaaa):', 'Validators' => array( array('Date', false, array('dd/mm/YYYY')), ), 'class' => 'textInput', )); $this->addDisplayGroup( array('group_id', 'comment', 'expire_date'), 'useraccountinfo', array( 'disableLoadDefaultDecorators' => true, 'decorators' => $this->_standardGroupDecorator, 'legend' => 'Règlages du compte' )); $this->addElement('button', 'submit', array( 'decorators' => $this->_buttonElementDecorator, 'label' => 'Créer le compte', 'class' => 'submitButton', 'type' => 'submit' )); $this->addElement('button', 'reset', array( 'decorators' => $this->_buttonElementDecorator, 'label' => 'Reset', 'class' => 'resetButton', 'type' => 'reset' ));
Après, je gère tous avec les CSS.
Dans mon cas, mes boutons ne sont pas cote à cote, mais l'un en dessous de l'autre, mais ils sont à droite avec un simple text-align: right;
Tous mes champs sont dans des diplaygroups( avec fieldset) sauf mes boutons.
Personnellement, je fais comme ca, mais on a tous une manière de faire.
Hors ligne
Non non, mes boutons ne sont pas avant.
Quand je dis qu'il ne sont pas sous le formulaire je veux simplement dire qu'ils sont bien après le formulaire mais pas dessous, ils sont décalés à gauche...
Voici la totalité du formulaire :
public function getForm() { $form = new Zend_Form(); $form->setAction('') ->setMethod('post'); $form->setAttrib('id','form01'); $form->addElementPrefixPath('Decorator', 'Decorator/', 'decorator'); // hotel_nom: $hotel_nom = $form->createElement('text', 'hotel_nom'); $hotel_nom->addValidator('alnum') ->setDecorators(array('Composite')) ->addValidator('regex', false, array('/^[a-z]+/')) ->addValidator('stringLength', false, array(3, 45)) ->setRequired(true) ->setLabel('Nom de l\'hôtel'); // hotel_nomchaine: $hotel_nomchaine = $form->createElement('text', 'hotel_nomchaine'); $hotel_nomchaine->addValidator('alnum') ->addValidator('regex', false, array('/^[a-z]+/')) ->setDecorators(array('Composite')) ->addValidator('stringLength', false, array(3, 45)) ->setRequired(false) ->setLabel('Nom de la chaîne hotelière'); // hotel_adresse1: $hotel_adresse1 = $form->createElement('text', 'hotel_adresse1'); $hotel_adresse1->addValidator('alnum') ->addValidator('regex', false, array('/^[a-z]+/')) ->setDecorators(array('Composite')) ->addValidator('stringLength', false, array(3, 45)) ->setRequired(true) ->setLabel('Première ligne adresse'); // hotel_adresse2: $hotel_adresse2 = $form->createElement('text', 'hotel_adresse2'); $hotel_adresse2->addValidator('alnum') ->addValidator('regex', false, array('/^[a-z]+/')) ->setDecorators(array('Composite')) ->addValidator('stringLength', false, array(3, 45)) ->setRequired(false) ->setLabel('Deuxième ligne adresse'); // hotel_ville: $hotel_ville = $form->createElement('text', 'hotel_ville'); $hotel_ville->addValidator('alnum') ->addValidator('regex', false, array('/^[a-z]+/')) ->setDecorators(array('Composite')) ->addValidator('stringLength', false, array(3, 45)) ->setRequired(true) ->setLabel('Ville'); // hotel_codepostal: $hotel_codepostal = $form->createElement('text', 'hotel_codepostal'); $hotel_codepostal->addValidator('alnum') ->addValidator('regex', false, array('/^[0-9]+/')) ->setDecorators(array('Composite')) ->addValidator('stringLength', false, array(5, 5)) ->setRequired(true) ->setLabel('Code Postal'); // hotel_departement: $hotel_departement = $form->createElement('text', 'hotel_departement'); $hotel_departement->addValidator('alnum') ->addValidator('regex', false, array('/^[0-9]+/')) ->setDecorators(array('Composite')) ->addValidator('stringLength', false, array(2, 3)) ->setRequired(false) ->setLabel('Département'); // hotel_pays: $hotel_pays = $form->createElement('text', 'hotel_pays'); $hotel_pays->addValidator('alnum') ->addValidator('regex', false, array('/^[a-z]+/')) ->setDecorators(array('Composite')) ->addValidator('stringLength', false, array(3, 20)) ->setRequired(false) ->setLabel('Pays'); // hotel_telephone: $hotel_telephone = $form->createElement('text', 'hotel_telephone'); $hotel_telephone->addValidator('alnum') ->addValidator('stringLength', false, array(10, 15)) ->setDecorators(array('Composite')) ->setRequired(true) ->setLabel('Téléphone'); // hotel_fax: $hotel_fax = $form->createElement('text', 'hotel_fax'); $hotel_fax->addValidator('alnum') ->addValidator('stringLength', false, array(10, 15)) ->setDecorators(array('Composite')) ->setRequired(false) ->setLabel('Fax'); // hotel_email: $hotel_email = $form->createElement('text', 'hotel_email'); $hotel_email->addValidator('regex', false, array('/^[a-z]+/')) ->setDecorators(array('Composite')) ->addValidator('stringLength', false, array(3, 20)) ->setRequired(true) ->setLabel('Email'); // hotel_siteweb: $hotel_siteweb = $form->createElement('text', 'hotel_siteweb'); $hotel_siteweb->addValidator('alnum') ->addValidator('stringLength', false, array(10, 100)) ->setDecorators(array('Composite')) ->setRequired(false) ->setLabel('Site Web'); // Submit $hotel_submit = new Zend_Form_Element_Submit('submit'); $hotel_submit->setLabel('Enregistrer'); $hotel_submit->removeDecorator('DtDdWrapper'); $hotel_submit->addDecorators(array( array('ViewHelper'), array('Errors'), array('HtmlTag', array('tag' => 'span')), )); // Reset $hotel_reset = new Zend_Form_Element_Reset('reset'); $hotel_reset->setLabel('Effacer'); $hotel_reset->removeDecorator('DtDdWrapper'); $hotel_reset->addDecorators(array( array('ViewHelper'), array('Errors'), array('HtmlTag', array('tag' => 'span')), )); // Add elements to form: $form->addElement($hotel_nom) ->addElement($hotel_nomchaine) ->addElement($hotel_adresse1) ->addElement($hotel_adresse2) ->addElement($hotel_ville) ->addElement($hotel_codepostal) ->addElement($hotel_departement) ->addElement($hotel_pays) ->addElement($hotel_telephone) ->addElement($hotel_fax) ->addElement($hotel_email) ->addElement($hotel_siteweb) // use addElement() as a factory to create 'Login' button: //->addElement('submit', 'creation', array('label' => 'Enregistrer')) ->addElement($hotel_submit) ->addElement($hotel_reset); $translations = array( Zend_Validate_StringLength::TOO_SHORT => "'%value%' fait moins de %min% caractères de long", Zend_Validate_StringLength::TOO_LONG => "'%value' dépasse %max% caractères qui est le maximum", Zend_Validate_NotEmpty::IS_EMPTY => "Le champ est requis" ); // instancier un translator et le passer son adapter à notre objet Zend_Form $translate = new Zend_Translate('array', $translations, 'fr'); $form->setDefaultTranslator($translate); return $form; }
et j'utilise cette classe pour gérer finement mes séparateurs entre les labels et les champs de saisie etc :
class Decorator_Composite extends Zend_Form_Decorator_Abstract { public function buildLabel() { $element = $this->getElement(); $label = $element->getLabel(); if ($translator = $element->getTranslator()) { $label = $translator->translate($label); } if ($element->isRequired()) { $label = '* '.$label; } $label .= ' : '; return $element->getView() ->formLabel($element->getName(), $label); } public function buildInput() { $element = $this->getElement(); $helper = $element->helper; return $element->getView()->$helper( $element->getName(), $element->getValue(), $element->getAttribs(), $element->options ); } public function buildErrors() { $element = $this->getElement(); $messages = $element->getMessages(); if (empty($messages)) { return ''; } return '<div class="errors">' . $element->getView()->formErrors($messages) . '</div>'; } public function buildDescription() { $element = $this->getElement(); $desc = $element->getDescription(); if (empty($desc)) { return ''; } return '<div class="description">' . $desc . '</div>'; } public function render($content) { $element = $this->getElement(); if (!$element instanceof Zend_Form_Element) { return $content; } if (null === $element->getView()) { return $content; } $separator = $this->getSeparator(); $placement = $this->getPlacement(); $label = $this->buildLabel(); $input = $this->buildInput(); $errors = $this->buildErrors(); $desc = $this->buildDescription(); $output = '<p>' . $label . $input . $errors . $desc . '</p>'; switch ($placement) { case (self::PREPEND): return $output . $separator . $content; case (self::APPEND): default: return $content . $separator . $output; } } }
Je voulais mes deux boutons côte à côte et alignés exactement sous la colonne des champs de saisie et tout ça sans tableau et que ça resiste au redimenssionnement et que ça marche sous FireFox, IE et Safari...
C'est résolu et encadrant mon formulaire dans un <div> avec ça comme style :
display: block; width: auto; float: left; padding-right: 10%; text-align: right;
Sinon, dans ton exemple :
$this->addDisplayGroup( array('group_id', 'comment', 'expire_date'), 'useraccountinfo', array( 'disableLoadDefaultDecorators' => true, 'decorators' => $this->_standardGroupDecorator, 'legend' => 'Règlages du compte' ));
Le standardGroupDecorator, ça fait partie du ZF ? Peux-tu mettre à la place une série de décorator ?
J'avoue avoir essayé les groupes, mais ç'est parti un peu en vrille...
Merci pour ton aide.
Hors ligne
Ton formulaire est bien construits, pas de problème
Pour ton décorateur, il te manques pleins d'astuces qui t'aurait simplifié la vie :
- le décorateur de label possède des options qui te permette de rajouter ton *
requiredPrefix
requiredSuffix
optionalPrefix
optionalSuffit
De plus, pour rajouté le ':' partout sur tes labels, on peut le faire en css :
label:after { content: ':'; }
Pour la description, le décorateur possède une proriété 'tag'. Cette propriété va en fait permettre au décorateur Description de "sur-décorerer" grace au décorateur htmlTag
Dans ton cas :
'tag' => array('tag' => 'div', 'class' => 'error');
(Dommage que errors ne possède pas cet option)
Pour ton <p> rajouté, c'est htmltag qui aurait put t'aider
Tu as réalisé un seul décorateur qui va réaliser toutes les décorations. Cet une façon de voir les choses et c'est ton choix.
Mais je me permet quand même de te réexpliquer comment le ZF fonctionne.
C'est comme les poupées russes, chaque pouée est conteneur et devient contenant.
Chaque décorateur englobe les autres.
Ceci me vient à te montrer mon standardGroupDecorator. Ca ne vient pas du ZF, cette propriété est défini dans une classe parente que voici :
protected $_standardGroupDecorator = array( 'FormElements', array('Fieldset', array('class' => 'inlineLabels')) );
Donc :
1/ décorateur 'formsElements' : on génère les élements
2/ décorateur 'fielset' : on entoure de balise fieldset
J'ai plusieurs groupes de décorateurs selon les élements
les élements 'standart' (input, textarea, select,..)
protected $_standardElementDecorator = array( 'ViewHelper', array('Description', array('class' => 'formHint')), array('Label', array('requiredPrefix' => '<em>*</em>', 'escape' => false)), array('Errors', array('placement' => 'prepend')), array('HtmlTag', array('tag' => 'div', 'class' => 'ctrlHolder')) );
ca donne pour un input requis :
<div class="ctrlHolder"> <label for="name" class="required"><em>*</em>Nom:</label> <input type="text" name="name" id="name" value="" class="textInput" /> </div>
ou pour mes boutons :
protected $_buttonElementDecorator = array( 'ViewHelper', array('HtmlTag', array('tag' => 'div', 'class' => 'buttonHolder')) );
Si tu as réglé ton problème de position de bouton, tant mieux.
J'espère néanmoins d'avoir éclairé sur le fonctionnement des décorateurs, car il me semble que tu as du mal à comprendre.
bon courage
Hors ligne
... j'arrive après la bataille mais je te conseille d'aller voir les tutoriaux de Padraic Brady , notamment le "PART 6" qui fait partie d'une série de 10 chapitres : http://blog.insicdesigns.com/category/zfblog-tutorial/
... je recommande leur lecture, ils sont très bien écrits
... parmis les sujets traités : comment mettre tout un site ZF, y compris Mysql, en UTF8. Impossible de trouver ce type de sujet sur un site américain !
Jean
Hors ligne
PS : le 10eme chapitre n'a pas encore été reposté
Hors ligne
nORKy, merci pour cette précision ! Toi aussi, tu as de saines lectures !
Rencontrant des difficultés dans la gestion des boutons, en complément à ce Post, j'ai un cas bizarre (bug ???)
C'est en essayant d'écrire l'action "delete" du tutorial PART 8 de Brady (... je me lance!!!).
Dans mon controller, je veux afficher un message de confirmation avant suppression d'un enregistrement.
Voici l'extrait de code :
public function deleteAction()
{
// message de confirmation : voulez-vous supprimer... ?
// on supprime l'enregistrement et on affiche le message "enregistrement supprimé"
$form = new Insic_Form_EntryDelete; // instanciation de la Form
// Vérifier la confirmation de suppression
if(!$this->getRequest()->isPost()){
// si pas POST : afficher la Form
$id = $this->_getParam('id'); // récupération de l'id qui est passé depuis l'URL
$data = array('id'=>$id);
$form->populate($data); // id passé dans la Form en champ caché
$this->view->entryDelete = $form; // la form est affichée depuis la vue
return;
}
// SI JE SUPPRIME LE elseif SUIVANT:
// - je ne récupère plus la valeur de l'id qui est posté
// - le controle $form->nomDunBouton->isChecked() ne fonctionne pas !
// - la valeur des boutons dans tous les cas accessible depuis $this->_hasParam('nomDunBouton')
// SI JE CONSERVE LE elsif SUIVANT, TOUT FONCTIONNE
elseif (!$form->isValid($_POST)){
$this->view->entryDelete = $form;
return;
}
else {
// la form est POSTée
// vérifier la confirmation de suppression
$values = $form->getValues();
// verifier la valeur de "id"
echo "Valeur de id=" . $values['id'] . "* "; // renseigne l'id uniquement si le elseif précédent est actif !!!
if($this->_hasParam('supprimerOui')){ // supprimerOui est le nom d'un des deux boutons
echo "ischecked" . $form->supprimerOui->isChecked() ."*";
echo "Supprimer=supprimerOui a été sélectionné " ;
}
elseif($this->_hasParam('supprimerNon')) { // nom du deuxième bouton
echo "Annuler=supprimerNon a été sélectionné " ;
echo "ischecked" . $form->supprimerNon->isChecked() ."*";
}
else {
echo "Aucun bouton sélectionné";
}
// reste à supprimer l'enregistrement uniquement si la suppression est confirmée...
}
}
J'ai donc l'impression que la condition du elseif $form->isValid($_POST) "force" le POST qui sinon n'est pas effectué.
Qu'en penses-tu ?
Gilles
Hors ligne
Merci Jean pour le lien,
et vraiment merci beaucoup pour tes explications sur les decorators.
En effet je n'ai pas bien abordé le problème, je vais changer ça, sinon ça m'oblige à avoir autant de classes qui vont redéfinir Zend_Form_Decorator_Abstract que de mise en forme différentes. C'est un peu stupide (beaucoup ?)
Je voyais la facilité d'accéder au format des erreurs avec
public function buildErrors() { $element = $this->getElement(); $messages = $element->getMessages(); if (empty($messages)) { return ''; } return '<span style="color: red;" class="errors">' . $element->getView()->formErrors($messages) . '</span>'; }
C'est le problème de ne pas avoir assez le temps de prendre de la distance et d'expérimenter.
Hors ligne
jean: c'est normal.
Quand tu créés ton formulaire (new ...) les valeurs ne sont pas remplies.
Quand tu appels isValid($_POST), ton formulaires récupères toutes les valeurs de $_POST avant d'être valider.
Voici mon controlleur :
public function deleteAction() { $user = Doctrine::getTable('User')->find($this->_getParam('id')); if ($this->_request->isPost() && $this->_request->getPost('delete') == 1) { if ($user !== false) $user->delete(); $this->gotoRoute(array(), 'users'); } $this->view->user = $user->toArray(); }
Je teste le poste, et je test directement moi-même la valeur de delete.
Jean-Marc : c'est le gros avantage de Zend : pouvoir faire comme un veux
Hors ligne
nORKy : j'ai adopté la méthode que tu préconises, c'est pas mal du tout et plus léger.
Et j'ai la mise en forme que je voulais sans utiliser la classe surchargée.
J'ai relu la doc du Zend_Form, et c'est moi ou elle n'est pas super didactique...
La réécriture de la classe décorator, ils la donnent en exemple alors que ton approche est beaucoup plus propre et respecte mieux l'approche générale du Framework.
La doc n'est pas la seule responsable de mon incompréhension, mais ça aide pas vraiment.
Hors ligne
nORKy :
Merci pour ta réponse. Lorsque l'on fait isValid, il a donc deux étapes :
1) les valeurs postées sont récupérées
2) la vérification des données s'effectue
... je ne connaissais pas la première étape...
Jean
Hors ligne