Howto: Adding explanations next to form fields with Tableless QuickForm renderer

Today, a user of HTML_QuickForm_Renderer_Tableless asked how he can add explanations next to form fields. An example of such an explaination or additional information is shown in the following screenshot:
Usage of two labels with HTML_QuickForm_Renderer_Tableless

To be able to add another text to an element, we will use a rather unknown feature of HTML_QuickForm. QuickForm supports multiple labels for elements. Instead of using a string of the $label parameter, one can specify an array of strings with labels. For example, instead of

$form->addElement('text', 'email', 'Your email:');

we can also write

$form->addElement('text', 'email',
    array('Your email:',
          'Please check your email address twice.'));

The second step is to modify the template for form elements. The default element template is defined as follows:

$elementTemplate =
"\n\t\t\t<li><label class=\"element\"><!-- BEGIN required
--><span class=\"required\">*</span><!-- END required -->
{label}</label><div class=\"element<!-- BEGIN error -->
error<!-- END error -->\"><!-- BEGIN error --><span
class=\"error\">{error}</span><br /><!-- END error -->
{element}</div></li>";

By adding

<!-- BEGIN label_2 --><br /><span style=\"font-size: 80%;\">
{label_2}</span><!-- END label_2 -->

after {element} we will now make the second label visible. Our new element template is now defined as follows:

$elementTemplateTwoLabel =
"\n\t\t\t<li><label class=\"element\"><!-- BEGIN required
--><span class=\"required\">*</span><!-- END required -->
{label}</label><div class=\"element<!-- BEGIN error -->
error<!-- END error -->\"><!-- BEGIN error --><span
class=\"error\">{error}</span><br /><!-- END error -->
{element}<!-- BEGIN label_2 --><br /><span style=
\"font-size: 80%;\">{label_2}</span><!-- END label_2 -->
</div></li>";

The third and last step is to change the element template in the renderer:

$renderer->setElementTemplate($elementTemplateTwoLabel);

If you need the second label only for a single element, you can also specify the element name as the second parameter:

$renderer->setElementTemplate($elementTemplateTwoLabel,
                              'elementName');

After reloading your modified script, you should get a similar output to the screensheet shown above.

Note: All linebreaks in the code snippets shown above are not required (in the templates they are even discouraged), they are just used to make the code readable.

20 Responses to “Howto: Adding explanations next to form fields with Tableless QuickForm renderer”

  1. seb says:

    hi, nice tutorial,
    i’m trying to put dynamic (client side) error msg after {element} and i didn’t find any good solution

  2. Mark Wiesemann says:

    If I get your idea right, you just need to move the <!– BEGIN error –><span class=\”error\”>{error}</span><br /><!– END error –> block after the {element}, and move the <br /> to the beginning (i.e. after <!– BEGIN error –>).

  3. seb says:

    it works for server-side validation, but it seems the client side validation (HTML_QuickForm_DHTMLRulesTableless) doesn’t use element template for displaying the error message.

  4. Mark Wiesemann says:

    The default templates of the tableless renderer are optimized (and prepared) for the DHTMLRulesTableless package. Maybe we should discuss your problem via email; without more details about your plans and your templates, I can’t tell you much more. Just send me an email with more details, and I’ll try to help you some more.

  5. Mike says:

    this is possibly a bit off-topic

    I stumbled onto QuickForms yesterday after spending a good part of a day designing a large form and realizing that I there has to be a better way.
    So far so good with the Tableless Renderer, but I’m having trouble with the Templates like headerTemplate, ElementTemplate, etc.

    What is the preferred method of changing these? I have used the setHeaderTemplate – but how can I determine what is currently in the template by default? I was looking for a getHeaderTemplate routine.

    For now, I have just copied it from the Tableless.php file and plugged it into my own $var, changed it and am using the calls like setHeaderTemplate.

    I can use the clearAllTemplates and then plug mine back in – is that the preferred method? I’m just having trouble getting my head around the intent of the programmers.

    regards,
    Mike

  6. Mark Wiesemann says:

    Mike, the naming and usage of the set…Template() methods was inspired by the way HTML_QuickForm does it for the default renderer. Therefore, currently the right way is to look into the Tableless.php file, copy the template string, modify them, and to call then set…Template() for those templates that you want to modify. Calling clearAllTemplates() is not needed.

    Providing get…Template() methods would be an option, but I don’t favor such methods because you would use them only once, i.e. for getting the default template string for (e.g.) the header element. For such one tasks it should be sufficient to look into the source code.

    Of course, this does not mean that you cannot convince me of adding such methods. If you have good arguments, I could add them to my renderer or plan to add them to HTML_QuickForm2 (it’s likely that QF2 will have the tableless renderer as the default renderer).

  7. Mike says:

    hi Mark, I agree that a one off routine like a get…Template() is probably a waste of your time. I could always extend your class and add them if I really wanted/needed them.

    I have copied the defaults from the source and made some major changes to my copies already – like stripped out most of the stylesheet class references, the tags, and the formatting \r\t\t stuff.

    I think most of my issues are noob in nature – having only spent 1 day with the whole suite of QF – it’s the lack of reference resources that’s killing me – googling on the subject is what got me here initially.

    What may be needed is a source – reference – discussion – etc – of what one can or cannot do with the template parts and the logic used in them. For example, so far, I only see references to logic for required and error – but are there others?

    I will spend some time reading the source code – that will help a lot.

    I had looked at QF last year but had already made the mental commitment away from tables for most things – I’m glad you have done the tableless renderer.

    regards,
    Mike

  8. Mark Wiesemann says:

    Mike, my point on the getter methods for the templates was not my time (although it is indeed limited currently). Adding them would be a task of a few minutes plus a few minutes for making a new release. But I would do this only if this would help a lot of people. And I don’t see a real need for adding methods which would be used one time and would then bloat up the code without any usage.

    About your question on what can be done with the templates: I can only think of some messages (like required notes, error messages, or some advices / hints like I did in the Howto here). And of course, customization of the templates is needed for layout changes that cannot be achieved by changing the stylesheet.

    If you have more ideas on what can be done, or if you have ideas on how the documentation could be improved, please don’t hesitate to contact me via email. Thanks!

  9. Noel says:

    What I’d like to see is a far more flexible way of creating whole form layouts. QF seems great if I have a form layout one item per row, very vertical. But my clients often want more intelligent groupings of data…perhaps two related items laid out vertically in column 1 and two related items laid out vertically in column 2. Or imagine a situation where I have 2 columns: column 1 contains form elements and column 2 contains an info box. Right now this kind of thing seems entirely non-trivial with QF. While I like QF for its validation and parsing of form data, I find the lack of flexibility of the rendering very limiting. Changing the renderer is all well and good if it pertains to your entire form, but having to modify the renderer 5 times in a single form is tedious, confusing, and I’m not even sure possible.

  10. Mark Wiesemann says:

    @Noel: Column layouts are indeed not trivial with QF. I have an item on my ToDo list about making an example with the tableless renderer that shows at least a rather simple column layout.

    The info box in a second box is already possible: Just follow my example here, but instead of putting the text below the form element, just put it next to the element. Using CSS is also possible, of course.

    And you don’t have to change the renderer multiple times: Just define your layout once, and then use it. (Just overload the default or e.g. my tableless renderer, and you’re done for all forms.)

  11. Noel says:

    Mark, thanks for the reply. I really want to make this work, so I have some follow up questions.

    First of all, what do you mean about defining the layout? As far as I know, I just get to define the template to render a single element, and when QF is rendered into html, it just spits them out all in a row.

    Second of all, when I said info box, I meant I needed to have an info box float right while three elements float left. It would be an info box to describe several elements, not just one, so I’m not sure it works like you explain in this article.

    Is there a way I can just intersperse html throughout the quickform?

  12. Mark Wiesemann says:

    Noel, setFormElementTemplate() can either be called with an element name or without it. If you specify an element name, the given layout string is used only for this element, otherwise it will be used for all elements that don’t have got own layouts.

    The ‘static’ and ‘html’ (this one is deprecated) QF elements allow you to add HTML code directly to your form.

    Putting a box next to multiple elements might need some hacks. For example, you could group these elements into a fieldset, and use two DIVs in this fieldset. The first DIV for the form elements, the second DIV for the info box.

    If you have further questions, please send an email. This comment section is not the best place for such questions.

  13. Noel says:

    The approach you outline above is something I am planning to try in the next few days. One thing I don’t like about it though is it seems tedious and inelegant to have to set the form element template for each and every element, which some forms might call for. At that point, it really stops being a template doesn’t it?

    What could be cool to have would be a setLayout() function that I could call before I call toHtml(). The layout could be html with substitutions for where I want each element to go. It’s an extra parsing step for QF to perform, but it seems to me the simplest, most straightforward, easiest for non-programmers to edit, way of having a form look exatly how I need it to.

  14. Noel says:

    Also I have a criticism with static. There are times when I want to start some html above some elements, and close it after. Using static doesn’t really allow that because it uses the template which opens and closes wrapping html tags. Since the html element is deprecated, currently QF doesn’t allow any method other than to override static with a custom template (something you can’t do for types of elements, but merely single instances of elements). Again this can all get very tedious and defeat the value of QF.

  15. I hope that you can shed some light on a problem I am having.

    I am using the HTML_QuickForm_Renderer_Tableless renderer in the following fashion:


    $form = $this->createForm();
    $form->accept(new HTML_QuickForm_Renderer_Tableless);
    if ($form->validate())
    {
    return “form valid”;
    } else {
    return $renderer->toHtml();
    }


    When I submit the form without filling in all required fields, I get the form again (i.e. the form is not valid as expected) but I do not see any error messages.

    If I simply use: return $form->toHtml(); I see the error messages but obviously I dont get to use the cool renderer?

    Is there a known problem with server side validation messages and
    HTML_QuickForm_Renderer_Tableless or am I doing something wrong?

    Please feel free to email me about this one if you prefer(skinner [at] destiny-denied.co.uk)

  16. Mark,

    Thank you very much for your quick response.

    accept()ing the renderer after calling the validate() function works perfectly. I should have realised that the renderer is a Visitor.

    Just to confirm for anyone else who has this problem:

    If you are having problems with the server side validation messages when using a Renderer make sure that you call:

    $form->validate();

    before:

    $form->accept($renderer);

  17. Noel says:

    You’re welcome, but I’m not Mark. But he helped me, and I helped you, so it’s been paid forward…

    BTW, I’ve overloaded setElementTemplate in my custom renderer (inherits from tableless).

    http://phpfi.com/269963

    The innovation is simply to allow me to pass an array of elements to set them all to the same custom template. I find this to be super useful as I am often delegating 3 different templates across my form.

    To illustrate the usefulness:

    http://phpfi.com/269964

    (edited: moved code samples to phpfi.com)

  18. Yes I have been using that too, its really useful!

  19. Alex says:

    Hi Mark:

    I hope this thread is still active.

    I want to put a class identifier inside an element of the form rendered with quickform. i.e. inside the input type=submit element (the button) in order to be able to style the element with css.

    Is this possible?

    Should I look for other kind of solution?

    I don’t seem to find any information on this.

    Thanks in advance and best regards.

  20. Mark Wiesemann says:

    Alex, if you have created your element via e.g. $element = $frm->addElement(…), you can call $element->setAttribute(‘class’, ‘example’) to add a class attribute to the element.

    HTML_QuickForm extends the HTML_Common class. Documentation for the setAttribute() method can be found in the HTML_Common API documentation:
    http://pear.php.net/package/HTML_Common/docs/latest/HTML_Common/HTML_Common.html