Using Yii2, I'm trying to create a detailView. I want to hide empty rows, and therefore I use the kartik-v detailview. However, I also want to hide attributes if they conform to a certain condition. So I stumbled across this SO question, which captures the intention of my question. It does not, however, answer it satisfactory. (This question asks roughly the same thing). An example
<?= DetailView::widget([
'hideIfEmpty' => true, //available in kartik's detailview
'model' => $model,
'attributes' => [
'id',
'name', //cant be null, always shown
'description:ntext', //can be null, so hidden thanks to kartiks detailview
isAdmin() ? "password" :"", //an example, of course
"hypotheticalOtherField",
isAdmin() ? [
'attribute'=>'client',
'format'=>'raw',
'value'=>function($object) {
return Html::button("MyButton".$object->client);
}
] : ""
]
]) ?>
As you can see, I want to show some fields based on (in this example) whether or not the user is admin. Sadly, inserting emtpy strings, empty arrays, or null values into the attributes array if the condition isn't met, produces an error (IE The attribute must be specified in the format of "attribute", "attribute:format" or "attribute:format:label" when inserting empty strings)
I suppose I could create the attributes array like this:
$attrs = ['id','name','description:ntext'];
if (isAdmin()) array_push($attrs, "password");
array_push($attrs, "hypotheticalOtherField");
if (isAdmin()) array_push($attrs, [
'attribute'=>'client',
'format'=>'raw',
'value'=>function($object) {
return Html::button("MyButton".$object->client);
}
]);
echo DetailView::widget([
'hideIfEmpty' => true, //available in kartik's detailview
'model' => $model,
'attributes' => $attrs
]);
but then the overview with the standard Yii2 code layout is severely undermined.
So is there some way to conditionally insert values into an array, so I can keep coding Yii-style: estetic, organized, and uncluttered? Or maybe a values from which Yii2 knows it should be skipped when creating the View
You can use visible to DetailView
<?= DetailView::widget([
'hideIfEmpty' => true, //available in kartik's detailview
'model' => $model,
'attributes' => [
'id',
'name', //cant be null, always shown
'description:ntext', //can be null, so hidden thanks to kartiks detailview
[
'visible' => (isAdmin() ? true : false),
'value' => $model->password,
'label' => 'test'
],
]) ?>
Add whatever condition you want to add!!! in visible
If you try with an array append shorthand and the look of the code is more yii2 stylish
The attribute based on array is a correct (good) practice.
$attrs[] = ['id','name','description:ntext'];
if (isAdmin()) {
$attrs[] = ['password'];
}
$attrs[] = ['hypotheticalOtherField']
if (isAdmin()) {
$attrs[] = [
'attribute'=>'client',
'format'=>'raw',
'value'=>function($object) {
return Html::button("MyButton".$object->client);
}
}
echo DetailView::widget([
'hideIfEmpty' => true, //available in kartik's detailview
'model' => $model,
'attributes' => $attrs
]);
Related
I have an issue with a Gridview using kartik\grid\EditableColumn, after changing the value I am returned the wrong value for the column when it updates. I am returned the dropdown key/main table integer rather than the string contained in a linked table.
I have two tables
Leads - columns id and status_id
Related fields - model, field, related_value, related_value
The relation is based on in this case
model:"Leads",
field:"status_id",
related_id:status_id
I have the following relation in my model
public function getStatus()
{
return $this->hasOne(RelatedFields::className(), ["related_id" => "status_id"])->andOnCondition(["status.field" => "status_id", "status.model"=>"Leads"])->from(["status" => RelatedFields::tableName()]);
}
I also created the following as a test based on this link
public function getStatusValue()
{
return $this->status->related_value;
}
Here is the column code
[
'class' => 'kartik\grid\EditableColumn',
'attribute' => 'status_id',
'value'=>'status.related_value',
//'value' => function($model){ return $model->status->related_value; },
//'value' => function($model){ return $model->StatusValue; },
//'refreshGrid' => true,//Works but not nice
'vAlign'=>'middle',
'hAlign'=>'center',
'pageSummary' => true,
'readonly' => false,
'width'=>'10%',
'filter'=>Html::activeDropDownList($searchModel, 'status', ArrayHelper::map(RelatedFields::Find()->where(['model' =>"Leads","field"=>"status_id"])->all(), 'related_id', 'related_value'),['class' => 'form-control','prompt' => Yii::t('app', '')]),
'editableOptions'=> [
//'attribute'=>'status_id',
//'value'=>'status.related_value',
//'header' => 'profile',
//'format' => Editable::FORMAT_BUTTON,
'inputType' => Editable::INPUT_DROPDOWN_LIST,
'data'=> ArrayHelper::map(RelatedFields::Find()->where(['model' =>"Leads","field"=>"status_id"])->all(), 'related_id', 'related_value'),
]
],
Commented out are a number of lines in my attempts to fix the issue as well as combinations of them, however all result in the wrong value.
If for example I select the related value "New" which has a related_id 1, after the column has been updated I get the value 1 instead of "New".
When the table is first loaded/reloaded the value does show correctly.
I could reload the grid, but this seems wrong just to fix 1% of the data shown on the page.
I your model take a public variable $status_value
create an assigning value method
public function getStatusValue()(){
return $this->status_value= $this->status->related_value;
}
Now in Gridview use getStatusValueenter code heremethod with assigning value as below
use yii\helpers\Url;
$gridColumns = [
[
'class' => 'kartik\grid\EditableColumn',
'attribute' => 'status_value',
'pageSummary' => true,
'readonly' => false,
'value' => function($model){ return $model->statusValue; }, // assign value from getStatusValue method
'editableOptions' => [
'header' => 'status_value',
'inputType' => kartik\editable\Editable::INPUT_TEXT,
'options' => [
'pluginOptions' => [
]
]
],
],
];
If you follow Kartik guide, he suggest to add EditableColumnAction to better handle the editable column:
The EditableColumnAction offers a quick easy way to setup your
controller action for updating, saving and managing the EditableColumn
output from GridView. This action class extends from yii\rest\Action
and hence all properties available with yii\rest\Action are applicable
here. The basic setup of the column involves setting up the controller
action and the EditableColumn.
So you need to add an EditableColumnAction in your controller to handle the update of the model:
public function actions()
{
return ArrayHelper::merge(parent::actions(), [
'edit-lead' => [
'class' => EditableColumnAction::class,
'modelClass' => Leads::class
]
]);
}
In your GridView editable column configuration, include the above
controller action for processing the Editable within editableOptions.
For example
And in your column code you need to add the action to editableOptions property:
'editableOptions' => [
...
'formOptions' => ['action' => ['/leads/edit-lead']]
]
Now, according to the guide, you can add to your action the outputValue property:
'outputValue' => function (Leads $model) {
return $model->status->related_value;
}
I made custom currency formatter + converter based on values from database.
This is how I use it in DetailView
use yii\helpers\Html;
use app\commands\AppHelper;
use yii\widgets\DetailView;
use app\models\Part;
<?= DetailView::widget([
'model' => $model,
'attributes' => [
// ...
[
'attribute' => 'price',
'label' => (new Part())->getAttributeLabel('price_user'),
'format' => [
'currency',
AppHelper::getUserCurrencyCode(),
[
'convert' => true,
'currencyFrom' => $model->currency->code,
'currencyTo' => AppHelper::getUserCurrencyCode(),
],
],
],
// ...
],
]) ?>
In this widget I can accomplish behaviour like this: when there is numeric value, it gets formatted, if there is NULL value, usual (not-set) is printed out...
Notice $model->currency->code which is data from relation, in DetailView easily accessible but I can not figure out how to get that data into formatter in GridView.
Problem is when I want to format data in GridView.
I allow NULL values on column that I need to use formatter on, so I already threw away idea of using
'value' => function ($data, $key, $index, $column) { return $data->value; }
because when NULL value is present, yii sends data like this
<span class="not-set">(not set)</span>
and either I want to let it be or set my custom value (considering different value for other columns with NULL value) and I also want to save trouble handling all those (not set) values.
Another reason is, as I noticed, that if I use 'format' => ... in attribute params, formatting happens before setting those (not set) values.
So I was thinking about somehow passing that $model->currency->code, which is data from relation, to that formatter.
Any ideas? Thanks.
Worst case scenario I will use formatter in value dumping values that contains '<span' or NULL like this, but it is ugly and I dont like it...
EDIT: I added custom static method to format unset data. I still dont like it, but hey, it works ... :D
use yii\helpers\Html;
use app\commands\AppHelper;
use yii\grid\GridView;
use app\models\Part;
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
// ...
[
'attribute' => 'price',
'label' => (new Part())->getAttributeLabel('price_user'),
'value' => function ($data, $key, $index, $column) {
return Part::requestPrice(Yii::$app->formatter->asCurrency(
$data->price,
AppHelper::getUserCurrencyCode(),
[
'precision' => 2,
'convert' => true,
'currencyFrom' => $data->currencyCode,
'currencyTo' => AppHelper::getUserCurrencyCode(),
]));
},
'format' => 'raw',
],
// ...
],
]); ?>
and in Part.php (Part model) I added method
public static function requestPrice($price)
{
if (strpos($price, 'class') !== false || empty($price) || floatval($price) == 0)
return '<span class="not-set">' . Yii::t('app', 'na vyĹžiadanie') . '</span>';
else
return $price;
}
I am new to yii2 and trying to get around. I have a dropdownlist whose values in the database are enum. So when the crud was created the dropdownlist had the enum values.
But I want to keep one value selected as default in the drop down list.
My form code is below:
<?= $form->field($model, 'priotiy_level')->dropDownList([ 'low' => 'Low', 'medium' => 'Medium', 'high' => 'High', ], ['prompt' => 'Select Priority Level']) ?>
Instead of the prompt, I want to have medium as a selected value. Can someone please help me with this?
Thank you.
After initialization of the $model instance in your controller set the attribute and then pass $model to view.
$model->priority_level = 'medium';
As #Bizley said, you need to set the value of the attribute in your Controller. In Yii2, you can do that with in one line:
public function actionSomething {
$model = new MyClass(['priotiy_level' => 'medium']);
// code
return $this->render('something', [
'model' => $model
]);
}
Additionally to previous answers you can also use default validator:
class SomeActiveRecord extends ActiveRecord {
// ...
function rules(){
return [
['priotiy_level', 'default', 'value' => 'medium']
// set "username" and "email" as null if they are empty
[['username', 'email'], 'default'],
// set "level" to be 1 if it is empty
['level', 'default', 'value' => 1],
];
}
}
More details see here: Handling Empty Inputs.
This code sets default value for the all actions/forms. If you need different default values on different forms, can be used also scenarios of validation.
Give class to your dropdownList :
Ex.
<?= $form->field($model, 'priotiy_level')->dropDownList([ 'low' => 'Low', 'medium' => 'Medium', 'high' => 'High', ], ['class' => 'priority_list','prompt' => 'Select Priority Level']) ?>
Give Default value using Java Script or Jquery
Ex.
<script>
$(".priority_list").val('medium'); // assing value using jquery
</script>
You can also use ID:
Ex.
<script>
var temp=document.getElementById('project-industry_id');
temp.value='medium';
</script>
Here I got 2 views, first view is a form which let user to register. Then save information to DB in controller and refer to the other view.
I write a checkbox list in first view.
<?= $form->field($model, 'items[]')->checkboxList(['a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C']); ?>
Then I tried to get the value from it in controller and save to DB.
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
//save to DB
$model = new EntryForm();
$tableMember=new Members;
$tableMember->select=$model-> items ;
$tableMember->save();
return $this->render('entry-confirm', ['model' => $model]);
}
to show in entry-confirm.php
<li><label>Selected</label>: <?php echo Html::encode($model->items['a']) ?></li>
but it's empty.
I used NetBeans debugger, it shows:
$_POST = [
'_csrf' => 'OTFHYUpIaVJNSxAJPBEDGV8DcTYjAhojAFofVx0HJmULVCwoAiRENA==',
'EntryForm' => [
'username' => 'df',
'email' => '2#c.c',
'password' => '123',
'items' => [
'a',
'b',
],
'country' => '',
],
];
It seems that items did get attributes. Is there the other way to create checkboxes? Or how can I get the values from a checkbox list?
try this way :
<?php
echo $form->checkBoxList($model,'items',
array('a' => 'Item A', 'b' => 'Item B', 'c' => 'Item C'),
);
?>
this checkbox list should be part of Form Widget, and Items should be your databse field or Variable in your class.First try to check your model fields are coming on your view or not.
Try this one
$tableMember->select = implode(",", $model-> items);
$model->items returns array of checked checkbox.
well, I always have issues getting back arrays with something like
$model->items
(as in values from e.g. checkboxlist), I find it easier to get the values with e.g. $_POST['EntryForm']['items'], like this:
$model->items=implode(',',$_POST['EntryForm']['items']);
(done in the controller, before $model->save())
as an example:
(we split the post and save action)
if ($model->load(Yii::$app->request->post())) {
$model->items=implode(',',$_POST['EntryForm']['items']); //change items into string to be saved
if($model->save()){
return $this->redirect(['view', 'id' => $model->id]);
}
} else {
$model->items=explode(',',$model->items); //string to array to fill the checkboxlist
return $this->render('create', [
'model' => $model,
]);
}
the main issue for $model->items not to work is that probably it is not considered "safe", meaning that it has not been declared in the models under rules (public function rules(), e.g. adding
[['items'], 'string', 'max' => 250],
or
[['items'], 'safe'],
should do the trick....
see also: Yii2 - Models - Safe Attributes
HTH
I have created a check-box input as type Boolean for storing values as dishcharged - checked or unchecked. Checked will store 1 and unchecked will store 0.
Now I want to show the label as Yes or No for value 1 and 0 in grid-view and view. How can achieve that.
my _form.php code is like
$form->field($model, 'discharged')->checkBox(['label' => 'Discharged',
'uncheck' => '0', 'checked' => '1'])
I have tried like
[
'attribute'=>'discharged',
'value'=> ['checked'=>'Yes','unchecked=>'no']
],
but doesn't look like the correct syntax.
Thanks.
As arogachev said, you should use boolean formatter :
'discharged:boolean',
http://www.yiiframework.com/doc-2.0/guide-output-formatter.html
http://www.yiiframework.com/doc-2.0/yii-i18n-formatter.html#asBoolean()-detail
Or you could add a getDischargedLabel() function in your model :
public function getDischargedLabel()
{
return $this->discharged ? 'Yes' : 'No';
}
And in your gridview :
[
'attribute'=>'discharged',
'value'=> 'dischargedLabel',
],
First option:
[
'attribute' => 'discharged',
'format' => 'boolean',
],
or shortcut:
'discharged:boolean',
This does not require additional methods in your model and writing text labels (it will be set automatically depending on language in your config).
See more details here.
Second option:
Instead of writing additional method in model you can just pass closure to value.
You can check details here.
[
'attribute' => 'discharged',
'value' => function ($model) {
return $model->discharged ? 'Yes' : 'No';
},
],
If you consistently display booleans the same way in your app, you can also define a global boolean formatter:
$config = [
'formatter' => [
'class' => 'yii\i18n\Formatter',
'booleanFormat' => ['<span class="glyphicon glyphicon-remove"></span> no', '<span class="glyphicon glyphicon-ok"></span> Yes'],
],
];
Then add your column:
'discharged:boolean',