使用 material 设计的 Nette 形式
Use Nette form with material design
我最近购买了 angular 主题 admin.io (https://themeforest.net/item/adminio-responsive-material-design-admin/10761963),我想将其与 PHP Nette 框架一起使用。通过手动渲染,文本输入和按钮一切顺利。但是我被 select 元素困住了。
它应该是这样的:
<md-select ng-model="someVal" name="priority" placeholder="Zvolte prioritu">
<md-option value="1">Urgent</md-option>
<md-option value="2">Vysoká</md-option>
<md-option value="3">Střední</md-option>
<md-option value="4">Nízká</md-option>
</md-select>
但 Nette 将其呈现为基本 <select>
。我可以用 <md-select n:name="">
渲染 select,但是渲染选项有问题。有没有什么方法可以像我处理 select 和整个表单那样手动呈现 <option>
,或者我可以为该表单使用我自己的模板,或者有人知道,如何使它工作?谢谢!
这会有点困难。 Nette SelectBox 不是为此而构建的。您需要构建自己的控件。您可以使用 Nette\Forms\Controls\SelectBox
作为模板并进行一些调整:
<?php
namespace App\Forms;
use Nette;
/**
* Select box control that allows single item selection.
*/
class MdSelectBox extends Nette\Forms\Controls\ChoiceControl
{
/** validation rule */
const VALID = ':selectBoxValid';
/** @var array of option / optgroup */
private $options = [];
/** @var mixed */
private $prompt = false;
/** @var array */
private $optionAttributes = [];
public function __construct($label = null, array $items = null)
{
parent::__construct($label, $items);
$this->setOption('type', 'md-select');
$this->addCondition(Nette\Forms\Form::BLANK)
->addRule([$this, 'isOk'], Nette\Forms\Validator::$messages[self::VALID]);
}
/**
* Sets first prompt item in select box.
* @param string|object
* @return static
*/
public function setPrompt($prompt)
{
$this->prompt = $prompt;
return $this;
}
/**
* Returns first prompt item?
* @return mixed
*/
public function getPrompt()
{
return $this->prompt;
}
/**
* Sets options and option groups from which to choose.
* @return static
*/
public function setItems(array $items, $useKeys = true)
{
if (!$useKeys) {
$res = [];
foreach ($items as $key => $value) {
unset($items[$key]);
if (is_array($value)) {
foreach ($value as $val) {
$res[$key][(string) $val] = $val;
}
} else {
$res[(string) $value] = $value;
}
}
$items = $res;
}
$this->options = $items;
return parent::setItems(Nette\Utils\Arrays::flatten($items, true));
}
/**
* Generates control's HTML element.
* @return Nette\Utils\Html
*/
public function getControl()
{
$items = $this->prompt === false ? [] : ['' => $this->translate($this->prompt)];
foreach ($this->options as $key => $value) {
$items[is_array($value) ? $this->translate($key) : $key] = $this->translate($value);
}
return Helpers::createSelectBox(
$items,
[
'disabled:' => is_array($this->disabled) ? $this->disabled : null,
] + $this->optionAttributes,
$this->value
)->addAttributes(parent::getControl()->attrs);
}
/**
* @return static
*/
public function addOptionAttributes(array $attributes)
{
$this->optionAttributes = $attributes + $this->optionAttributes;
return $this;
}
/**
* @return bool
*/
public function isOk()
{
return $this->isDisabled()
|| $this->prompt !== false
|| $this->getValue() !== null
|| !$this->options
|| $this->control->size > 1;
}
}
您还需要重写 Nette\Forms\Helpers
class,这是您无法使用 Nette\Forms\Controls\SelectBox
:
实现的主要原因
<?php
namespace App\Forms;
use Nette;
use Nette\Utils\Html;
/**
* Forms helpers.
*/
class Helpers
{
use Nette\StaticClass;
/**
* @return Html
*/
public static function createSelectBox(array $items, array $optionAttrs = null, $selected = null)
{
if ($selected !== null) {
$optionAttrs['selected?'] = $selected;
}
list($optionAttrs, $optionTag) = self::prepareAttrs($optionAttrs, 'md-option');
$option = Html::el();
$res = $tmp = '';
foreach ($items as $group => $subitems) {
if (is_array($subitems)) {
$res .= Html::el('md-optgroup')->label($group)->startTag();
$tmp = '</md-optgroup>';
} else {
$subitems = [$group => $subitems];
}
foreach ($subitems as $value => $caption) {
$option->value = $value;
foreach ($optionAttrs as $k => $v) {
$option->attrs[$k] = isset($v[$value]) ? $v[$value] : null;
}
if ($caption instanceof Html) {
$caption = clone $caption;
$res .= $caption->setName('md-option')->addAttributes($option->attrs);
} else {
$res .= $optionTag . $option->attributes() . '>'
. htmlspecialchars((string) $caption, ENT_NOQUOTES, 'UTF-8')
. '</md-option>';
}
if ($selected === $value) {
unset($optionAttrs['selected'], $option->attrs['selected']);
}
}
$res .= $tmp;
$tmp = '';
}
return Html::el('md-select')->setHtml($res);
}
private static function prepareAttrs($attrs, $name)
{
$dynamic = [];
foreach ((array) $attrs as $k => $v) {
$p = str_split($k, strlen($k) - 1);
if ($p[1] === '?' || $p[1] === ':') {
unset($attrs[$k], $attrs[$p[0]]);
if ($p[1] === '?') {
$dynamic[$p[0]] = array_fill_keys((array) $v, true);
} elseif (is_array($v) && $v) {
$dynamic[$p[0]] = $v;
} else {
$attrs[$p[0]] = $v;
}
}
}
return [$dynamic, '<' . $name . Html::el(null, $attrs)->attributes()];
}
}
然后你可以像这样在你的表单中使用你新建的 MdSelectBox
:
$options = ['1' => 'Urgent', '2' => 'Medium', '3' => 'Low'];
$form->addComponent(new \App\Forms\MdSelectBox('Priority', $options), 'priority');
或者您可以在某处注册您的新控件以直接在表单上使用它 addMdSelectBox()
:
ObjectMixin::setExtensionMethod(Container::class, 'addMdSelectbox', function (Container $container, $name, $label = null, array $items = null) {
return $container[$name] = new \App\Forms\MdSelectBox($label, $items);
});
我最近购买了 angular 主题 admin.io (https://themeforest.net/item/adminio-responsive-material-design-admin/10761963),我想将其与 PHP Nette 框架一起使用。通过手动渲染,文本输入和按钮一切顺利。但是我被 select 元素困住了。 它应该是这样的:
<md-select ng-model="someVal" name="priority" placeholder="Zvolte prioritu">
<md-option value="1">Urgent</md-option>
<md-option value="2">Vysoká</md-option>
<md-option value="3">Střední</md-option>
<md-option value="4">Nízká</md-option>
</md-select>
但 Nette 将其呈现为基本 <select>
。我可以用 <md-select n:name="">
渲染 select,但是渲染选项有问题。有没有什么方法可以像我处理 select 和整个表单那样手动呈现 <option>
,或者我可以为该表单使用我自己的模板,或者有人知道,如何使它工作?谢谢!
这会有点困难。 Nette SelectBox 不是为此而构建的。您需要构建自己的控件。您可以使用 Nette\Forms\Controls\SelectBox
作为模板并进行一些调整:
<?php
namespace App\Forms;
use Nette;
/**
* Select box control that allows single item selection.
*/
class MdSelectBox extends Nette\Forms\Controls\ChoiceControl
{
/** validation rule */
const VALID = ':selectBoxValid';
/** @var array of option / optgroup */
private $options = [];
/** @var mixed */
private $prompt = false;
/** @var array */
private $optionAttributes = [];
public function __construct($label = null, array $items = null)
{
parent::__construct($label, $items);
$this->setOption('type', 'md-select');
$this->addCondition(Nette\Forms\Form::BLANK)
->addRule([$this, 'isOk'], Nette\Forms\Validator::$messages[self::VALID]);
}
/**
* Sets first prompt item in select box.
* @param string|object
* @return static
*/
public function setPrompt($prompt)
{
$this->prompt = $prompt;
return $this;
}
/**
* Returns first prompt item?
* @return mixed
*/
public function getPrompt()
{
return $this->prompt;
}
/**
* Sets options and option groups from which to choose.
* @return static
*/
public function setItems(array $items, $useKeys = true)
{
if (!$useKeys) {
$res = [];
foreach ($items as $key => $value) {
unset($items[$key]);
if (is_array($value)) {
foreach ($value as $val) {
$res[$key][(string) $val] = $val;
}
} else {
$res[(string) $value] = $value;
}
}
$items = $res;
}
$this->options = $items;
return parent::setItems(Nette\Utils\Arrays::flatten($items, true));
}
/**
* Generates control's HTML element.
* @return Nette\Utils\Html
*/
public function getControl()
{
$items = $this->prompt === false ? [] : ['' => $this->translate($this->prompt)];
foreach ($this->options as $key => $value) {
$items[is_array($value) ? $this->translate($key) : $key] = $this->translate($value);
}
return Helpers::createSelectBox(
$items,
[
'disabled:' => is_array($this->disabled) ? $this->disabled : null,
] + $this->optionAttributes,
$this->value
)->addAttributes(parent::getControl()->attrs);
}
/**
* @return static
*/
public function addOptionAttributes(array $attributes)
{
$this->optionAttributes = $attributes + $this->optionAttributes;
return $this;
}
/**
* @return bool
*/
public function isOk()
{
return $this->isDisabled()
|| $this->prompt !== false
|| $this->getValue() !== null
|| !$this->options
|| $this->control->size > 1;
}
}
您还需要重写 Nette\Forms\Helpers
class,这是您无法使用 Nette\Forms\Controls\SelectBox
:
<?php
namespace App\Forms;
use Nette;
use Nette\Utils\Html;
/**
* Forms helpers.
*/
class Helpers
{
use Nette\StaticClass;
/**
* @return Html
*/
public static function createSelectBox(array $items, array $optionAttrs = null, $selected = null)
{
if ($selected !== null) {
$optionAttrs['selected?'] = $selected;
}
list($optionAttrs, $optionTag) = self::prepareAttrs($optionAttrs, 'md-option');
$option = Html::el();
$res = $tmp = '';
foreach ($items as $group => $subitems) {
if (is_array($subitems)) {
$res .= Html::el('md-optgroup')->label($group)->startTag();
$tmp = '</md-optgroup>';
} else {
$subitems = [$group => $subitems];
}
foreach ($subitems as $value => $caption) {
$option->value = $value;
foreach ($optionAttrs as $k => $v) {
$option->attrs[$k] = isset($v[$value]) ? $v[$value] : null;
}
if ($caption instanceof Html) {
$caption = clone $caption;
$res .= $caption->setName('md-option')->addAttributes($option->attrs);
} else {
$res .= $optionTag . $option->attributes() . '>'
. htmlspecialchars((string) $caption, ENT_NOQUOTES, 'UTF-8')
. '</md-option>';
}
if ($selected === $value) {
unset($optionAttrs['selected'], $option->attrs['selected']);
}
}
$res .= $tmp;
$tmp = '';
}
return Html::el('md-select')->setHtml($res);
}
private static function prepareAttrs($attrs, $name)
{
$dynamic = [];
foreach ((array) $attrs as $k => $v) {
$p = str_split($k, strlen($k) - 1);
if ($p[1] === '?' || $p[1] === ':') {
unset($attrs[$k], $attrs[$p[0]]);
if ($p[1] === '?') {
$dynamic[$p[0]] = array_fill_keys((array) $v, true);
} elseif (is_array($v) && $v) {
$dynamic[$p[0]] = $v;
} else {
$attrs[$p[0]] = $v;
}
}
}
return [$dynamic, '<' . $name . Html::el(null, $attrs)->attributes()];
}
}
然后你可以像这样在你的表单中使用你新建的 MdSelectBox
:
$options = ['1' => 'Urgent', '2' => 'Medium', '3' => 'Low'];
$form->addComponent(new \App\Forms\MdSelectBox('Priority', $options), 'priority');
或者您可以在某处注册您的新控件以直接在表单上使用它 addMdSelectBox()
:
ObjectMixin::setExtensionMethod(Container::class, 'addMdSelectbox', function (Container $container, $name, $label = null, array $items = null) {
return $container[$name] = new \App\Forms\MdSelectBox($label, $items);
});