Используйте форму Нетте с дизайном материала

Недавно я купил угловую тему admin.io (https://themeforest.net/item/adminio-responsive-material-design-admin/10761963), который я хотел бы использовать с фреймворком PHP Nette. С текстовыми вводами и кнопками при ручном рендеринге все в порядке. Но я застрял с выбором элемента. Это должно выглядеть так:

<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>

но Нетте переводит его как основное <select>. Я мог бы сделать выбор с помощью <md-select n:name="">, но есть проблема с параметрами рендеринга. Есть ли способ вручную отобразить <option>, как я сделал с формой выбора и всей формой, или я могу использовать свой собственный шаблон для этой формы, или, пожалуйста, кто-нибудь знает, как это сделать? Благодарю вас!


person hyst    schedule 13.03.2018    source источник


Ответы (1)


Это будет немного сложно. Нетте SelectBox не создана для этого. Вам нужно построить свой собственный Control. Вы можете использовать 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, что является основной причиной, по которой вы не можете добиться этого с помощью 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);
});
person Tomáš Jacík    schedule 20.03.2018
comment
Извини за поздний ответ. Я пробовал это, это прекрасно работает, но md-select не отправляет свое значение в функцию onSuccess[]. Есть ли способ отправить его значения, или мне придется использовать обычный выбор, пожалуйста? - person hyst; 13.04.2018
comment
Конечно, потому что это не обычный выбор. Единственный способ: вы отправляете значения с помощью вызова ajax в JS. - person Tomáš Jacík; 15.04.2018