Reputation: 37
I am using yii2-formwizard which is a handy tool in my project with kartik\select2
. Everything is work perfectly except when I press add to get next group select2
drop-down on previous groups are disappeared.
This has happen when I modify my controller to capture data from my model as explained in my previous post, I have missed something in scripting side I'm bit poor in jquery/JS etc, anyhow except that data is being saved and widget work perfectly
<?php
public function actionCreatemulti()
{
$this->layout = 'layout2';
$model = [new Todelete()];
$sex = [['id' => 1, 'name' => 'male'], ['id' => 2, 'name' => 'female']];
if (Yii::$app->request->isPost) {
$count = count(Yii::$app->request->post('Todelete', []));
//start the loop from 1 rather than 0 and use the $count for limit
for ($i = 1; $i < $count; $i++) {
$model[] = new Todelete();
}
if (Model::loadMultiple($model, Yii::$app->request->post()) && Model::validateMultiple($model)) {
foreach ($model as $md) {
$md->save(false);
}
return $this->render('index');
}
}
return $this->render('create', [
'model' => $model,
'sex' => $sex
]);
}
echo FormWizard::widget(
[
'formOptions' => [
'id' => 'my_form_tabular'
],
'steps' => [
[
//should be a single model or array of Activerecord model objects but for a single model only see wiki on github
'model' => $model,
//set step type to tabular
'type' => FormWizard::STEP_TYPE_TABULAR,
'fieldConfig' => [
'sex' => [
'widget' => Select2::class,
'containerOptions' => [
'class' => 'form-group'
],
'options' => [
'data' => $data,
'options' => [
'class' => 'form-control'
],
'theme' => Select2::THEME_BOOTSTRAP,
'pluginOptions' => [
'allowClear' => true,
'placeholder' => 'Select sex'
]
],
//set tabular events for select2 fix which doesnot work correctly after cloning
'tabularEvents' => [
'beforeClone' => "function(event, params){
//fix for select2 destroy the plugin
let element = $(this);
element.select2('destroy');
}",
"afterClone" => "function(event, params){
//bind select2 again after clone
let element = $(this);
let elementId = $(this).attr('id');
let dataKrajee = eval(element.data('krajee-select2'));
let dataSelect2Options = element.data('s2-options');
$.when(element.select2(dataKrajee)).done(initS2Loading(elementId, dataSelect2Options));
}",
"afterInsert" => "function(event,params){
//initialize the options for the select2 after initializing
//changed according to my environment
let selectElement = $(this).find('.field-todelete-'+params.rowIndex+'-sex > select');
let dataKrajee = eval(selectElement.data('krajee-select2'));
selectElement.select2(dataKrajee);
}"
]
]
]
]
]
]
);
https://cdn1.imggmi.com/uploads/2019/8/31/158dc0f338e0d780747c5f72fa2ed6bb-full.png https://cdn1.imggmi.com/uploads/2019/8/31/4e394e87aa162d3f457c32af8d30373b-full.png
Upvotes: 1
Views: 1365
Reputation: 541
This usually happens when there is a collision of ids using widgets. Check by inspecting the HTML page that this does not happen. In particular, dwell on these sections (this code is only an example):
<Block1>
<select id = "todelete-0-sex">
</Block1>
<Block2>
<select id = "todelete-1-sex">
</Block2>
<Script>
// ...
$("# Todelete-0-sex").select2({...});
// ...
$("#Todelete-1-sex").select2({...});
// ...
</Script>
I have replicated your code on a clean installation of Yii2 (2.0.25), using the two components:
with some minor changes not relevant to make the code work and everything seems ok.
Recap
MyController.php (controller)
...
public function actionTest()
{
//$this->layout = 'layout2';
$model = [new Todelete(['id' => 1, 'name' => 'a', 'sex' => 'male']), new Todelete(['id' => 2, 'name' => 'b', 'sex' => 'male']), new Todelete(['id' => 3, 'name' => 'c', 'sex' => 'female'])];
$sex = [['id' => 1, 'name' => 'male'], ['id' => 2, 'name' => 'female']];
if (Yii::$app->request->isPost) {
$count = count(Yii::$app->request->post('Todelete', []));
//start the loop from 1 rather than 0 and use the $count for limit
for ($i = 1; $i < $count; $i++) {
$model[] = new Todelete();
}
if (Model::loadMultiple($model, Yii::$app->request->post()) && Model::validateMultiple($model)) {
foreach ($model as $md) {
$md->save(false);
}
return $this->render('index');
}
}
return $this->render('test', [
'model' => $model,
'sex' => $sex
]);
}
...
Todelete.php (model)
...
use yii\base\Model; // NOTE: in your case your model will most likely extend ActiveRecord instead of Model class
class Todelete extends Model
{
public $id;
public $name;
public $sex;
/**
* @inheritdoc
*/
public function rules()
{
return [
['id', 'integer'],
[['sex', 'name'], 'string'],
];
}
}
...
create.php (view)
use kartik\select2\Select2;
use \buttflattery\formwizard\FormWizard;
echo FormWizard::widget(
[
'formOptions' => [
'id' => 'my_form_tabular'
],
'steps' => [
[
//should be a single model or array of Activerecord model objects but for a single model only see wiki on github
'model' => $model,
//set step type to tabular
'type' => FormWizard::STEP_TYPE_TABULAR,
'fieldConfig' => [
'sex' => [
'widget' => Select2::class,
'containerOptions' => [
'class' => 'form-group'
],
'options' => [
//'data' => $data,
'options' => [
'class' => 'form-control'
],
'theme' => Select2::THEME_BOOTSTRAP,
'pluginOptions' => [
'allowClear' => true,
'placeholder' => 'Select sex'
]
],
//set tabular events for select2 fix which doesnot work correctly after cloning
]
]
]
]
]
);
Upvotes: 1
Reputation: 23778
The issue you have pointed out does exist, and you are right about it. But this issue is related with the recent change to the kartik\select2 @V2.1.4
.The demo links are using an older version of the select2 V2.1.3
which dont have this dataset
attribute defined, and hence works correctly.
The widget does not integrate all these changes and leaves on the user who is integrating the widget
The reason is that it won't be possible to control it correctly inside the plugin as there could be any widget a user wants to use and going to keep adding the code for every other widget isn't what I would vote for. So a better approach would be to provide the event triggers for the specific actions which require a pre or post-processing of an element, where according to the needs the user can adjust his code
Now about the issue, there is a new dataset attribute data-select2-id
which holds the name of the input that the select2 is bind to and after cloning the new element that attribute is not updated to the newer element id which is causing your old select element to vanish.
See the image below it is from my own code so just ignore the address-0-city
field name as it is not related to your code and is for understanding only
So we need to change the code in the afterInsert
event to the below one
let selectElement = $(this).find('.field-todelete-'+params.rowIndex+'-sex > select');
let dataKrajee = eval(selectElement.data('krajee-select2'));
//update the dataset attribute to the
if (typeof selectElement[0].dataset.select2Id !== 'undefined') {
//get the old dataset which has the old id of the input the select2 is bind to
let oldDataSelect2Id = selectElement[0].dataset.select2Id;
//delete the old dataset
delete selectElement[0].dataset.select2Id;
//add the new dataselect pointing to the new id of the cloned element
let newDataSelect2Id = oldDataSelect2Id.replace(
/\-([\d]+)\-/,
'-' + parseInt(params.rowIndex) + '-'
);
//add the new dataset with the new cloned input id
selectElement[0].dataset.select2Id= newDataSelect2Id;
}
selectElement.select2(dataKrajee);
I will update the code on the wiki docs and the code samples along with the demos too in next few days.
Hope it helps you out.
Upvotes: 1