flsouto/htwidget

There is no license information available for the latest version (1.0.2) of this package.

Abstract class for defining form widget types

1.0.2 2017-03-31 02:12 UTC

This package is not auto-updated.

Last update: 2024-04-22 13:14:44 UTC


README

Overview

This class can be used to define different types of widgets. But what is a widget? A widget is an interactive field/element which has some sort of state and allows users to communicate with a server or a backend. Notice that not all form fields are interactive. A hidden field, for instance, even though it has a state, it does not provide any form of direct interaction. A button is also not a widget, in my opinion, because it doesn't allow any complex interaction other than just clicking on it. An input text field or a checkbox, on the other hand, can be considered to be a widget because they do have a state and require some kind of interaction. In this documentation we are going to see how to implement a simple TextField class which inherits from HtWidget.

Notice: A lot of functionality is inherited from a more basic abstract class called HtField. If you find difficulties in understanding some of the features being reused here, please refer to this documentation.

Installation

Run composer:

composer require flsouto/htwidget

Usage

Below we are going to implement a simple TextField class. Pay attention to it because we are going to use it through out this document in order to learn about all the functionality inherited from HtWidget:

<?php

class TextField extends HtWidget{

	function __construct($name){
		parent::__construct($name);
		// By default all HtWidget instances have a default, random id.
		// We want to change that so the id is always the name of the field itself.
		$this->attrs['id'] = $name;
	}

	// All concrete HtWidget implementations must define a renderWritable method
	function renderWritable(){
		$attrs = $this->attrs;
		$attrs['value'] = $this->value();
		echo '<input '.$attrs.' />';
	}

	// All concrete HtWidget implementations must define a renderReadonly method
	function renderReadonly(){
		$attrs = $this->attrs;
		echo "<span ".$attrs.">".$this->value()."</span>";
	}

}

In the next example we instantiate the newly defined class and render it.

<?php
require_once('vendor/autoload.php');

$field = new TextField('email');
$field->context(['email'=>'someuser@domain.com']);

echo $field;

Notice that by default the widget is rendered in "writable mode":

<div class="widget email" style="display:block">
 <input id="email" name="email" value="someuser@domain.com" />
 <div style="color:yellow;background:red" class="error">
 </div>
</div>

Switch to readonly mode

To render the readonly version of your widget, simply call the readonly setter with a positive argument:

require_once('vendor/autoload.php');

$field = new TextField('email');
$field->context(['email'=>'someuser@domain.com'])
	->readonly(true);

echo $field;

Output:

<div class="widget email" style="display:block">
  <span>someuser@domain.com</span>
 <div style="color:yellow;background:red" class="error">
 </div>
</div>

Understanding how it works

All those extra tags surrounding the main element are produced by default. When we echo $field the HtWidget::render() method gets called which produces a wrapper containing the output of HtWidget::renderInner(), which in turn decides which mode we are in and calls the respective renderReadonly or renderWritable method, along with error messages. So, if you wanted to display only the inner content, without the wrapper, you would have to call the renderInner() method:

$field->renderInner()

Show the widget inline

By default the widget is rendered in block mode, which means it occupies the entire line. If you want it to appear in the same line as whatever was printed before, use the inline setter:

$field = new TextField('username');
$field->inline(true);

echo $field;

Output:

<div class="widget username" style="display:inline-block;vertical-align:text-top">
 <input id="username" name="username" value="" />
 <div style="color:yellow;background:red" class="error">
 </div>
</div>

Labels

By default the widget is rendered without an associated label. You have to specify one if you want it to be displayed:

$field = new TextField('product_name');
$field->label('Product Name');

echo $field;

Output:

<div class="widget product_name" style="display:block">
 <label style="display:block" for="product_name">Product Name</label>
 <input id="product_name" name="product_name" value="" />
 <div style="color:yellow;background:red" class="error">
 </div>
</div>

You can change the label's tag attributes by passing an associative array. In that case you have to use the special text attribute in order to set the label's text:

$field = new TextField('product_name');
$field->label(['text'=>'Product Name', 'class'=>'some_class']);

echo $field;

Output:

<div class="widget product_name" style="display:block">
 <label style="display:block" class="some_class" for="product_name">Product Name</label>
 <input name="product_name" value="" />
 <div style="color:yellow;background:red" class="error">
 </div>
</div>

By default, the label is rendered above the widget. If you want it to be displayed in the same line you can use the special inline attribute:

$field = new TextField('name');
$field->label(['text'=>'Name','inline'=>true]);

echo $field;

Output:

<div class="widget name" style="display:block">
 <label style="display:inline-block;margin-right:10px" for="name">Name</label>
 <input id="name" name="name" value="" />
 <div style="color:yellow;background:red" class="error">
 </div>
</div>

Set the field as required

Call the required method passing the error message to be shown in case the field is left blank:

$field = new TextField('name');
$field
	->required('Name is required!')
	->context(['name'=>'']);

echo $field->validate();

Output:

Name is required!

Activate error messsages

By default, error messages are not displayed along the field if any validation error occurs internally. You can change that by calling the error method with a positive value:

$field = new TextField('name');
$field
	->required('Name is required!')
	->context(['name'=>''])
	->error(true);

echo $field;

Output:

<div class="widget name" style="display:block">
 <input name="name" value="" />
 <div style="color:yellow;background:red" class="error">
    Name is required!
 </div>
</div>

You can customize the error message tag by passing an array of attributes to the error function.

$field = new TextField('name');
$field->required('Name is required!')
	->context(['name'=>''])
	->error(['display'=>true,'class'=>'errmsg','style'=>['padding'=>'5px']]);

echo $field;

Output:

<div class="widget name" style="display:block">
 <input name="name" value="" />
 <div style="color:yellow;background:red;padding:5px" class="errmsg">
    Name is required!
 </div>
</div>

Notice that in this case we enable the error display by setting the 'display' attribute.

Disable error displaying by passing a negative (i.e. false) argument:

$field = new TextField('name');
$field->required('Name is required!')
	->context(['name'=>''])
	->error(['class'=>'errmsg','style'=>['padding'=>'5px']])
	->error(false);

echo $field;

Output:

<div class="widget name" style="display:block">
 <input name="name" value="" />
 <div style="color:yellow;background:red;padding:5px" class="errmsg">
 </div>
</div>

Notice: you could instead pass the 'display' attribute set to false

Make error tag display in the same line using inline option:

$field = new TextField('name');
$field->required('Name is required!')
	->context(['name'=>''])
	->error(['display'=>true,'inline'=>true]);

echo $field;

Output:

<div class="widget name" style="display:block">
 <input name="name" value="" />
 <div style="color:yellow;background:red;display:inline-block;margin-right:10px" class="error">
    Name is required!
 </div>
</div>

Specify a default value

You can setup a default value to be used in case the field is left blank and/or a validation error occurs:

$field = new TextField('amount');
$field->fallback(1);

echo $field;

Output:

<div class="widget amount" style="display:block">
 <input id="amount" name="amount" value="1" />
 <div style="color:yellow;background:red" class="error">
 </div>
</div>