前言
前段时间研究了DetailView插件结合自定义字段的显示与编辑,DetailView插件本身没有这么灵活,要实现这种效果必须得改造插件。显示与编辑DetailView插件都有了,只需要我们把自定义的字段加进去就行可以实现了。但是怎么加进去呢?既然是自定义字段,那肯定是可有可无,不能写死的,我来分享下具体实现思路吧!
效果图
提交时间、昵称、联系人分类、性别属于自定义字段
找插件源码
找插件的源码(vendor\kartik-v\yii2-detail-view\DetailView.php
),可以通过它方法的命名或者打印测试找到相关的代码,知点击详细时走插件的如下方法:
/**
* 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) ?
'' . $output . '' :
'' . $output . '';
}
重写类及其方法
1、类与方法都找到了,就重写。把DetailView类以及renderDetailView方法里涉及的方法、组件都拷贝过来重写一遍,并继承父类\kartik\detail\DetailView。
2、仿造父类的public $attributes;
属性给子类增加public $custom;
属性,原因是$attributes属性的字段与自定义字段走的不是同一个model,所以必须得定义一个新的属性来用。
3、重写完了之后代码如下(重点注意的部分我在代码中已加上注释):
'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);
}
//注意点一:增加循环获取自定义字段的值
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) ?
'' . $output . '' :
'' . $output . '';
}
//自定义字段的显示
protected function renderCustomAttributeRow($attribute)
{
//注意点二:只留这三行代码,把其它多余的删除
$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 '';
}
//注意点三:把这里的model注释掉,不能用,这是attribute的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 = '' . $row . '';
}
$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 "yyyy";
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);
}
/*
注意点四:把原来的返回值改写。至于为什么要改写成如下,原因有:
1、用$this->_form->field()的话会走attribute的model,改写失败;
2、可以参照$this->_form->field()的用法,然后查看原码生成功div、input的规律你就明白为什么要这么改写了。
*/
$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;
}
}
视图用custom属性
= 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, //custom属性放这
'attributes' => $attributes,
'deleteOptions'=>[
'class'=>"prohibit deletet",
'label'=>'',
],
'enableEditMode'=>true,
'fadeDelay'=>0, //关闭动画
]) ?>
$custom的数组格式如下:
Array
(
[id] => ClassForm
[attributes] => Array
(
[0] => Array
(
[attribute] => add_time
[value] =>
[label] => 提交时间
[format] => text
)
[1] => Array
(
[attribute] => nickname
[value] => 测试姓名
[label] => 昵称
[format] => text
)
[2] => Array
(
[attribute] => sort
[value] =>
[label] => 联系人分类
[format] => text
)
[3] => Array
(
[attribute] => sex
[value] => 男
[label] => 性别
[format] => text
)
)
)
注意:如果编辑与保存刷新的页面分开写,那么记得也要在刷新的页面加上custom属性,不然保存刷新后加上的自定义字段就消失了。
总结分析
以上就是我要给大家分享的DetailView插件结合自定义字段的显示与编辑的实现过程。
搞程序,水很深,不常总结,将来更费劲。
(完)
相关资料
1、DetailView