1

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

my controller

<?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
    ]);
}

my view

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

Muhammad Omer Aslam
  • 22,976
  • 9
  • 42
  • 68
chandee77
  • 37
  • 6
  • the images you added at the end of your question arent opening and about the select2 you have a working demo [here](https://yii2plugins.omaraslam.com/formwizard/tabular-step) which uses the select2 within the tabular steps to show the working, and the detailed docs are on [GITHUB-WIKI](https://github.com/buttflattery/yii2-formwizard/wiki/Tabular-Steps:-Working-with-3rd-Party-Widgets) you must be missing some class name do you have a live link to the application ? also please look into the console for javascript errors when you press the add button – Muhammad Omer Aslam Aug 31 '19 at 12:53
  • tahnks for your help,I update links & gone through all above mentioned link.i don't get any error in console. – chandee77 Aug 31 '19 at 15:42
  • added an answer for you , and please dont add un-necesary slashes in the question it kills the readability i have rolled-back and edited your question again. – Muhammad Omer Aslam Aug 31 '19 at 16:11

2 Answers2

1

Reason

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

Troubleshooting

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

enter image description here

Solution

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.

Muhammad Omer Aslam
  • 22,976
  • 9
  • 42
  • 68
  • 1
    excellent explanation and it's work perfectly.as you explained I updated kartik/select2 and all widgets through composer this should the caused even I couldn't identified clearly.Again this widget is really useful than other similar implementation as it's super simple and very cleared for beginners. – chandee77 Sep 01 '19 at 03:35
1

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:

  • buttflattery/yii2-formwizard (1.4.6)
  • kartik-v/yii2-widget-select2 (v2.1.3)

with some minor changes not relevant to make the code work and everything seems ok.

screenshot

Recap

  1. verify that there are no conflicts when creating the two widgets
  2. Check the version of the "yii2-formwizard" and "yii2-widget-select2" components

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

                    ]
                ]
            ]

        ]
    ]
);
  • this has nothing to do with what you are talking about the inputs are cloned and the widget provides you the eents to destroy the select2 and then bind again, the above issue is realted to `data-select2-id` attribute that was added in the `v2.1.4` – Muhammad Omer Aslam Aug 31 '19 at 17:17
  • It is correct what you say, however data-select2-id depends on the id of the select element. The kartik widget is just a wrapper around the select2 widget – Francesco Simeoli Aug 31 '19 at 17:22
  • its a tabular step that dynamically generates the inputs , you cant provide the `id` of the inputs yourself it just takes the first input id and adds increment to the current index like `todelete-0-sex` will be converted to `todelete-1-sex` and it is done by the widget script itself when cloning the select or any other inputs – Muhammad Omer Aslam Aug 31 '19 at 17:38
  • and the `id` of the input in the `data-select2-id` needs to be updated for the element in the `afterInsert` event, i am saying all this because i developed this widget and that is how it works – Muhammad Omer Aslam Aug 31 '19 at 17:41
  • ya I updated to newest kartik/select2 widget even I couldn't noticed, that's the root course for this issue.thanks for helping – chandee77 Sep 01 '19 at 03:39