Preface
The DetailView plug-in, which combines the display and editing of custom fields, has been studied for some time. The DetailView plug-in itself is not so flexible and must be revamped to achieve this effect.There are both display and edit DetailView plug-ins, which can be done simply by adding custom fields.But how do you add it?Since it is a custom field, it must be necessary and can not be written to death. Let me share the specific implementation ideas!
Design sketch
Submission time, nickname, contact category, gender are custom fields
Find Plugin Source
The source code for the plug-in (vendor\kartik-v\yii2-detail-view\DetailView.php) can be found by naming or printing tests of its methods, and the following methods for clicking on details are known:
/** * Renders the main detail view widget * * @return string the detail view content */ protected function renderDetailView() { $rows = []; foreach ($this->attributes as $attribute) { $rows[] = $this->renderAttributeRow($attribute); } $tag = ArrayHelper::remove($this->options, 'tag', 'table'); $output = Html::tag($tag, implode("\n", $rows), $this->options); return ($this->bootstrap && $this->responsive) ? '<div class="table-responsive kv-detail-view">' . $output . '</div>' : '<div class="kv-detail-view">' . $output . '</div>'; }
Override classes and their methods
1. If both classes and methods are found, they will be rewritten.Copy and rewrite the DetailView class and the methods and components involved in the renderDetailView method, inheriting the parent class \kartik\detailDetailView.
2. Copy the public $attributes of the parent class; attribute adds public $custom to the subclass; attribute because the field of the $attributes attribute does not follow the same model as the custom field, so a new attribute must be defined to use.
3. After rewriting, the code is as follows (I have commented on the important part of the code):
<?php namespace common\components\detailview; use Yii; use yii\helpers\ArrayHelper; use kartik\helpers\Html; use kartik\base\Config; class DetailView extends \kartik\detail\DetailView { public $custom; // inputs list private static $_inputsList = [ self::INPUT_HIDDEN => 'hiddenInput', self::INPUT_TEXT => 'textInput', self::INPUT_PASSWORD => 'passwordInput', self::INPUT_TEXTAREA => 'textArea', self::INPUT_CHECKBOX => 'checkbox', self::INPUT_RADIO => 'radio', self::INPUT_LIST_BOX => 'listBox', self::INPUT_DROPDOWN_LIST => 'dropDownList', self::INPUT_CHECKBOX_LIST => 'checkboxList', self::INPUT_RADIO_LIST => 'radioList', self::INPUT_HTML5_INPUT => 'input', self::INPUT_FILE => 'fileInput', self::INPUT_WIDGET => 'widget', ]; // dropdown inputs private static $_dropDownInputs = [ self::INPUT_LIST_BOX => 'listBox', self::INPUT_DROPDOWN_LIST => 'dropDownList', self::INPUT_CHECKBOX_LIST => 'checkboxList', self::INPUT_RADIO_LIST => 'radioList', ]; protected function renderDetailView() { $rows = []; foreach ($this->attributes as $attribute) { $rows[] = $this->renderAttributeRow($attribute); } //Note 1: Increase the loop to get the value of a custom field foreach ($this->custom['attributes'] as $attribute) { $rows[] = $this->renderCustomAttributeRow($attribute); } $tag = ArrayHelper::remove($this->options, 'tag', 'table'); $output = Html::tag($tag, implode("\n", $rows), $this->options); return ($this->bootstrap && $this->responsive) ? '<div class="table-responsive kv-detail-view">' . $output . '</div>' : '<div class="kv-detail-view">' . $output . '</div>'; } //Custom field display protected function renderCustomAttributeRow($attribute) { //Note 2: Just leave these three lines of code and delete the rest $this->_rowOptions = ArrayHelper::getValue($attribute, 'rowOptions', $this->rowOptions); $content = $this->renderCustomAttributeItem($attribute); return Html::tag('tr', $content, $this->_rowOptions); } protected function renderCustomAttributeItem($attribute) { $labelColOpts = ArrayHelper::getValue($attribute, 'labelColOptions', $this->labelColOptions); $valueColOpts = ArrayHelper::getValue($attribute, 'valueColOptions', $this->valueColOptions); if (ArrayHelper::getValue($attribute, 'group', false)) { $groupOptions = ArrayHelper::getValue($attribute, 'groupOptions', []); $label = ArrayHelper::getValue($attribute, 'label', ''); if (empty($groupOptions['colspan'])) { $groupOptions['colspan'] = 2; } return Html::tag('th', $label, $groupOptions); } if ($this->hideIfEmpty === true && empty($attribute['value'])) { Html::addCssClass($this->_rowOptions, 'kv-view-hidden'); } if (ArrayHelper::getValue($attribute, 'type', 'text') === self::INPUT_HIDDEN) { Html::addCssClass($this->_rowOptions, 'kv-edit-hidden'); } $value = $attribute['value']; if ($this->notSetIfEmpty && ($value === '' || $value === null)) { $value = null; } $dispAttr = $this->formatter->format($value, $attribute['format']); Html::addCssClass($this->viewAttributeContainer, 'kv-attribute'); Html::addCssClass($this->editAttributeContainer, 'kv-form-attribute'); $output = Html::tag('div', $dispAttr, $this->viewAttributeContainer) . "\n"; //var_dump($this->editAttributeContainer);exit; if ($this->enableEditMode) { $editInput = ArrayHelper::getValue($attribute, 'displayOnly', false) ? $dispAttr : $this->renderFormCustomAttribute($attribute); $output .= Html::tag('div', $editInput, $this->editAttributeContainer); } return Html::tag('th', $attribute['label'], $labelColOpts) . "\n" . Html::tag('td', $output, $valueColOpts); } protected function renderFormCustomAttribute($config) { if (empty($config['attribute'])) { return ''; } //Note 3: Comment out the model here, it can't be used. This is the attribute s model //$model = ArrayHelper::getValue($config, 'editModel', $this->model); //if (!$model instanceof Model) { $model = $this->model; //} $attr = ArrayHelper::getValue($config, 'updateAttr', $config['attribute']); $input = ArrayHelper::getValue($config, 'type', self::INPUT_TEXT); $fieldConfig = ArrayHelper::getValue($config, 'fieldConfig', []); $inputWidth = ArrayHelper::getValue($config, 'inputWidth', ''); $container = ArrayHelper::getValue($config, 'inputContainer', []); if ($inputWidth != '') { Html::addCssStyle($container, "width: {$inputWidth}"); // deprecated since v1.7.4 } $template = ArrayHelper::getValue($fieldConfig, 'template', "{input}\n{error}\n{hint}"); $row = Html::tag('div', $template, $container); if (static::hasGridCol($container)) { $row = '<div class="row">' . $row . '</div>'; } $fieldConfig['template'] = $row; if (substr($input, 0, 8) == "\\kartik\\") { Config::validateInputWidget($input, 'as an input widget for DetailView edit mode'); } elseif ($input !== self::INPUT_WIDGET && !in_array($input, self::$_inputsList)) { throw new InvalidConfigException( "Invalid input type '{$input}' defined for the attribute '" . $config['attribute'] . "'." ); } $options = ArrayHelper::getValue($config, 'options', []); $widgetOptions = ArrayHelper::getValue($config, 'widgetOptions', []); $class = ArrayHelper::remove($widgetOptions, 'class', ''); if (!empty($config['options'])) { $widgetOptions['options'] = $config['options']; } //return array("options"=> array("class"=> "form-group")); //return "<a>yyyy</a>"; if (Config::isInputWidget($input)) { $class = $input; return $this->_form->field($model, $attr, $fieldConfig)->widget($class, $widgetOptions); } if ($input === self::INPUT_WIDGET) { if ($class == '') { throw new InvalidConfigException("Widget class not defined in 'widgetOptions' for {$input}'."); } return $this->_form->field($model, $attr, $fieldConfig)->widget($class, $widgetOptions); } if (in_array($input, self::$_dropDownInputs)) { $items = ArrayHelper::getValue($config, 'items', []); return $this->_form->field($model, $attr, $fieldConfig)->$input($items, $options); } if ($input == self::INPUT_HTML5_INPUT) { $inputType = ArrayHelper::getValue($config, 'inputType', self::INPUT_TEXT); return $this->_form->field($model, $attr, $fieldConfig)->$input($inputType, $options); } /* Note 4: Rewrite the original return value.There are several reasons why it should be rewritten as follows: 1,Using $this->_form->field() will walk the attribute s model and overwrite fails; 2,You can refer to the usage of $this->_form->field() and then look at the rules for the successful div and input generation of the source code to see why you have made this change. */ $name = $this->custom['id']."[".$config['attribute']."]"; $id = strtolower($this->custom['id'])."-".$config['attribute']; $html = Html::input('text', $name, $config['value'], ['class' => "form-control",'id'=> $id]); $html .= Html::tag('div', "", ['class' => 'help-block']); $html = Html::tag('div', $html, []); $fieldclass = "field-".$id; $html = Html::tag('div', $html, ['class' => $fieldclass]); return $html; } }
View custom properties
<?= DetailView::widget([ 'id'=>'contact-view', 'formOptions'=>[ 'id'=>'contact-form-view', 'action'=>'/contact/update?id='.$model->id."&salesId=".$model->sale_id, 'options'=>['data-form'=>'public-form-submit'], ], 'model' => $model, 'condensed'=>false, 'hover'=>true, 'mode'=>Yii::$app->request->get('edit')=='t' ? DetailView::MODE_EDIT : DetailView::MODE_VIEW, 'panel'=>[ 'heading'=>$this->title, 'type'=>DetailView::TYPE_INFO, ], 'custom' => $custom, //Cusm property put here 'attributes' => $attributes, 'deleteOptions'=>[ 'class'=>"prohibit deletet", 'label'=>'<span class="glyphicon"></span>', ], 'enableEditMode'=>true, 'fadeDelay'=>0, //Close Animation ]) ?>
The array format of $custom is as follows:
Array ( [id] => ClassForm [attributes] => Array ( [0] => Array ( [attribute] => add_time [value] => [label] => Submission Time [format] => text ) [1] => Array ( [attribute] => nickname [value] => Test Name [label] => Nickname? [format] => text ) [2] => Array ( [attribute] => sort [value] => [label] => Contact Category [format] => text ) [3] => Array ( [attribute] => sex [value] => male [label] => Gender [format] => text ) ) )
Note: If editing is written separately from saving the refreshed page, remember to add custom attributes to the refreshed page as well, otherwise the custom fields added after saving the refresh disappear.
Summary analysis
That's how I want to share the etailView plug-in with custom field display and editing.
Procedures are very deep, not always summarized, and will be more arduous in the future.
(finished)