NAME

Mojolicious::Plugin::FormFieldsFromJSON - create form fields based on a definition in a JSON file

VERSION

version 0.16

SYNOPSIS

# Mojolicious
$self->plugin('FormFieldsFromJSON');

# Mojolicious::Lite
plugin 'FormFieldsFromJSON';

DESCRIPTION

Mojolicious::Plugin::FormFieldsFromJSON is a Mojolicious plugin.

CONFIGURATION

You can configure some settings for the plugin:

  • dir

    The directory where the json files for form field configuration are located

    $self->plugin( 'FormFieldsFromJSON' => {
      dir => '/home/mojo/fields',
    });
  • template

    With template you can define a template for the form fields.

    $self->plugin( 'FormFieldsFromJSON' => {
      template => '<label for="<%= $id %>"><%= $label %>:</label><div><%= $field %></div>',
    });

    See Templates.

  • templates

    With template you can define type specific templates for the form fields.

    plugin 'FormFieldsFromJSON' => {
      templates => {
        text => '<%= $label %>: <%= $field %>',
      },
    };

    See Templates.

  • global_attributes

    With global_attributes, you can define attributes that should be set for every field (except hidden fields)

    plugin 'FormFieldsFromJSON' => {
      global_attributes => {
        class => 'important-field',
      },
    };

    So with this configuration

    [
       {
           "label" : "Name",
           "type" : "text",
           "name" : "name"
       },
       {
           "label" : "Background",
           "type" : "text",
           "name" : "background"
       }
    ]

    You get

    <input class="important-field" id="name" name="name" type="text" value="" />
    <input class="important-field" id="background" name="background" type="text" value="" />
  • alias

    Using aliases can help you a lot. Given you want to have several forms where the user can define a color (e.g. by using bootstrap-colorpicker), you don't want to define the special templates in each form. Instead you can define those fiels as type "color" and use an alias:

    plugin 'FormFieldsFromJSON' => {
      template  => '<%= $label %>: <%= $field %>',
      templates => {
        color => '<%= $label %> (color): <%= $field %>',
      },
      alias => {
        color => 'text',
      },
    };

    The alias defines that "color" fields are "text" fields.

    So with this configuration

    [
       {
           "label" : "Name",
           "type" : "text",
           "name" : "name"
       },
       {
           "label" : "Background",
           "type" : "color",
           "name" : "background"
       }
    ]

    You get

    <label for="name">Name:</label><div><input id="name" name="name" type="text" value="" /></div>
    <label for="background">Background (color):</label><div><input id="background" name="background" type="text" value="" /></div>
  • translate_labels

    If translate_labels is true, the labels for the templates are translated. You have to provide a translation_method|Mojolicious::Plugin::FormFieldsFromJSON/Translation_method, too.

    plugin 'FormFieldsFromJSON' => {
      template           => '<%= $label %>: <%= $field %>',
      translate_labels   => 1,
      translation_method => \&loc,
    };

    For more details see Translation|Mojolicious::Plugin::FormFieldsFromJSON/Translation.

  • translation_method

    If translate_labels is true, the labels for the templates are translated. You have to provide a translation_method|Mojolicious::Plugin::FormFieldsFromJSON/Translation_method, too.

    plugin 'FormFieldsFromJSON' => {
      template           => '<%= $label %>: <%= $field %>',
      translate_labels   => 1,
      translation_method => \&loc,
    };

    For more details see Translation|Mojolicious::Plugin::FormFieldsFromJSON/Translation.

HELPER

form_fields

form_fields returns a string with all configured fields "translated" to HTML.

$controller->form_fields( 'formname' );

Given this configuration:

[
   {
       "label" : "Name",
       "type" : "text",
       "name" : "name"
   },
   {
       "label" : "City",
       "type" : "text",
       "name" : "city"
   }
]

You'll get

<input id="name" name="name" type="text" value="" />
<input id="city" name="city" type="text" value="" />

validate_form_fields

This helper validates the input. It uses the Mojolicious::Validator::Validation and it validates all fields defined in the configuration file.

For more details see Validation.

forms

This method returns a list of forms. That means the filenames of all .json files in the configured directory.

my @forms = $controller->forms;

The filenames are returned without the file suffix .json.

fields

fields() returns a list of fields (label or name).

my @fieldnames = $controller->fields('formname');

If your configuration looks like

[
  {
    "label" : "Email",
    "name"  : "email",
    "type"  : "text"
  },
  {
    "name"  : "password",
    "type"  : "password"
  }
]

You get

(
  Email,
  password
)

FIELD DEFINITIONS

This plugin supports several form fields:

  • text

  • checkbox

  • radio

  • select

  • textarea

  • password

  • hidden

Those fields have the following definition items in common:

  • label

  • type

  • data

  • attributes

    Attributes of the field like "class"

EXAMPLES

The following sections should give you an idea what's possible with this plugin

text

With type text you get a simple text input field.

A simple text field

This is the configuration for a simple text field:

[
   {
       "label" : "Name",
       "type" : "text",
       "name" : "name"
   }
]

And the generated form field looks like

<input id="name" name="name" type="text" value="" />

Set CSS classes

If you want to set a CSS class, you can use the attributes field:

[
   {
       "label" : "Name",
       "type" : "text",
       "name" : "name",
       "attributes" : {
           "class" : "W75px"
       }
   }
]

And the generated form field looks like

<input class="W75px" id="name" name="name" type="text" value="" />

Text field with predefined value

Sometimes, you want to predefine a value shown in the text field. Then you can use the data field:

[
   {
       "label" : "Name",
       "type" : "text",
       "name" : "name",
       "data" : "default value"
   }
]

This will generate this input field:

<input id="name" name="name" type="text" value="default value" />

select

Simple: Value = Label

When you have a list of values for a select field, you can define an array reference:

[
  {
    "type" : "select",
    "name" : "language",
    "data" : [
      "de",
      "en"
    ]
  }
]

This creates the following select field:

<select id="language" name="language">
    <option value="de">de</option>
    <option value="en">en</option>
</select>

Preselect a value

You can define

[
  {
    "type" : "select",
    "name" : "language",
    "data" : [
      "de",
      "en"
    ],
    "selected" : "en"
  }
]

This creates the following select field:

<select id="language" name="language">
    <option value="de">de</option>
    <option value="en" selected="selected">en</option>
</select>

If a key named as the select exists in the stash, those values are preselected (this overrides the value defined in the .json):

$c->stash( language => 'en' );

and

[
  {
    "type" : "select",
    "name" : "language",
    "data" : [
      "de",
      "en"
    ]
  }
]

This creates the following select field:

<select id="language" name="language">
    <option value="de">de</option>
    <option value="en" selected="selected">en</option>
</select>

Multiselect

[
  {
    "type" : "select",
    "name" : "languages",
    "data" : [
      "de",
      "en",
      "cn",
      "jp"
    ],
    "multiple" : 1,
    "size" : 3
  }
]

This creates the following select field:

<select id="languages" name="languages" multiple="multiple" size="3">
    <option value="cn">cn</option>
    <option value="de">de</option>
    <option value="en">en</option>
    <option value="jp">jp</option>
</select>

Preselect multiple values

[
  {
    "type" : "select",
    "name" : "languages",
    "data" : [
      "de",
      "en",
      "cn",
      "jp"
    ],
    "multiple" : 1,
    "selected" : [ "en", "de" ]
  }
]

This creates the following select field:

<select id="language" name="language">
    <option value="cn">cn</option>
    <option value="de" selected="selected">de</option>
    <option value="en" selected="selected">en</option>
    <option value="jp">jp</option>
</select>

Values != Label

[
  {
    "type" : "select",
    "name" : "language",
    "data" : {
      "de" : "German",
      "en" : "English"
    }
  }
]

This creates the following select field:

<select id="language" name="language">
    <option value="en">English</option>
    <option value="de">German</option>
</select>

Option groups

[
  {
    "type" : "select",
    "name" : "language",
    "data" : {
      "EU" : {
        "de" : "German",
        "en" : "English"
      },
      "Asia" : {
        "cn" : "Chinese",
        "jp" : "Japanese"
      }
    }
  }
]

This creates the following select field:

<select id="language" name="language">
    <option value="en">English</option>
    <option value="de">German</option>
</select>

Disable values

[
  {
    "type" : "select",
    "name" : "languages",
    "data" : [
      "de",
      "en",
      "cn",
      "jp"
    ],
    "multiple" : 1,
    "disabled" : [ "en", "de" ]
  }
]

This creates the following select field:

<select id="language" name="language">
    <option value="cn">cn</option>
    <option value="de" disabled="disabled">de</option>
    <option value="en" disabled="disabled">en</option>
    <option value="jp">jp</option>
</select>

radio

For radiobuttons, you can use two ways: You can either configure form fields for each value or you can define a list of values in the data field. With the first way, you can create radiobuttons where the template (if any defined) is applied to each radiobutton. With the second way, the radiobuttons are handled as one single field in the template.

A single radiobutton

Given the configuration

[
   {
       "label" : "Name",
       "type" : "radio",
       "name" : "type",
       "data" : "internal"
   }
]

You get

Two radiobuttons configured seperately

With the configuration

[
   {
       "label" : "Name",
       "type" : "radio",
       "name" : "type",
       "data" : "internal"
   },
   {
       "label" : "Name",
       "type" : "radio",
       "name" : "type",
       "data" : "external"
   }
]

You get

Two radiobuttons as a group

And with

[
   {
       "label" : "Name",
       "type" : "radio",
       "name" : "type",
       "data" : ["internal", "external" ]
   }
]

You get

Two radiobuttons configured seperately - with template

Define template:

plugin 'FormFieldsFromJSON' => {
  dir      => './conf',
  template => '<%= $label %>: <%= $form %>';
};

Config:

[
   {
       "label" : "Name",
       "type" : "radio",
       "name" : "type",
       "data" : "internal"
   },
   {
       "label" : "Name",
       "type" : "radio",
       "name" : "type",
       "data" : "external"
   }
]

Fields:

Name: <input id="type" name="type" type="radio" value="internal" />



Name: <input id="type" name="type" type="radio" value="external" />

Two radiobuttons as a group - with template

Same template definition as above, but given this field config:

[
   {
       "label" : "Name",
       "type" : "radio",
       "name" : "type",
       "data" : ["internal", "external" ]
   }
]

You get this:

Name: <input id="type" name="type" type="radio" value="internal" />
<input id="type" name="type" type="radio" value="external" />

Two radiobuttons - one checked

Config:

[
   {
       "label" : "Name",
       "type" : "radio",
       "name" : "type",
       "data" : ["internal", "external" ],
       "selected" : ["internal"]
   }
]

Field:

<input checked="checked" id="type" name="type" type="radio" value="internal" />
<input id="type" name="type" type="radio" value="external" />

Radiobuttons with HTML after every element

When you want to add some HTML code after every element - e.g. a <br /> - you can use after_element

[
   {
       "label" : "Name",
       "type" : "radio",
       "name" : "type",
       "after_element" : "<br />",
       "data" : ["internal", "external" ]
   }
]

Fields:

<input id="type" name="type" type="radio" value="internal" />
<br /><input id="type" name="type" type="radio" value="external" />
<br />

Radiobuttons with values shown as label

When you want to show the value as a label, you can use show_value.

[
   {
       "label" : "Name",
       "type" : "radio",
       "name" : "type",
       "show_value" : 1,
       "data" : ["internal", "external" ]
   }
]

Creates

<input id="type" name="type" type="radio" value="internal" /> internal
<input id="type" name="type" type="radio" value="external" /> external

checkbox

For checkboxes, you can use two ways: You can either configure form fields for each value or you can define a list of values in the data field. With the first way, you can create checkboxes where the template (if any defined) is applied to each checkbox. With the second way, the checkboxes are handled as one single field in the template.

A single checkbox

Given the configuration

[
   {
       "label" : "Name",
       "type" : "checkbox",
       "name" : "type",
       "data" : "internal"
   }
]

You get

Two checkboxes configured seperately

With the configuration

[
   {
       "label" : "Name",
       "type" : "checkbox",
       "name" : "type",
       "data" : "internal"
   },
   {
       "label" : "Name",
       "type" : "checkbox",
       "name" : "type",
       "data" : "external"
   }
]

You get

Two checkboxes as a group

And with

[
   {
       "label" : "Name",
       "type" : "checkbox",
       "name" : "type",
       "data" : ["internal", "external" ]
   }
]

You get

Two checkboxes configured seperately - with template

Define template:

plugin 'FormFieldsFromJSON' => {
  dir      => './conf',
  template => '<%= $label %>: <%= $form %>';
};

Config:

[
   {
       "label" : "Name",
       "type" : "checkbox",
       "name" : "type",
       "data" : "internal"
   },
   {
       "label" : "Name",
       "type" : "checkbox",
       "name" : "type",
       "data" : "external"
   }
]

Fields:

Name: <input id="type" name="type" type="checkbox" value="internal" />



Name: <input id="type" name="type" type="checkbox" value="external" />

Two checkboxes as a group - with template

Same template definition as above, but given this field config:

[
   {
       "label" : "Name",
       "type" : "checkbox",
       "name" : "type",
       "data" : ["internal", "external" ]
   }
]

You get this:

Name: <input id="type" name="type" type="checkbox" value="internal" />
<input id="type" name="type" type="checkbox" value="external" />

Two checkboxes - one checked

Config:

[
   {
       "label" : "Name",
       "type" : "checkbox",
       "name" : "type",
       "data" : ["internal", "external" ],
       "selected" : ["internal"]
   }
]

Field:

<input checked="checked" id="type" name="type" type="checkbox" value="internal" />
<input id="type" name="type" type="checkbox" value="external" />

Checkboxes with HTML after every element

When you want to add some HTML code after every element - e.g. a <br /> - you can use after_element

[
   {
       "label" : "Name",
       "type" : "checkbox",
       "name" : "type",
       "after_element" : "<br />",
       "data" : ["internal", "external", "unknown" ]
   }
]

Fields:

<input id="type" name="type" type="checkbox" value="internal" />
<br /><input id="type" name="type" type="checkbox" value="external" />
<br /><input id="type" name="type" type="checkbox" value="unknown" />
<br />

Checkboxes with values shown as label

When you want to show the value as a label, you can use show_value.

[
   {
       "label" : "Name",
       "type" : "checkbox",
       "name" : "type",
       "show_value" : 1,
       "data" : ["internal", "external" ]
   }
]

Creates

<input id="type" name="type" type="checkbox" value="internal" /> internal
<input id="type" name="type" type="checkbox" value="external" /> external

textarea

This type is very similar to text.

A simple textarea

This is the configuration for a simple text field:

[
   {
       "type" : "textarea",
       "name" : "message",
       "data" : "Current message"
   }
]

And the generated form field looks like

<textarea id="message" name="message">Current message</textarea>

A textarea with defined number of columns and rows

This is the configuration for a simple text field:

[
   {
       "type" : "textarea",
       "name" : "message",
       "data" : "Current message",
       "attributes" : {
           "cols" : 80,
           "rows" : 10
       }
   }
]

And the generated textarea looks like

<textarea cols="80" id="message" name="message" rows="10">Current message</textarea>

password

This type is very similar to text. You can use the very same settings as for text fields, so we show only a simple example here:

A simple password field

This is the configuration for a simple text field:

[
   {
       "type" : "password",
       "name" : "user_password"
   }
]

And the generated form field looks like

<input id="user_password" name="password" type="password" value="" />

Templates

Especially when you work with frameworks like Bootstrap, you want to your form fields to look nice. For that the form fields are within divs or other HTML elements.

To make your life easier, you can define templates. Either a "global" one, a type specific template or a template for one field.

For hidden fields, no template is applied!

A global template

When you load the plugin this way

$self->plugin( 'FormFieldsFromJSON' => {
  template => '<label for="<%= $id %>"><%= $label %>:</label><div><%= $field %></div>',
});

and have a configuration that looks like

You get

<label for="name">Name:</label><div><input id="name" name="name" type="text" value="" /></div>

 
<label for="password">Password:</label><div><input id="password" name="password" type="text" value="" /></div>

A type specific template

When you want to use a different template for select fields, you can use a different template for that kind of fields:

plugin 'FormFieldsFromJSON' => {
  dir       => File::Spec->catdir( dirname( __FILE__ ) || '.', 'conf' ),
  template  => '<label for="<%= $id %>"><%= $label %>:</label><div><%= $field %></div>',
  templates => {
    select => '<%= $label %>: <%= $field %>',
  },
};

With a configuration file like

[
   {
       "label" : "Name",
       "type" : "text",
       "name" : "name"
   }
   {
       "label" : "Country",
       "type" : "select",
       "name" : "country",
       "data" : [ "au" ]
   }
]

You get

<label for="name">Name:</label><div><input id="name" name="name" type="text" value="" /></div>

 
Country: <select id="country" name="country"><option value="au">au</option></select>

A field specific template

When you want to use a different template for a specific field, you can use the template field in the configuration file.

plugin 'FormFieldsFromJSON' => {
  dir       => File::Spec->catdir( dirname( __FILE__ ) || '.', 'conf' ),
  template  => '<label for="<%= $id %>"><%= $label %>:</label><div><%= $field %></div>',
};

With a configuration file like

[
   {
       "label" : "Name",
       "type" : "text",
       "name" : "name"
   }
   {
       "label" : "Country",
       "type" : "select",
       "name" : "country",
       "data" : [ "au" ],
       "template" : "<%= $label %>: <%= $field %>"
   }
]

You get

<label for="name">Name:</label><div><input id="name" name="name" type="text" value="" /></div>

 
Country: <select id="country" name="country"><option value="au">au</option></select>

Template variables

You get three template variables for free:

  • $label

    If a label is defined in the field configuration

  • $field

    The form field (HTML)

  • $id

    The id for the field. If no id is defined, the name of the field is set.

Validation

You can define some validation rules in your config file. And when you call validate_form_fields, the fields defined in the configuration file are validated.

Mojolicious::Validator::Validation is shipped with some basic validation checks:

  • in

  • size

  • like

  • equal_to

There is Mojolicious::Plugin::AdditionalValidationChecks with some more basic checks. And you can also define your own checks.

The validation field is a hashref where the name of the check is the key and the parameters for the check can be defined in the value:

"validation" : {
    "size" : [ 2, 5 ]
},

This will call ->size(2,5). If you want to pass a single parameter, you can set a scalar:

"validation" : {
    "equal_to" : "foo"
},

Validation checks are done in asciibetical order.

Check a string for its length

This is a simple check for the length of a string

[
   {
       "label" : "Name",
       "type" : "text",
       "validation" : {
           "size" : [ 2, 5 ]
       },
       "name" : "name"
   }
]

Then you can call validate_form_fields:

my %errors = $c->validate_form_fields( $config_name );

In the returned hash, you get the fieldnames as keys where a validation check fails.

A mandatory string

If you have mandatory fields, you can define them as required

[
   {
       "label" : "Name",
       "type" : "text",
       "validation" : {
           "required" : "name"
       },
       "name" : "name"
   }
]

Provide your own error message

With the simple configuration seen above, the %error hash contains the value "1" for each invalid field. If you want to get a better error message, you can define a hash in the validation config

[
   {
       "label" : "Name",
       "type" : "text",
       "validation" : {
           "like" : { "args" : [ "es" ], "msg" : "text must contain 'es'" },
           "size" : { "args" : [ 2, 5 ], "msg" : "length must be between 2 and 5 chars" }
       },
       "name" : "name"
   }
]

Examples:

text   | error
-------+---------------------------------
test   |
t      | text must contain 'es'
tester | length must be between 2 and 5 chars

Translation

Most webapplications nowadays are internationalized, therefor this module provides some support for translations.

If translate_labels is set to a true value, a template is used and translation_method is given, the labels are translated.

translation_method

translation_method has to be a reference to a subroutine.

An example for translation

Load and configure the plugin:

plugin 'FormFieldsFromJSON' => {
  dir                => File::Spec->catdir( dirname( __FILE__ ) || '.', 'conf' ),
  template           => '<label for="<%= $id %>"><%= $label %>:</label><div><%= $field %></div>',
  translate_labels   => 1,
  translation_method => \&loc,
};

The translation method gets two parameters:

  • the controller object

  • the label

sub loc {
    my ($c, $value) = @_;

    my %translation = ( Address => 'Adresse' );
    return $translation{$value} // $value;
};

This can be a more complex subroutine that makes use of any translation framework.

Given this field configuration file:

[
   {
       "label" : "Address",
       "type" : "text",
       "name" : "name"
   }
]

You'll get

<label for="name">Adresse:</label><div><input id="name" name="name" type="text" value="" /></div>

Internationalization

There is more about internationalization (i18n) than just translation. There are dates, ranges, order of characters etc. But that can't be covered within this single module. There are more Mojolicious plugins that provide more features about i18n:

You can combine these plugins with this plugin.

SEE ALSO

Mojolicious, Mojolicious::Guides, http://mojolicio.us.

AUTHOR

Renee Baecker <reneeb@cpan.org>

COPYRIGHT AND LICENSE

This software is Copyright (c) 2014 by Renee Baecker.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)