How to make dependent dropdownlist in Yii2? - yii2

I'm trying to make a dependend dropDownList in Yii2. I'm trying to use DepDrop Widget, but I can't understand how to edit the code according to my situation. I have 1 model and inside of it I need to make category dropdown list and according to the category_id, the next dropDownList should be Item. (F.e If I select category 1, the Item should be Item1 and so on).
I guess that extension only can do dropdowns of the same model? I'm new to Yii2, so.
My view file:
<div class="site-create">
<?php $form = ActiveForm::begin(); ?>
<?= $form->field($model, 'code') ?>
<?= $form->field($category, 'id')->dropDownList($category, ['id'=>'category-id']); ?>
<?= $form->field($item, 'subcat')->widget(DepDrop::Item(), [
'options'=>['id'=>'item-id'],
'pluginOptions'=>[
'depends'=>['category-id'],
'placeholder'=>'Select...',
'url'=>Url::to(['/site/subcat'])
]
]); ?>
My $model is different model from $category and $item. I set those variables to use a different model in the action
Here is my action:
public function actionSubcat() {
$category = new Category();
$item = new Item();
$out = [];
if (isset($_POST['depdrop_parents'])) {
$parents = $_POST['depdrop_parents'];
if ($parents != null) {
$cat_id = $parents[0];
$out = self::getSubCatList($cat_id);
echo Json::encode(['output'=>$out, 'selected'=>'']);
return $this->render('create', [
'category' => $category,
'item' => $item,
]);
}
}
echo Json::encode(['output'=>'', 'selected'=>'']);
}
}
Now I'm getting the error message that the $category variable is undefined. Could someone explain me what I'm doing wrong?

Actually you dont need any Widgets or Plugins. In yii2 you can easily do Dependent Dropdown list here is example:
echo $form->field($search,'category')->dropDownList(
ArrayHelper::map(
\app\models\Category::find()->all(),
'id','name'
),
[
'prompt' => Yii::t('app','choose_city'),
'onchange'=>'
$.get( "'.Yii::$app->urlManager->createUrl('site/dropdown?id=').'"+$(this).val(), function( data ) {
$( "select#subcat" ).html( data );
})'
]
);
echo $form->field($search, 'sub_category', ['inputOptions'=>['id'=>'subcat',]])->dropDownList([]);
after that you need to create action for returning action. Example:
public function actionDropdown($id)
{
$countPosts = \app\models\SubCategory::find()
->where(['category_id' => $id])
->count();
$posts = \app\models\SubCategory::find()
->where(['category_id' => $id])
->orderBy('name ASC')
->all();
echo "<option value=''>-</option>";
if($countPosts>0){
foreach($posts as $post){
echo "<option value='".$post->id."'>".Yii::t('app',$post->name)."</option>";
}
}
}
thats all.

i suggest using knockoutjs for doing this. i answered a similar question here
knockout example is here (category > product)
html:
<select data-bind="options: data, value: selected, optionsText: 'title', optionsCaption: '---'"></select>
<select data-bind="options: dependent"></select>
javascript:
<script>
function viewmodel() {
var self = this;
this.data = [
{ title: 'title a', items: ['a1', 'a2', 'a3'] },
{ title: 'title b', items: ['b1', 'b2', 'b3'] },
{ title: 'title c', items: ['c1', 'c2', 'c3'] }
];
this.selected = ko.observable();
this.dependent = ko.computed(function() {
return ((self.selected() || {}).items || []);
});
}
$(document).ready(function() {
ko.applyBindings(new viewmodel());
});
</script>
you can still use $form->field, just add 'data-bind' attribute as inputOption.
if you want to use the field's default values, Json::encode those key-value-pairs and create the viewmodel like this:
ko.applyBindings(new viewmodel(<?= Json::encode($data) ?>));
using those parameters is javascript manual labour

<?= $form->field(**$category**, 'id')->dropDownList(**$category_dropdown**, ['id'=>'category-id']); ?>
Both the names are not same:
$category = your model
$category_dropdown = arrayhelper variable in your action
like below
$transactioncategory = ArrayHelper::map(Transactioncategory::find()->all(), 'id', 'transaction_category');

Related

Dependend dropdowns not refreshing properly in Yii2

I have a problem that's driving me mad. I have two Select2-Dropdowns showing employees, one for dayshift, the other for nightshift. The employee selected for dayshift should be removed from the nightshift dropdown and vice versa. I managed to refresh the nightshift dropdown when an employee is selected in the dayshift dropdown, but when I select an employee in the nightshift dropdown he isn't removed from the dayshift dropdown...
here's the snippet from the _form.php:
<?= $form->field($model, 'customer_id')->widget(Select2::classname(), [
'options'=>['id'=>'customer-id','class'=>'customer-id shift-name'],
'disabled' => !$create_jn,
'data' => ArrayHelper::map(customer::find()->where(['active_yn' => 1])->orderBy(['name'=>SORT_ASC])->all(),'customer_id','name')
]); ?>
<?=
$form->field($model, 'site_id')->widget(DepDrop::classname(), [
'options'=>['id'=>'site-id','class'=>'site-id shift-name'],
'disabled' => $disable_yn,
'type' => DepDrop::TYPE_SELECT2,
'pluginOptions'=>[
'depends'=>['cutomer-id'],
'placeholder'=>'Please choose',
'url'=>Url::to(['/site/customer-site']),
// 'initialize'=>true,
]]);
?>
<?= $form->field($model, 'site_employee_id')->widget(DepDrop::classname(), [
'options'=>['id'=>'site-employee-id'],
'disabled' => $disable_yn,
'type' => DepDrop::TYPE_SELECT2,
'pluginOptions'=>[
'depends'=>['customer-id'],
'placeholder'=>'Please choose',
'url'=>Url::to(['/site/customer']),
'params' => ['site-employee-night-id'],
// 'initialize'=>true,
]
]); ?>
<?= $form->field($model, 'bst_leiter_nacht_id')->widget(DepDrop::classname(), [
'options'=>['id'=>'site-employee-night-id'],
'disabled' => $disable_yn,
'type' => DepDrop::TYPE_SELECT2,
'pluginOptions'=>[
'depends'=>['customer-id','site-employee-id'],
'placeholder'=>'Please choose',
'url'=>Url::to(['/site/customer-night']),
'params' => ['site-employee-id'],
'initialize'=>true,
]
]); ?>
and here's the controller:
public function actionCustomer() {
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$out=array();
if (isset($_POST['depdrop_parents'])) {
$parents = $_POST['depdrop_parents'];
if (isset($_POST['depdrop_params'])) {
$params = $_POST['depdrop_params'];
if ($parents != null) {
$customer_id = $parents[0];
if($params != null) {
$site_employee_night_id = $params[0];
$out= \frontend\models\Employee::find()
->where(['customer_id'=>$customer_id])
->andWhere(['<>','employee_id', $site_employee_night_id])
->select(['employee_id as id','CONCAT(firstname," ",lastname) as name'])
->orderBy('lastname')->asArray()->all();
return ['output'=>$out, 'selected'=>''];
}
}
}
}
return ['output'=>'', 'selected'=>''];
}
public function actionCustomerNight() {
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$out=array();
if (isset($_POST['depdrop_parents'])) {
$parents = $_POST['depdrop_parents'];
if (isset($_POST['depdrop_params'])) {
$params = $_POST['depdrop_params'];
if ($parents != null) {
$customer_id = $parents[0];
if($params != null) {
$site_employee_id = $params[0];
$out= \frontend\models\Employee::find()
->where(['customer_id'=>$customer_id])
->andWhere(['<>','employee_id', $site_employee_id])
->select(['employee_id as id','CONCAT(firstname," ",lastname) as name'])
->orderBy('lastname')->asArray()->all();
return ['output'=>$out, 'selected'=>''];
}
}
}
}
return ['output'=>'', 'selected'=>''];
}
Any help, tips, hints would be highly appreciated.
The problem might be that night shift dropdown depends on dayshift dropdown and gets refreshed when dayshift one is changed. Since dayshift dropdown depends only on customer-id it won't be refreshed (function in controller wont refresh data) when you change night shift dropdown.
On nightshift add onchange event and trigger refresh manually

Dependent Dropdown in Yii2 lost values on Update

I created a form with a dropdownlist dependent on another (without extensions) and it ran fast.
But when you try to change the same form, the value of the second drop-down list is lost (type_id). Basically the Type field depends on the selected Area. See my code:
_form.php
<?= $form->field($model, 'area_id')->dropDownList(ArrayHelper::map(Department::find()->where(['helpdesk' => 1])->all(),'id','name'),['prompt'=>'Selecione a Área',
'onchange' => '
$.post("index.php?r=helpdesk/solicitation/lists&id=' . '"+$(this).val(),function(data){
$("select#solicitation-type_id").html(data);
});']) ?>
<?php
echo $form->field($model, 'type_id')->dropDownList(['prompt'=>'Selecione a Área']);
?>
Controller
public function actionLists($id)
{
$countType = Type::find()
->where(['area_id' => $id])
->count();
$types = Type::find()
->where(['area_id' => $id])
->orderBy(['name' => SORT_ASC])
->all();
if($countType > 0 )
{
foreach($types as $type ){
echo "<option value='".$type->id."'>".$type->name."</option>";
}
}
else{
echo "<option> - </option>";
}
}
I think it's the same problem at this topic, but I did not understand how it worked.
you need to provide the default set of options from which it should select the saved option whereas you are not providing any data
<?php
$types = Type::find()
->where(['area_id' => $model->area_id])
->orderBy(['name' => SORT_ASC])
->all();
echo $form->field($model, 'type_id')->dropDownList(ArrayHelper::map($types,'id','name'),['prompt'=>'Selecione a Área']);
?>
Now when you will use findOne() and select the model the saved value for the type_id will be automatically selected.
You can move the above query in the controller/action and pass the $types to the view.
also you can make you method actionLists D.R.Y by using count($types) and remove the first query
public function actionLists($id)
{
$types = Type::find()
->where(['area_id' => $id])
->orderBy(['name' => SORT_ASC])
->all();
if(count($types) > 0 )
{
foreach($types as $type ){
echo "<option value='".$type->id."'>".$type->name."</option>";
}
}
else{
echo "<option> - </option>";
}
}

How to create a new input field which would offer values stored in different database table? Yii2

It's pretty hard to formulate the question correctly, but I'll try to explain it more here. I have an Item, Category and Sale models. In category and Item models I manually stored data. Now I've created a form, which has some input fields and dependent drop-down, which is:
According to the selected category, items are loaded.
Now I need to create a new input field, which would be price and it has to be taken from the database as a offer (f.e if the user selects Item1 -> Item1 price has to be loaded up as a offer in the input field, which I could edit it and store in different database table.
How should I do that?
Here is my SaleController action:
/**
* Creates a new sale
*
* #return string
*/
public function actionCreate()
{
$model = new Sale();
$model->scenario = Sale::SCENARIO_CREATE;
$category = new Category();
$category->scenario = Category::SCENARIO_CREATE;
$categoryList = ArrayHelper::map(Category::find()->all(), 'id', 'name');
if ($model->load(Yii::$app->request->post())) {
if ($model->save()) {
return $this->redirect(['index']);
}
}
return $this->render('create', [
'model' => $model,
'category' => $category,
'categoryList' => $categoryList,
]);
}
And here is my view:
<?= $form->field($category, 'id')->dropDownList($categoryList, [
'id' => 'category-id',
'prompt' => 'Choose a category',
]); ?>
<?= $form->field($model, 'item_id')->widget(DepDrop::classname(), [
'options'=>['id'=>'item-id'],
'pluginOptions'=>[
'depends'=>['category-id'],
'placeholder'=> 'Choose an item',
'url'=>Url::to(['/sale/subcat'])
]
]); ?>
<?= $form->field($model, 'price') ?>
Thank you for any help. I hope you understand the question
First you must add an input field in your view from model:
<?= $form->field($model, 'item_price')->textInput([
'maxlength' => true,
'id' => 'input-price-id'
]) ?>
Then you need to add some javascript:
<?php
$item_price=<<<JS
$('#item-id').on('change', function(event) {
var id = $(this).attr('id');
var val = $(this).val();
.get(
// you must code function actionRefreshPrice wtih parameter named item_id in
// your controller with fetch from any table you want
'refresh-price',
{
item_id: val // this is id from your item
},
function (data) {
// here you set a value returned from ajax call
// you must have an input element with id input-price-is (change as you like)
$('#input-price-id').val(data);
}
);
});
JS;
$this->registerJs($item_price);
?>
Then you must add a controller action something like this:
public function actionRefreshPrice($item_id) {
// I asume you have table with Price for items prices
$price = Price::findOne(['item_id'=>$item_id]);
return ($price?0:$price->price_field);
}
I hope I have given you enough guidelines. And a comment learn more about models and relations. I think you overthought problem a little bit. Happy learning and coding.

Style jQuery autocomplete to add image, and extra fields / variables

I want to style the field and its dropdown of jQuery autocomplete, but the way around is only through the controller I suppose because there, I'm able to concat the variables I need and send it to the jQuery autocomplete on the Blade file. But concatenating only echoes out the variables as a field, I wish to style the output like it is on the image attached to this post. How do I achieve this?
This is the controller:
public function SelectLocationPlaces(Request $userAdressRequest)
{
$term = $userAdressRequest->term;
$data = LocationPlaces::where('name', 'LIKE', '%'.$term.'%')->take(10)->get();
$countries = Country::all();
$countryFlag = CountryFlag::all();
$results = array();
foreach ($data as $key => $value) {
foreach ($countries as $key => $values) {
if($value->country_id === $values->iso_alpha2){
$countrySelected = $values->name;
$selectedCapital = $values->capital;
}
}
foreach ($countryFlag as $key => $valueFlag) {
if($countrySelected === $valueFlag->name){
$countryPerFlag = $valueFlag->flag;
$countryCurrency = $valueFlag->currency;
}
}
$results[] =
**
['value'=>$value->name .$value->timezone_id .$countrySelected, ];
**
}
return Response::json($results);
}
This is the Blade file:
<DIV align="center"><SPAN class="div-header">USER ADDRESS</SPAN></DIV>
<div>{!! Form::text('city_town', Input::old('city_town'), ['class'=>'css-classes', 'placeholder'=>'area/city', 'id'=>'city_town']) !!}</div>
<div>{!! Form::text('state_province_region', Input::old('state_province_region'), ['class'=>'css-classes', 'placeholder'=>'state/province', 'id'=>'state_province_region']) !!}</div>
<script type="text/javascript">
$('#city_town').autocomplete({
source: '{{ url('json/redirects') }}',
minLength: 1,
autofocus: true,
select: function(e, ui){
$('#state_province_region').val(ui.item.state_province_region);
$('#name').val(ui.item.name);
}
});
</script>

Dependent fields yii2

I need 2 dependent fields in framework Yii2 Example:
I want when i insert in "Numero de Personas"= 5 or other numeric value, automatically "Consumo de Agua"= 2 * "Numero de Personas" = 10.
I saw onchange yii2 but i don't know how use it, i think onchange yii2 is an alternative.
<?= $form->field($model, 'NUM_PERSONAS')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'AGUA_CONSUMO')->textInput(['maxlength' => true]) ?>
Update:
With 2 calculated fields example:
$("#consumo-form #{$aguaId},#consumo-form #{$personaId}").on("keyup", function (e) {
var persona = $("#consumo-form #{$personaId}").val();
var agua = $("#consumo-form #{$aguaId}").val();
$("#consumo-form #{$indConsumoAguaM}").val(persona*agua)
});
Here is the approach i would use:
in your view:
<?php
$personaId = Html::getInputId($model, 'NUM_PERSONAS');
$aguaId = Html::getInputId($model, 'AGUA_CONSUMO');
?>
<!-- Begin of form -->
<?= $form->field($model, 'NUM_PERSONAS')->textInput(['maxlength' => true]) ?>
<?= $form->field($model, 'AGUA_CONSUMO')->textInput(['maxlength' => true]) ?>
<!-- End of form -->
<?php
$js = <<<JS
$("#my-form-id #{$personaId}").on("keyup", function (e) {
var persona = $(this).val();
$("#my-form-id #{$aguaId}").val(persona*2)
});
$("#my-form-id #{$aguaId}").on("keyup", function (e) {
var agua = $(this).val()%2 == 0 ? $(this).val() : $(this).val() - 1;
$("#my-form-id #{$personaId}").val(agua/2)
});
JS;
$this->registerJs($js);
With that, both fields will try to auto-complete after the other being changed. But, just to be safe, we need to add a validation on model's rules also.
In your Model:
public function rules()
{
return [
// Your rules
[['NUM_PERSONAS', 'AGUA_CONSUMO'], 'checkValues']
];
}
public function checkValues($attribute)
{
$persona = $this->NUM_PERSONAS;
$agua = $this->AGUA_CONSUMO%2 == 0 ? $this->AGUA_CONSUMO : $this->AGUA_CONSUMO - 1;
if ($persona != $agua/2) {
$this->addError($attribute, 'Error message');
}
}