User:OliverCharles/ServerManual/Forms

From MusicBrainz Wiki
Jump to navigationJump to search

Forms are created in 3 separate parts - designing the form at the data level, designing a view to the form, and adding controller logic to work with the form.

Writing Form Modules

Form modules are handled by writing Form::Processor modules, in the MusicBrainz::Server::Form namespace. You should add a new level of hierarchy to match the name of your controller (for example, MusicBrainz::Server::Form::Annotation::Edit).

Rather than inheriting directly from Form::Processor, it is best to inherit from MusicBrainz::Server::Form - this module exports some small helper methods to create common fields such as edit notes, and may do more in the future.

The single most important method in a form module is profile. This is the skeleton for a template, and will create all relevant fields. Your profile method should return a hash reference, with fields under the required and optional keys.

sub profile
{
    return {
        required => {
            <name> => <type>,
        },
        optional => {
            <name> => <type>,
        },
    }
}

Rather than documenting the details of how profile work here, you should read the official documentation.

For Forms That Enter Edits

If your form creates an edit, you should wrap the result of profile with a call to with_mod_fields. This will create fields for an edit note, and a toggle to change auto-editor status.

sub profile
{
    return shift->with_mod_fields({
        …
    })
}

Form Templates

A fairly comprehensive form template library has been written, and all modules live in the root/forms directory. These templates help you achieve compatibility with the rest of the site, and ensures we have a consistent system (as more than half the website is based around data input).

First, wrap your form with the forms/form.tt template, which will create the <form> tags.

For each field you now have 2 options:

  • Use a generic row: generic rows are the quickest option, and assume one field is on a row, and this field has a label.
  • Manually lay out the row by hand: sometimes you need more control, and you can manually lay your row out by hand.

To use a generic row, use: [% INCLUDE 'forms/generic/row.tt' %] and pass the required field and label parameters. To manually lay out a row, you need to first create a WRAPPER, to contain your row. Then, add a label using the forms/label.tt template, and then add the contents of your row.

Interacting With Forms in Controllers

Forms in controller actions are straight forward. First, add the :Form sub-routine attribute, to your actions definition (much like :Local, etc) passing it the name of your form. You don't need to specify the MusicBrainz::Server::Form part of the namespace, this is assumed.

To access the form in your action, use the $self->form accessor.

To make sure the user has submitted a valid form, use the following pattern:

return unless $self->submit_and_validate($c)

This will lead to your actions having the following pattern (if they use forms):

sub action : Local Form('Controller::Action')
{
   my ($self, $c) = @_;

   # GET request, set up to present form, etc…

   return unless $form->submit_and_validate($c);

   # POST handling…
}

Advanced

Compound Fields

Compound fields are fields that require multiple inputs to create a value. Examples of these are the date input fields (which split into YYYY-MM-DD), and track fields. For the most part, you can create your own composite fields by inheriting from MusicBrainz::Server::Form::Field::Compound, and then writing the rest of your module like a normal form module.

Saddly, compound fields do not behave exactly like a form, so you may well need to use other compound fields as references.

You will also need to write a template to render the compound field. This template should be placed in the root/forms/widgets directory.

Reference

Field Types

MusicBrainz::Server::Form

with_mod_fields

Add fields for an edit note, and the ability to toggle auto-editor status.

Form Template Library

blank_row.tt

Wrapper, for creating rows without a template.

  • No parameters

fieldset.tt

Wrapper, for creating a <fieldset> with a <legend>.

  • Parameters
    • legend: Text to use for the legend

form.tt

Wrapper to create <form> element, display general form errors, etc.

  • Parameters:
    • action: Where to go after the user submits the form. Defaults to the current page
    • method: Just like the method attribute for the <form> tag. Defaults to post.
    • custom: If true, this form will not have the "generic" class.
    • quiet: If true, do not add the text "bold fields are required"
    • moderation: If true, add the edit-note/auto-editor fields.

input.tt

Create an input field (may be <input>, <select> or other type). Can attempt to do just what you mean, if given a minimal set of parameters.

  • Parameters:
    • class: For adding a custom class around the element. Space separated string, just like html (optional).
    • field: The field you are creating. This can be a Form::Processor::Field instance, or just a string for the name of the field (required).
    • value: for manually specifying the value of a field (optional).
    • type: for manually setting the type of the input. Required if field is not an instance, optional otherwise.

label.tt

For creating <label>s. This will correctly create a <label> element for real input controls, or a paragraph otherwise.

  • Parameters:
    • for: The field this label is for. Takes same values as "field" in input.tt (required).
    • class: Apply a class to this label. Like class attribute in html (optional).
    • hidden: Whether this label should be visible or hidden (optional).
    • required: To mark this label as labelling a required field. Only necessary if for is not an object.
    • label: The label itself (required)

row.tt

Wrapper for rows of a generic form

  • Takes no parameters

submit.tt

Add a submit button

  • Parameters:
    • label: The label of the button. Defaults to 'Enter Edit'
    • name: The name of the submit button. Defaults to 'submit'
    • inline: If true, do not place the button on its own row (optional)