0

I am currently using this for my new user form:

class UserForm(AdminPage):
    entity = Model.User
    title = 'User'
    class child(ListForm):
        css_class = 'form-horizontal'
        buttons = [SaveButton(),CancelButton()]
        ...
        phone = TextField(
                label = 'Phone',
                validator = twc.Required
        )
        ...

I am assuming that I will have to use something else than a ListForm to do what I want. Here is what I need:

I would like to customize the length of certain form fields, put two form fields next to one another rather than below and change the label on these two fields to appear above the fields rather then on their left.

I spent hours wading through the different versions of tg docs and the 1.0 API, but I could find nothing that worked. I tried adding:

__field_attrs__={'phone':{'rows':'2'}}

but nothing changed. I am assuming a ListForm does not have field_attrs!? Can anyone point me in the right direction on how to achieve what I am looking for?

Thanks a lot!

DennisB
  • 3
  • 3
  • This is not a question about TurboGears2, but about ToscaWidgets2. – Cito Jul 18 '13 at 21:34
  • What do you mean with 1.0 API? – Cito Jul 18 '13 at 22:54
  • I could not find a 2.2 or 2.0 API for that matter, that is why I looked into the 1.0 one. And yes, I suppose it is about ToscaWidgets, I am still new with all this. Thanks, will look it up more. – DennisB Jul 19 '13 at 13:06
  • Note that there is TurboGears 1 (which supports internal widgets and ToscaWidgets 1 which developed from these) and TurboGears 2 (which supports ToscaWidgets 1 and ToscaWidgets 2 which is again a further development). There are important differences between all of these, so always use the proper docs (latest TurboGears 2 docs: http://turbogears.readthedocs.org/en/latest, latest ToscaWidgets 2 docs: http://tw2core.readthedocs.org/en/latest). – Cito Jul 19 '13 at 16:00

1 Answers1

2

You can add CSS classes and styles to your fields like so:

phone = TextField(label='Phone',
                  validator=twc.Required,
                  css_class='MyTextFieldClass',
                  attrs=dict(style='display:block;width:8em', maxlength='12'))

For a completely different layout, you need to subclass BaseLayout and reference your own template as explained here: http://tw2core.readthedocs.org/en/latest/design/#template.

For instance, I have created a more flexible Layout class called FloatLayout:

from itertools import groupby
from tw2.core import Param
from tw2.forms.widgets import BaseLayout

class FloatLayout(BaseLayout):

    template = "widgets.templates.float_layout"

    aside = Param('All fields aside', default=False)

    def rows(self, children):
        """Create the rows."""
        def row_no(child, no=[0]):
            if not self.aside and not getattr(child, 'aside', False):
                no[0] += 1
            return no[0]
        return groupby(children, row_no)

It can be used with this FloatForm class:

from tw2.core import Variable
from tw2.forms import Form

class FloatForm(Form):
    """Form using floating divisions allowing multiple fields per row.

    Fields having the 'aside' attribute set appear on the same row.

    Something like the following should be included in the site CSS file:

    form.floatform {
        margin-bottom: 1ex;
    }
    form.floatform div.row {
        clear: left;
        overflow: hidden;
        height: 100%;
        margin-top: 1.5ex;
    }
    form.floatform div.field {
        float: left;
        margin-right: 1em;
    }
    form.floatform label.fieldlabel {
        display: block;
    }
    form.floatform div.submit {
        margin-top: 3ex;
    }

    """

    template = "widgets.templates.float_form"

    child = Variable(default=FloatLayout)

    css_class = "floatform"

The Genshi template float_layout.html for the FloatLayout is this:

<div xmlns:py="http://genshi.edgewall.org/" py:attrs="w.attrs" py:strip="True">
    <div py:for="row_no, row in w.rows(w.children_non_hidden)"
            class="${row_no % 2 and 'odd' or 'even'} row">
        <div py:for="child in row" py:attrs="child.container_attrs"
            class="field${child.validator and
                getattr(child.validator, 'required', None) and ' required' or ''}"
            title="${w.hover_help and w.help_text or ''}">
            <label py:if="child.label" for="${child.attrs.get('id')}"
                class="fieldlabel" py:content="child.label"/>
            <span py:replace="child.display()"/>
            <span py:if="not w.hover_help and child.help_text"
                class="fieldhelp" py:content="child.help_text"/>
            <span py:if="child.error_msg"
                class="fielderror" py:content="child.error_msg"/>
        </div>
    </div>
    <div py:if="w.children_hidden" style="display:none">
        <div py:for="child in w.children_hidden" py:replace="child.display()"/>
    </div>
</div>

The Genshi template float_form.html for the FloatForm is this:

<form xmlns:py="http://genshi.edgewall.org/"
    class="floatform" py:attrs="w.attrs">
    <div py:if="w.error_msg" class="formerror" py:content="w.error_msg"/>
    <div py:if="w.help_msg" class="formhelp"><p py:content="w.help_msg"/></div>
    <div py:replace="w.child.display()"/>
    <div py:for="button in w.buttons" class="field" py:content="button.display()"/>
</form>

A concrete Form could now look like this:

class UserForm(FloatForm):
    action = url('save_user')
    submit = SubmitButton('Save user')
    user_id = HiddenField(validator=IntValidator())
    user_name = TextField(validator=UserNameValidator(max=16),
        size=20, maxlength=16, label=u'User name:')
    remote_account = CheckBox(validator=BoolValidator(),
        label=u'Remote account:', aside=True)
    new_password = PasswordField(
        validator=PasswordValidator(required=False),
        size=20, maxlength=16, label=u'Password:', aside=True)
    group_id = CheckBoxList(item_validator=IntValidator(),
        label=u'Roles:', css_class='inline')
    display_name = TextField(validator=NameValidator(max=255),
        size=64, maxlength=255, label=u'Real name:')
    mail = TextField(validator=EmailIfLocalValidator(),
        size=64, maxlength=255, label=u'Email address:')

As you see, the fields remote_account, new_password have an attribute aside which causes them to appear on the same line as user_name.

Cito
  • 5,365
  • 28
  • 30
  • I tried adding the css as above, but it does absolutely nothing. – DennisB Jul 18 '13 at 22:04
  • What do you mean with nothing? Don't you get the style and css attribtues in the HTML for the input field? – Cito Jul 18 '13 at 23:08
  • HA! Your change in your code now did the trick. Adding the 'em' made it so that the width actually changed this time around. Thank you. I will look up ToscaWidgets2 a little more to find out how to do the other two things that I need (changing label position and putting two of the fields in line). Thanks so far. – DennisB Jul 19 '13 at 13:07
  • By setting a style of `display:block`, the text field should also appear in a line of its own, below the label. – Cito Jul 19 '13 at 15:27
  • I have added an example `FloatForm` now that gives you full flexibility. – Cito Jul 19 '13 at 15:54
  • This looks great. I am going to give this a try over the weekend and come back to you here. Danke vielmals soweit :) – DennisB Jul 19 '13 at 18:07
  • one more question beforehand: the child =Variable() and aside=Param() lines. What do I have to import for Variable and Param so these are defined? – DennisB Jul 19 '13 at 18:23
  • I have added the missing imports now. – Cito Jul 19 '13 at 21:57
  • Thank you. I am still a beginner at all this, so this all is very useful information. After implementing all this, I ran into an error: ValueError: Could not find engine name for widgets.float_form – DennisB Jul 20 '13 at 13:17
  • it is raised in the get_engine_name() function in tw2.core.templating and I am not sure how to fix it. – DennisB Jul 20 '13 at 13:19
  • This may be because you don't have Genshi installed. Depending on your middleware configuration (`preferred_rendering_engines`) you may also need an explicit prefix `genshi:` in front of the template name. – Cito Jul 20 '13 at 14:18
  • thanks. adding genshi: helped. new error: IOError: Couldn't find source for 'widgets.float_form'. The reason is, that it is looking for the float_form.html deep inside my virtual env folder(/usr/local/tg2/venv/lib/python2.7/site-packages/tw2.core-2.1.6-py2.7.egg/tw2/core/float_form.html), instead of the widgets folder. Anyways, I copied the float_form.html to said folder just to see what happens. No more error message, form is being displayed, but neither size nor aside seem to have any effect on the fields. All of the fields are below one another with the label above each field. – DennisB Jul 21 '13 at 15:08
  • obviously there has to be another way to make it look into the right folder... and of course I still seem to be doing something wrong which is why the form is not displayed according to the set attributes. – DennisB Jul 21 '13 at 15:09
  • Regarding the IOError, the simplest way is to create a subpackage for the Genshi templates in your project. For instance, use a subpackage `myproject.widgets` for your Tosca widgets, and a subpackage `myproject.widgets.templates` for the necessary templates (put an `__init__.py` file in the templates directory). Then you will be able to reference the Genshi template in your widget as `'genshi:myproject.widgets.templates.float_form'`. – Cito Jul 21 '13 at 16:36
  • Regarding the layout, did you add the CSS in the FloatForm docstring to your site CSS file? Alternatively, you can also add a `resources = [CSSLink(...)]` attribute pointing to a file with these styles to the FloatForm class. – Cito Jul 21 '13 at 16:37
  • Oh, I had forgotten about the CSS. Thanks. This was really good help! – DennisB Jul 22 '13 at 00:54