ActiveField.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657
  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\widgets;
  8. use Yii;
  9. use yii\base\Component;
  10. use yii\helpers\ArrayHelper;
  11. use yii\helpers\Html;
  12. use yii\base\Model;
  13. use yii\web\JsExpression;
  14. /**
  15. * @author Qiang Xue <[email protected]>
  16. * @since 2.0
  17. */
  18. class ActiveField extends Component
  19. {
  20. /**
  21. * @var ActiveForm the form that this field is associated with.
  22. */
  23. public $form;
  24. /**
  25. * @var Model the data model that this field is associated with
  26. */
  27. public $model;
  28. /**
  29. * @var string the model attribute that this field is associated with
  30. */
  31. public $attribute;
  32. /**
  33. * @var array the HTML attributes (name-value pairs) for the field container tag.
  34. * The values will be HTML-encoded using [[Html::encode()]].
  35. * If a value is null, the corresponding attribute will not be rendered.
  36. * The following special options are recognized:
  37. *
  38. * - tag: the tag name of the container element. Defaults to "div".
  39. */
  40. public $options = ['class' => 'form-group'];
  41. /**
  42. * @var string the template that is used to arrange the label, the input field, the error message and the hint text.
  43. * The following tokens will be replaced when [[render()]] is called: `{label}`, `{input}`, `{error}` and `{hint}`.
  44. */
  45. public $template = "{label}\n{input}\n{hint}\n{error}";
  46. /**
  47. * @var array the default options for the input tags. The parameter passed to individual input methods
  48. * (e.g. [[textInput()]]) will be merged with this property when rendering the input tag.
  49. */
  50. public $inputOptions = ['class' => 'form-control'];
  51. /**
  52. * @var array the default options for the error tags. The parameter passed to [[error()]] will be
  53. * merged with this property when rendering the error tag.
  54. * The following special options are recognized:
  55. *
  56. * - tag: the tag name of the container element. Defaults to "div".
  57. */
  58. public $errorOptions = ['class' => 'help-block'];
  59. /**
  60. * @var array the default options for the label tags. The parameter passed to [[label()]] will be
  61. * merged with this property when rendering the label tag.
  62. */
  63. public $labelOptions = ['class' => 'control-label'];
  64. /**
  65. * @var array the default options for the hint tags. The parameter passed to [[hint()]] will be
  66. * merged with this property when rendering the hint tag.
  67. * The following special options are recognized:
  68. *
  69. * - tag: the tag name of the container element. Defaults to "div".
  70. */
  71. public $hintOptions = ['class' => 'hint-block'];
  72. /**
  73. * @var boolean whether to enable client-side data validation.
  74. * If not set, it will take the value of [[ActiveForm::enableClientValidation]].
  75. */
  76. public $enableClientValidation;
  77. /**
  78. * @var boolean whether to enable AJAX-based data validation.
  79. * If not set, it will take the value of [[ActiveForm::enableAjaxValidation]].
  80. */
  81. public $enableAjaxValidation;
  82. /**
  83. * @var boolean whether to perform validation when the input field loses focus and its value is found changed.
  84. * If not set, it will take the value of [[ActiveForm::validateOnChange]].
  85. */
  86. public $validateOnChange;
  87. /**
  88. * @var boolean whether to perform validation while the user is typing in the input field.
  89. * If not set, it will take the value of [[ActiveForm::validateOnType]].
  90. * @see validationDelay
  91. */
  92. public $validateOnType;
  93. /**
  94. * @var integer number of milliseconds that the validation should be delayed when the input field
  95. * is changed or the user types in the field.
  96. * If not set, it will take the value of [[ActiveForm::validationDelay]].
  97. */
  98. public $validationDelay;
  99. /**
  100. * @var array the jQuery selectors for selecting the container, input and error tags.
  101. * The array keys should be "container", "input", and/or "error", and the array values
  102. * are the corresponding selectors. For example, `['input' => '#my-input']`.
  103. *
  104. * The container selector is used under the context of the form, while the input and the error
  105. * selectors are used under the context of the container.
  106. *
  107. * You normally do not need to set this property as the default selectors should work well for most cases.
  108. */
  109. public $selectors;
  110. /**
  111. * @var array different parts of the field (e.g. input, label). This will be used together with
  112. * [[template]] to generate the final field HTML code. The keys are the token names in [[template]],
  113. * while the values are the corresponding HTML code. Valid tokens include `{input}`, `{label}` and `{error}`.
  114. * Note that you normally don't need to access this property directly as
  115. * it is maintained by various methods of this class.
  116. */
  117. public $parts = [];
  118. /**
  119. * PHP magic method that returns the string representation of this object.
  120. * @return string the string representation of this object.
  121. */
  122. public function __toString()
  123. {
  124. // __toString cannot throw exception
  125. // use trigger_error to bypass this limitation
  126. try {
  127. return $this->render();
  128. } catch (\Exception $e) {
  129. trigger_error($e->getMessage() . "\n\n" . $e->getTraceAsString());
  130. return '';
  131. }
  132. }
  133. /**
  134. * Renders the whole field.
  135. * This method will generate the label, error tag, input tag and hint tag (if any), and
  136. * assemble them into HTML according to [[template]].
  137. * @param string|callable $content the content within the field container.
  138. * If null (not set), the default methods will be called to generate the label, error tag and input tag,
  139. * and use them as the content.
  140. * If a callable, it will be called to generate the content. The signature of the callable should be:
  141. *
  142. * ~~~
  143. * function ($field) {
  144. * return $html;
  145. * }
  146. * ~~~
  147. *
  148. * @return string the rendering result
  149. */
  150. public function render($content = null)
  151. {
  152. if ($content === null) {
  153. if (!isset($this->parts['{input}'])) {
  154. $this->parts['{input}'] = Html::activeTextInput($this->model, $this->attribute, $this->inputOptions);
  155. }
  156. if (!isset($this->parts['{label}'])) {
  157. $this->parts['{label}'] = Html::activeLabel($this->model, $this->attribute, $this->labelOptions);
  158. }
  159. if (!isset($this->parts['{error}'])) {
  160. $this->parts['{error}'] = Html::error($this->model, $this->attribute, $this->errorOptions);
  161. }
  162. if (!isset($this->parts['{hint}'])) {
  163. $this->parts['{hint}'] = '';
  164. }
  165. $content = strtr($this->template, $this->parts);
  166. } elseif (!is_string($content)) {
  167. $content = call_user_func($content, $this);
  168. }
  169. return $this->begin() . "\n" . $content . "\n" . $this->end();
  170. }
  171. /**
  172. * Renders the opening tag of the field container.
  173. * @return string the rendering result.
  174. */
  175. public function begin()
  176. {
  177. $clientOptions = $this->getClientOptions();
  178. if (!empty($clientOptions)) {
  179. $this->form->attributes[$this->attribute] = $clientOptions;
  180. }
  181. $inputID = Html::getInputId($this->model, $this->attribute);
  182. $attribute = Html::getAttributeName($this->attribute);
  183. $options = $this->options;
  184. $class = isset($options['class']) ? [$options['class']] : [];
  185. $class[] = "field-$inputID";
  186. if ($this->model->isAttributeRequired($attribute)) {
  187. $class[] = $this->form->requiredCssClass;
  188. }
  189. if ($this->model->hasErrors($attribute)) {
  190. $class[] = $this->form->errorCssClass;
  191. }
  192. $options['class'] = implode(' ', $class);
  193. $tag = ArrayHelper::remove($options, 'tag', 'div');
  194. return Html::beginTag($tag, $options);
  195. }
  196. /**
  197. * Renders the closing tag of the field container.
  198. * @return string the rendering result.
  199. */
  200. public function end()
  201. {
  202. return Html::endTag(isset($this->options['tag']) ? $this->options['tag'] : 'div');
  203. }
  204. /**
  205. * Generates a label tag for [[attribute]].
  206. * @param string $label the label to use. If null, it will be generated via [[Model::getAttributeLabel()]].
  207. * Note that this will NOT be [[Html::encode()|encoded]].
  208. * @param array $options the tag options in terms of name-value pairs. It will be merged with [[labelOptions]].
  209. * The options will be rendered as the attributes of the resulting tag. The values will be HTML-encoded
  210. * using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
  211. * @return static the field object itself
  212. */
  213. public function label($label = null, $options = [])
  214. {
  215. $options = array_merge($this->labelOptions, $options);
  216. if ($label !== null) {
  217. $options['label'] = $label;
  218. }
  219. $this->parts['{label}'] = Html::activeLabel($this->model, $this->attribute, $options);
  220. return $this;
  221. }
  222. /**
  223. * Generates a tag that contains the first validation error of [[attribute]].
  224. * Note that even if there is no validation error, this method will still return an empty error tag.
  225. * @param array $options the tag options in terms of name-value pairs. It will be merged with [[errorOptions]].
  226. * The options will be rendered as the attributes of the resulting tag. The values will be HTML-encoded
  227. * using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
  228. *
  229. * The following options are specially handled:
  230. *
  231. * - tag: this specifies the tag name. If not set, "div" will be used.
  232. *
  233. * @return static the field object itself
  234. */
  235. public function error($options = [])
  236. {
  237. $options = array_merge($this->errorOptions, $options);
  238. $this->parts['{error}'] = Html::error($this->model, $this->attribute, $options);
  239. return $this;
  240. }
  241. /**
  242. * Renders the hint tag.
  243. * @param string $content the hint content. It will NOT be HTML-encoded.
  244. * @param array $options the tag options in terms of name-value pairs. These will be rendered as
  245. * the attributes of the hint tag. The values will be HTML-encoded using [[Html::encode()]].
  246. *
  247. * The following options are specially handled:
  248. *
  249. * - tag: this specifies the tag name. If not set, "div" will be used.
  250. *
  251. * @return static the field object itself
  252. */
  253. public function hint($content, $options = [])
  254. {
  255. $options = array_merge($this->hintOptions, $options);
  256. $tag = ArrayHelper::remove($options, 'tag', 'div');
  257. $this->parts['{hint}'] = Html::tag($tag, $content, $options);
  258. return $this;
  259. }
  260. /**
  261. * Renders an input tag.
  262. * @param string $type the input type (e.g. 'text', 'password')
  263. * @param array $options the tag options in terms of name-value pairs. These will be rendered as
  264. * the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
  265. * @return static the field object itself
  266. */
  267. public function input($type, $options = [])
  268. {
  269. $options = array_merge($this->inputOptions, $options);
  270. $this->adjustLabelFor($options);
  271. $this->parts['{input}'] = Html::activeInput($type, $this->model, $this->attribute, $options);
  272. return $this;
  273. }
  274. /**
  275. * Renders a text input.
  276. * This method will generate the "name" and "value" tag attributes automatically for the model attribute
  277. * unless they are explicitly specified in `$options`.
  278. * @param array $options the tag options in terms of name-value pairs. These will be rendered as
  279. * the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
  280. * @return static the field object itself
  281. */
  282. public function textInput($options = [])
  283. {
  284. $options = array_merge($this->inputOptions, $options);
  285. $this->adjustLabelFor($options);
  286. $this->parts['{input}'] = Html::activeTextInput($this->model, $this->attribute, $options);
  287. return $this;
  288. }
  289. /**
  290. * Renders a password input.
  291. * This method will generate the "name" and "value" tag attributes automatically for the model attribute
  292. * unless they are explicitly specified in `$options`.
  293. * @param array $options the tag options in terms of name-value pairs. These will be rendered as
  294. * the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
  295. * @return static the field object itself
  296. */
  297. public function passwordInput($options = [])
  298. {
  299. $options = array_merge($this->inputOptions, $options);
  300. $this->adjustLabelFor($options);
  301. $this->parts['{input}'] = Html::activePasswordInput($this->model, $this->attribute, $options);
  302. return $this;
  303. }
  304. /**
  305. * Renders a file input.
  306. * This method will generate the "name" and "value" tag attributes automatically for the model attribute
  307. * unless they are explicitly specified in `$options`.
  308. * @param array $options the tag options in terms of name-value pairs. These will be rendered as
  309. * the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
  310. * @return static the field object itself
  311. */
  312. public function fileInput($options = [])
  313. {
  314. // https://github.com/yiisoft/yii2/pull/795
  315. if ($this->inputOptions !== ['class' => 'form-control']) {
  316. $options = array_merge($this->inputOptions, $options);
  317. }
  318. $this->adjustLabelFor($options);
  319. $this->parts['{input}'] = Html::activeFileInput($this->model, $this->attribute, $options);
  320. return $this;
  321. }
  322. /**
  323. * Renders a text area.
  324. * The model attribute value will be used as the content in the textarea.
  325. * @param array $options the tag options in terms of name-value pairs. These will be rendered as
  326. * the attributes of the resulting tag. The values will be HTML-encoded using [[Html::encode()]].
  327. * @return static the field object itself
  328. */
  329. public function textarea($options = [])
  330. {
  331. $options = array_merge($this->inputOptions, $options);
  332. $this->adjustLabelFor($options);
  333. $this->parts['{input}'] = Html::activeTextarea($this->model, $this->attribute, $options);
  334. return $this;
  335. }
  336. /**
  337. * Renders a radio button.
  338. * This method will generate the "checked" tag attribute according to the model attribute value.
  339. * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
  340. *
  341. * - uncheck: string, the value associated with the uncheck state of the radio button. If not set,
  342. * it will take the default value '0'. This method will render a hidden input so that if the radio button
  343. * is not checked and is submitted, the value of this attribute will still be submitted to the server
  344. * via the hidden input.
  345. * - label: string, a label displayed next to the radio button. It will NOT be HTML-encoded. Therefore you can pass
  346. * in HTML code such as an image tag. If this is is coming from end users, you should [[Html::encode()]] it to prevent XSS attacks.
  347. * When this option is specified, the radio button will be enclosed by a label tag.
  348. * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
  349. *
  350. * The rest of the options will be rendered as the attributes of the resulting tag. The values will
  351. * be HTML-encoded using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
  352. * @param boolean $enclosedByLabel whether to enclose the radio within the label.
  353. * If true, the method will still use [[template]] to layout the checkbox and the error message
  354. * except that the radio is enclosed by the label tag.
  355. * @return static the field object itself
  356. */
  357. public function radio($options = [], $enclosedByLabel = true)
  358. {
  359. if ($enclosedByLabel) {
  360. if (!isset($options['label'])) {
  361. $attribute = Html::getAttributeName($this->attribute);
  362. $options['label'] = Html::encode($this->model->getAttributeLabel($attribute));
  363. }
  364. $this->parts['{input}'] = Html::activeRadio($this->model, $this->attribute, $options);
  365. $this->parts['{label}'] = '';
  366. } else {
  367. $this->parts['{input}'] = Html::activeRadio($this->model, $this->attribute, $options);
  368. }
  369. $this->adjustLabelFor($options);
  370. return $this;
  371. }
  372. /**
  373. * Renders a checkbox.
  374. * This method will generate the "checked" tag attribute according to the model attribute value.
  375. * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
  376. *
  377. * - uncheck: string, the value associated with the uncheck state of the radio button. If not set,
  378. * it will take the default value '0'. This method will render a hidden input so that if the radio button
  379. * is not checked and is submitted, the value of this attribute will still be submitted to the server
  380. * via the hidden input.
  381. * - label: string, a label displayed next to the checkbox. It will NOT be HTML-encoded. Therefore you can pass
  382. * in HTML code such as an image tag. If this is is coming from end users, you should [[Html::encode()]] it to prevent XSS attacks.
  383. * When this option is specified, the checkbox will be enclosed by a label tag.
  384. * - labelOptions: array, the HTML attributes for the label tag. This is only used when the "label" option is specified.
  385. *
  386. * The rest of the options will be rendered as the attributes of the resulting tag. The values will
  387. * be HTML-encoded using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
  388. * @param boolean $enclosedByLabel whether to enclose the checkbox within the label.
  389. * If true, the method will still use [[template]] to layout the checkbox and the error message
  390. * except that the checkbox is enclosed by the label tag.
  391. * @return static the field object itself
  392. */
  393. public function checkbox($options = [], $enclosedByLabel = true)
  394. {
  395. if ($enclosedByLabel) {
  396. if (!isset($options['label'])) {
  397. $attribute = Html::getAttributeName($this->attribute);
  398. $options['label'] = Html::encode($this->model->getAttributeLabel($attribute));
  399. }
  400. $this->parts['{input}'] = Html::activeCheckbox($this->model, $this->attribute, $options);
  401. $this->parts['{label}'] = '';
  402. } else {
  403. $this->parts['{input}'] = Html::activeCheckbox($this->model, $this->attribute, $options);
  404. }
  405. $this->adjustLabelFor($options);
  406. return $this;
  407. }
  408. /**
  409. * Renders a drop-down list.
  410. * The selection of the drop-down list is taken from the value of the model attribute.
  411. * @param array $items the option data items. The array keys are option values, and the array values
  412. * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
  413. * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
  414. * If you have a list of data models, you may convert them into the format described above using
  415. * [[ArrayHelper::map()]].
  416. *
  417. * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
  418. * the labels will also be HTML-encoded.
  419. * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
  420. *
  421. * - prompt: string, a prompt text to be displayed as the first option;
  422. * - options: array, the attributes for the select option tags. The array keys must be valid option values,
  423. * and the array values are the extra attributes for the corresponding option tags. For example,
  424. *
  425. * ~~~
  426. * [
  427. * 'value1' => ['disabled' => true],
  428. * 'value2' => ['label' => 'value 2'],
  429. * ];
  430. * ~~~
  431. *
  432. * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
  433. * except that the array keys represent the optgroup labels specified in $items.
  434. *
  435. * The rest of the options will be rendered as the attributes of the resulting tag. The values will
  436. * be HTML-encoded using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
  437. *
  438. * @return static the field object itself
  439. */
  440. public function dropDownList($items, $options = [])
  441. {
  442. $options = array_merge($this->inputOptions, $options);
  443. $this->adjustLabelFor($options);
  444. $this->parts['{input}'] = Html::activeDropDownList($this->model, $this->attribute, $items, $options);
  445. return $this;
  446. }
  447. /**
  448. * Renders a list box.
  449. * The selection of the list box is taken from the value of the model attribute.
  450. * @param array $items the option data items. The array keys are option values, and the array values
  451. * are the corresponding option labels. The array can also be nested (i.e. some array values are arrays too).
  452. * For each sub-array, an option group will be generated whose label is the key associated with the sub-array.
  453. * If you have a list of data models, you may convert them into the format described above using
  454. * [[\yii\helpers\ArrayHelper::map()]].
  455. *
  456. * Note, the values and labels will be automatically HTML-encoded by this method, and the blank spaces in
  457. * the labels will also be HTML-encoded.
  458. * @param array $options the tag options in terms of name-value pairs. The following options are specially handled:
  459. *
  460. * - prompt: string, a prompt text to be displayed as the first option;
  461. * - options: array, the attributes for the select option tags. The array keys must be valid option values,
  462. * and the array values are the extra attributes for the corresponding option tags. For example,
  463. *
  464. * ~~~
  465. * [
  466. * 'value1' => ['disabled' => true],
  467. * 'value2' => ['label' => 'value 2'],
  468. * ];
  469. * ~~~
  470. *
  471. * - groups: array, the attributes for the optgroup tags. The structure of this is similar to that of 'options',
  472. * except that the array keys represent the optgroup labels specified in $items.
  473. * - unselect: string, the value that will be submitted when no option is selected.
  474. * When this attribute is set, a hidden field will be generated so that if no option is selected in multiple
  475. * mode, we can still obtain the posted unselect value.
  476. *
  477. * The rest of the options will be rendered as the attributes of the resulting tag. The values will
  478. * be HTML-encoded using [[Html::encode()]]. If a value is null, the corresponding attribute will not be rendered.
  479. *
  480. * @return static the field object itself
  481. */
  482. public function listBox($items, $options = [])
  483. {
  484. $options = array_merge($this->inputOptions, $options);
  485. $this->adjustLabelFor($options);
  486. $this->parts['{input}'] = Html::activeListBox($this->model, $this->attribute, $items, $options);
  487. return $this;
  488. }
  489. /**
  490. * Renders a list of checkboxes.
  491. * A checkbox list allows multiple selection, like [[listBox()]].
  492. * As a result, the corresponding submitted value is an array.
  493. * The selection of the checkbox list is taken from the value of the model attribute.
  494. * @param array $items the data item used to generate the checkboxes.
  495. * The array values are the labels, while the array keys are the corresponding checkbox values.
  496. * Note that the labels will NOT be HTML-encoded, while the values will.
  497. * @param array $options options (name => config) for the checkbox list. The following options are specially handled:
  498. *
  499. * - unselect: string, the value that should be submitted when none of the checkboxes is selected.
  500. * By setting this option, a hidden input will be generated.
  501. * - separator: string, the HTML code that separates items.
  502. * - item: callable, a callback that can be used to customize the generation of the HTML code
  503. * corresponding to a single item in $items. The signature of this callback must be:
  504. *
  505. * ~~~
  506. * function ($index, $label, $name, $checked, $value)
  507. * ~~~
  508. *
  509. * where $index is the zero-based index of the checkbox in the whole list; $label
  510. * is the label for the checkbox; and $name, $value and $checked represent the name,
  511. * value and the checked status of the checkbox input.
  512. * @return static the field object itself
  513. */
  514. public function checkboxList($items, $options = [])
  515. {
  516. $this->adjustLabelFor($options);
  517. $this->parts['{input}'] = Html::activeCheckboxList($this->model, $this->attribute, $items, $options);
  518. return $this;
  519. }
  520. /**
  521. * Renders a list of radio buttons.
  522. * A radio button list is like a checkbox list, except that it only allows single selection.
  523. * The selection of the radio buttons is taken from the value of the model attribute.
  524. * @param array $items the data item used to generate the radio buttons.
  525. * The array keys are the labels, while the array values are the corresponding radio button values.
  526. * Note that the labels will NOT be HTML-encoded, while the values will.
  527. * @param array $options options (name => config) for the radio button list. The following options are specially handled:
  528. *
  529. * - unselect: string, the value that should be submitted when none of the radio buttons is selected.
  530. * By setting this option, a hidden input will be generated.
  531. * - separator: string, the HTML code that separates items.
  532. * - item: callable, a callback that can be used to customize the generation of the HTML code
  533. * corresponding to a single item in $items. The signature of this callback must be:
  534. *
  535. * ~~~
  536. * function ($index, $label, $name, $checked, $value)
  537. * ~~~
  538. *
  539. * where $index is the zero-based index of the radio button in the whole list; $label
  540. * is the label for the radio button; and $name, $value and $checked represent the name,
  541. * value and the checked status of the radio button input.
  542. * @return static the field object itself
  543. */
  544. public function radioList($items, $options = [])
  545. {
  546. $this->adjustLabelFor($options);
  547. $this->parts['{input}'] = Html::activeRadioList($this->model, $this->attribute, $items, $options);
  548. return $this;
  549. }
  550. /**
  551. * Renders a widget as the input of the field.
  552. *
  553. * Note that the widget must have both `model` and `attribute` properties. They will
  554. * be initialized with [[model]] and [[attribute]] of this field, respectively.
  555. *
  556. * If you want to use a widget that does not have `model` and `attribute` properties,
  557. * please use [[render()]] instead.
  558. *
  559. * @param string $class the widget class name
  560. * @param array $config name-value pairs that will be used to initialize the widget
  561. * @return static the field object itself
  562. */
  563. public function widget($class, $config = [])
  564. {
  565. /** @var \yii\base\Widget $class */
  566. $config['model'] = $this->model;
  567. $config['attribute'] = $this->attribute;
  568. $config['view'] = $this->form->getView();
  569. $this->parts['{input}'] = $class::widget($config);
  570. return $this;
  571. }
  572. /**
  573. * Adjusts the "for" attribute for the label based on the input options.
  574. * @param array $options the input options
  575. */
  576. protected function adjustLabelFor($options)
  577. {
  578. if (isset($options['id']) && !isset($this->labelOptions['for'])) {
  579. $this->labelOptions['for'] = $options['id'];
  580. }
  581. }
  582. /**
  583. * Returns the JS options for the field.
  584. * @return array the JS options
  585. */
  586. protected function getClientOptions()
  587. {
  588. $attribute = Html::getAttributeName($this->attribute);
  589. if (!in_array($attribute, $this->model->activeAttributes(), true)) {
  590. return [];
  591. }
  592. $enableClientValidation = $this->enableClientValidation || $this->enableClientValidation === null && $this->form->enableClientValidation;
  593. if ($enableClientValidation) {
  594. $validators = [];
  595. foreach ($this->model->getActiveValidators($attribute) as $validator) {
  596. /** @var \yii\validators\Validator $validator */
  597. $js = $validator->clientValidateAttribute($this->model, $attribute, $this->form->getView());
  598. if ($validator->enableClientValidation && $js != '') {
  599. $validators[] = $js;
  600. }
  601. }
  602. if (!empty($validators)) {
  603. $options['validate'] = new JsExpression("function(attribute, value, messages) {" . implode('', $validators) . '}');
  604. }
  605. }
  606. $enableAjaxValidation = $this->enableAjaxValidation || $this->enableAjaxValidation === null && $this->form->enableAjaxValidation;
  607. if ($enableAjaxValidation) {
  608. $options['enableAjaxValidation'] = 1;
  609. }
  610. if ($enableClientValidation && !empty($options['validate']) || $enableAjaxValidation) {
  611. $inputID = Html::getInputId($this->model, $this->attribute);
  612. $options['name'] = $inputID;
  613. foreach (['validateOnChange', 'validateOnType', 'validationDelay'] as $name) {
  614. $options[$name] = $this->$name === null ? $this->form->$name : $this->$name;
  615. }
  616. $options['container'] = isset($this->selectors['container']) ? $this->selectors['container'] : ".field-$inputID";
  617. $options['input'] = isset($this->selectors['input']) ? $this->selectors['input'] : "#$inputID";
  618. if (isset($this->errorOptions['class'])) {
  619. $options['error'] = '.' . implode('.', preg_split('/\s+/', $this->errorOptions['class'], -1, PREG_SPLIT_NO_EMPTY));
  620. } else {
  621. $options['error'] = isset($this->errorOptions['tag']) ? $this->errorOptions['tag'] : 'span';
  622. }
  623. return $options;
  624. } else {
  625. return [];
  626. }
  627. }
  628. }