Create A Form
Create a new form instance and add fields into it.
use Windwalker\Form\Field\TextareaField;
use Windwalker\Form\Form;
use Windwalker\Form\Field\TextField;
use Windwalker\Form\Field\PasswordField;
$form = new Form();
$form->addField(new TextField('username', 'Username'));
$form->addField(new PasswordField('password', 'Password'));
$form->addField(new TextField('email', 'Email'));
$form->addField(new TextareaField('description', 'Description'));
echo $form->renderFields();
Render all fields, and we get this HTML output.
<div id="username-control" class="text-field ">
<label id="username-label" for="username">Username</label>
<input type="text" name="username" id="username" />
</div>
<div id="password-control" class="password-field ">
<label id="password-label" for="password">Password</label>
<input type="password" name="password" id="password" />
</div>
<div id="email-control" class="text-field ">
<label id="email-label" for="email">Email</label>
<input type="text" name="email" id="email" />
</div>
<div id="description-control" class="textarea-field ">
<label id="description-label" for="description">Description</label>
<textarea name="description" id="description"></textarea>
</div>
Use add()
add()
is a simple alias of addField()
to make Field support chaining.
$form->add('username', new TextField())
->label('Username')
->setClass('input-large')
->defauleValue('foo')
->set('placeholder', 'Enter username')
->required();
Using XML as Configuration
<form>
<field
name="username"
type="text"
label="Username"
/>
<field
name="password"
type="password"
label="Password"
/>
</form>
$form = new Form();
$form->loadFile('form.xml');
Form Control
$control = 'user';
$form = new Form($control);
$form->addField(new TextField('username', 'Username'));
echo $form->renderFields();
The result will make name as an array.
<div id="user-username-control" class="text-field ">
<label id="user-username-label" for="user-username">Username</label>
<input type="text" name="user[username]" id="user-username" />
</div>
Organize Fields
Fieldset
Fieldset is a category of fields, we can filter our fields by fieldset:
$form = new Form();
$form->addField(new TextField('flower', 'Flower'), 'plant');
$form->addField(new TextField('tree', 'Tree'), 'plant');
$form->addField(new TextField('dog', 'Dog'), 'animal');
$form->addField(new TextField('cat', 'Cat'), 'animal');
// Only render Flower & Tree
echo $form->renderFields('plant');
// Only render Dog & Cat
echo $form->renderFields('animal');
// Will render all
echo $form->renderFields();
Use callback to wrap fieldset.
$form->fieldset('plant', function (Form $form)
{
$form->addField(new TextField('flower', 'Flower'));
$form->addField(new TextField('tree', 'Tree'));
});
$form->fieldset('animal', function (Form $form)
{
$form->addField(new TextField('flower', 'Flower'));
$form->addField(new TextField('tree', 'Tree'));
});
Using XML
<form>
<fieldset name="plant">
<field name="flower" label="Flower"/>
<field name="tree" label="Tree"/>
</fieldset>
<fieldset name="animal">
<field name="dog" label="Dog"/>
<field name="cat" label="Cat"/>
</fieldset>
</form>
Group
Group is like fieldset as a category of fields, but it will make name of fields to be array:
$form = new Form();
$form->addField(new TextField('flower', 'Flower'), null, 'earth');
$form->addField(new TextField('bird', 'Bird'), null, 'sky');
// The name will be name="earth[flower]"
echo $form->renderFields(null, 'plant');
// The name will be name="sky[dog]"
echo $form->renderFields(null, 'animal');
Now we can use fieldset and group to organize our fields.
$form = new Form();
$form->addField(new TextField('flower', 'Flower'), 'plant', 'earth');
$form->addField(new TextField('tree', 'Tree'), 'plant', 'earth');
$form->addField(new TextField('dog', 'Dog'), 'animal', 'home');
$form->addField(new TextField('cat', 'Cat'), 'animal', 'home');
$form->addField(new TextField('bird', 'Bird'), 'animal', 'sky');
// Only render Bird
echo $form->renderFields('animal', 'sky');
// Only render Dog & Cat & Bird
echo $form->renderFields('animal');
// Only render Flower & Tree
echo $form->renderFields(null, 'earth');
// Will render all
echo $form->renderFields();
Use callback to wrap group:
$form->group('earth', function (Form $form)
{
$form->addField(new TextField('flower', 'Flower'));
$form->addField(new TextField('tree', 'Tree'));
});
Wrap with fieldset and group:
$form->wrap('plant', 'earth', function (Form $form)
{
$form->addField(new TextField('flower', 'Flower'));
$form->addField(new TextField('tree', 'Tree'));
});
$form->wrap('animal', 'sky', function (Form $form)
{
$form->addField(new TextField('bird', 'Bird'));
});
Using XML
<form>
<group name="earth">
<fieldset name="plant">
<field name="flower" label="Flower"/>
<field name="tree" label="Tree"/>
</fieldset>
</group>
<fieldset name="animal">
<group name="home">
<field name="dog" label="Dog"/>
<field name="cat" label="Cat"/>
</group>
<group name="home">
<field name="bird" label="Bird"/>
</group>
</fieldset>
</form>
Control and group supports multi-level by /
or .
separator:
$form = new Form('foo/bar');
$form->group('egg/bread', ...);
Attributes of Fields
Name & Label
$form->addField(new TextField('name', 'Label'));
Set Attributes
You can set some attributes to field by set()
or setAttribute()
, this method will only allow standard
HTML attributes inject to latest rendered result.
$form->addField(new TextField('name'))
->label('Label')
->set('id', 'my-name')
->set('class', 'col-md-8 form-input')
->set('onclick', 'return false;')
->set('labelClass', 'form-label') // <-- This will add to <label class="...">
->set('data-my-attr', 'foo'); // <-- This custom attribute will not work
If you want to set custom data attributes to make your inputs work with some JS frameworks (e.g. Vue, Angular, Bootstrap),
you can use attr()
to directly set HTML attributes.
$form->addField(new TextField('name'))
->label('Label')
->set('class', 'col-md-8 form-input')
->attr('v-on:click', 'foo()')
->attr(':value', 'bar')
->attr('data-toggle', 'tooltip');
These custom directives will directly add to HTML. attr()
method is like jQuery attr()
, if you don't pass
second param, this method will be a getter to return attribute value.
You can also set attribute to input wrapper and label, use controlAttr()
and labelAttr()
.
Required, Disabled and Readonly
$form->addField(new TextField('name', 'Label'))
->set('id', 'my-name')
->required()
->disabled();
Set to false.
$form->addField(new TextField('name', 'Label'))
->set('id', 'my-name')
->required(false)
->disabled(false);
Wrap by Elements
Use wrap()
to wrap field by HTML elements to support Web Components or Vue.js pattern.
$form->addField(new TextField('name', 'Label'))
->wrap(new HtmlElement('transition', null, ['name' => 'fade']))
->wrap(new HtmlElement('dom-repeat', null, ['items' => 'sakuras']));
Result:
<transition name="fade">
<dom-repeat items="sakuras">
{Your field}
</dom-repeat>
</transition>
Filter
use Windwalker\Filter\InputFilter;
$form->addField(new TextField('id', 'ID'))
->addFilter(InputFilter::INTEGER);
// Prepare data
$data['id'] = '123abc';
// Bind data into form
$form->bind($data);
// Do filter
$form->filter();
$values = $form->getValues(); // Array(id = 123)
Validate
$form->addField(new TextField('name', 'Name'))
->required();
$form->addField(new TextField('email', 'Email'))
->addValidator(new EmailValidator());
// Prepare data
$data['name'] = null;
$data['email'] = 'foo';
// Bind data into form
$form->bind($data);
// Do validate
$r = $form->validate();
$results = $form->getErrors();
var_dump($r); // bool(false)
$results[0]->getMessage(); // Field Email validate fail.
$results[1]->getMessage(); // Field Name value not allow empty.
Field Types
Select List
use Windwalker\Form\Field\ListField;
use Windwalker\Html\Option;
$selectField = new ListField(
'flower',
'Flower',
array(
new Option('', ''),
new Option('Yes', 1),
new Option('No', 0),
)
);
echo $selectField->render();
The output is:
<div id="flower-control" class="list-field ">
<label id="flower-label" for="flower">Flower</label>
<select name="flower" id="flower" class="stub-flower">
<option selected="selected"></option>
<option value="1">Yes</option>
<option value="0">No</option>
</select>
</div>
Multiple List
$selectField->set('multiple', true);
RadioList
$form->addField(new RadioList('flower', 'Flower'))
->addOption(new Option('Yes', 1))
->addOption(new Option('No', 0));
We can also add options in constructor:
$field = new RadioList(
'flower',
'Flower',
array(
new Option('Yes', 1),
new Option('No', 0),
)
);
Available Fields
Name | HTML | Description |
---|---|---|
TextField | <input type="text"> |
The text input field. |
TextareaField | <textarea></textarea> |
Textarea field. |
ColorField | <input type="color"> |
HTML5 Color picker |
DateField | <input type="date"> |
Date selector |
DatetimeLocalField | <input type="datetime-local"> |
Datetime selector |
MonthField | <input type="month"> |
Month selector |
NumberField | <input type="number"> |
Number text field |
RangeField | <input type="range"> |
A range bar. |
TelField | <input type="tel"> |
Tel text field. |
TimeField | <input type="time"> |
Time field. |
UrlField | <input type="url"> |
URL text field |
WeekField | <input type="week"> |
Week selector |
EmailField | <input type="email"> |
The email text field. |
HiddenField | <input type="hidden"> |
Hidden input. |
PasswordField | <input type="password"> |
Password field. |
SpacerField | <hr> |
The spacer to separate fields and fields. |
ListField | <select> |
Select list. |
CheckboxField | <input type="checkbox"> |
Single checkbox. |
CheckboxesField | <input type="checkbox"> |
Checkbox list. |
RadioField | <input type="radio"> |
Radio list. |
TimezoneField | <select> |
A timezone select list. |
ButtonField | <button> |
Create a custom button. |
CustomHtmlField | ... |
Create any HTML you want with set('content', ...) . |
See every fields' prepare()
method you will know available attributes.
Custom Fields
Custom TextField.
namespace MyCode\Fields;
class FooField extends TextField
{
protected $type = 'foo';
public function prepare(&$attrs)
{
parent::prepare($attrs);
$attrs['bar'] = $this->getAttribute('bar');
}
}
For XML
\Windwalker\Form\FieldHelper::addNamespace('MyCode\Filter');
<field
name="foo"
type="foo"
/>
Custom List field
namespace MyCode\Fields;
class UsersField extends ListField
{
protected function prepareOptions()
{
$users = Database::getList('SELECT * FROM users');
$options = array();
foreach ($users as $user) {
$options[] = new Option($user->name, $user->id);
}
return $options;
}
}
Custom Filter
namespace MyCode\Filter;
class MyFilter implements FilterInterface
{
public function clean($text)
{
return my_filter_function($text);
}
}
(new TextField('foo', 'Foo'))
->addFilter(new MyFilter());
For XML
\Windwalker\Form\FilterHelper::addNamespace('MyCode\Filter');
<field
name="foo"
type="text"
filter="my"
/>
Custom Validator
namespace MyCode\Validator;
use Windwalker\Validator\AbstractValidator;
class MyValidator extends AbstractValidator
{
protected function test($value)
{
return (bool) $value;
}
}
(new TextField('foo', 'Foo'))
->addFilter(new MyValidator());
For XML
\Windwalker\Form\ValidatorHelper::addNamespace('MyCode\Validator');
<field
name="foo"
type="text"
filter="my"
/>
Form Renderer
If you wish to override core HTML output, use FormRendererInterface
to render your fields.
class MyFormRenderer implements \Windwalker\Form\Renderer\FormRendererInterface
{
public function renderField(AbstractField $field, array $attribs = array())
{
$attribs['class'] .= ' my-custom-style';
return new \Windwalker\Dom\HtmlElement('div', array(
$field->renderLabel(), // Will load $this->renderLabel()
$field->renderInput(), // Will load $this->renderInput()
), $attribs);
}
public function renderLabel(AbstractField $field, array $attribs = array())
{
return ...; // Render different field types
}
public function renderInput(AbstractField $field, array $attribs = array())
{
return ...; // Render different field types
}
}
$form->setRenderer(new MyFormRenderer());
If you found a typo or error, please help us improve this document.