Frontend

Latte Templates for Forms #

How to handle forms on your frontend? Well, this is a chapter which can be described with a whole new book. There is many and different solutions. Many of these are already deprecated. Do you remember form renderers?

This book will describe solution based on blocks. It is simple and modular solution with definition of complex form inputs defined in one place.

You will need one file to define your basic blocks which you will use inside your latte templates.

Start with app/Presenters/templates/components/forms.latte with a basic blocks:

{import 'input-inner-checkbox.latte'}
{import 'input-inner-checkboxlist.latte'}
{import 'input-inner-radiolist.latte'}
{import 'input-inner-default.latte'}

{define form-errors App\Forms\UI\Form $form}
	<div n:if="$form->ownErrors" class="alerts">
		{foreach $form->ownErrors as $error}
			{include flash-message 'danger', $error, dismissible => true}
		{/foreach}
	</div>
{/define}


{define input-label Nette\Forms\Controls\BaseControl $input, $caption, $class}
	{if !$input instanceof Nette\Forms\Controls\Checkbox && $caption !== false}
		<label n:name="$input" n:class="form-label, $input->isRequired() ? 'required', $class">
			<span>{$input|caption:$caption|translate}</span>
		</label>
		{php [$desc, $args] = $input->getOption('description', [null, []])}
		<div n:if="$desc" class="small text-muted">{_$desc, $args}</div>
	{/if}
{/define}


{define input-inner Nette\Forms\Controls\BaseControl $input, $caption, $placeholder, $append, $class}
	{if $input instanceof Nette\Forms\Controls\Checkbox}
		{include input-inner-checkbox $input, caption => $caption}
	{elseif $input instanceof Nette\Forms\Controls\CheckboxList}
		{include input-inner-checkboxlist $input}
	{elseif $input instanceof Nette\Forms\Controls\RadioList}
		{include input-inner-radiolist $input}
	{else}
		{include input-inner-default $input, placeholder => $placeholder, append => $append, class => $class}
	{/if}
{/define}


{define input Nette\Forms\Controls\BaseControl $input, $caption, $placeholder, $append, $id, $class, $groupClass}
    <div n:class="form-group, $groupClass ?? mb-3, $input->hasErrors() ? 'is-invalid'" n:attr="id => $id">
        {include input-label $input, caption => $caption}
        {include input-inner $input, caption => $caption, placeholder => $placeholder, append => $append, class => $class}
        <div class="invalid-feedback">{inputError $input}</div>
    </div>
{/define}

{define input-optional $input}
	{if $input}
		{include input $input}
	{/if}
{/define}

{define save-buttons-inner App\Forms\UI\Form $form, $class}
	{var $saveButtons = $form->getComponents(false, Nette\Forms\Controls\SubmitButton::class)}
	{foreach $saveButtons as $button}
		{var $classes = ($button|inputClass)}
		{php $classes[] = 'btn btn-primary'}
		{php $classes[] = $class}
		{input $button, class => $classes, value => ($button|caption|translate)}
	{/foreach}
{/define}

{define save-buttons App\Forms\UI\Form $form}
	<div class="row mb-4 init-clone-buttons">
		<div class="col-100 text-right">
			{include save-buttons-inner $form}
		</div>
	</div>
{/define}

Which will provide basic blocks to use in your forms:

  • {include form-errors $form} to display your form errors,
  • {include input $form['inputName']} to display input named as inputName,
  • {include input-optional $form['inputName']} to display input named as inputName,
  • {include save-buttons $form} to display submit buttons,

and additional blocks for manual drawing your form elements:

  • {include input-label $form['inputName']} to display label,
  • {include input-inner $form['inputName']} to display input,
  • {include save-buttons-inner $form} to display submit buttons placed within any html element.

Default Input #

Checkbox Input #

Checkbox List #

Radio List #