chandee77
chandee77

Reputation: 37

Yii2 Using Select2 with tabular step of yii2-formwizard

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

Upvotes: 1

Views: 1365

Answers (2)

Francesco Simeoli
Francesco Simeoli

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:

  • 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

                    ]
                ]
            ]

        ]
    ]
);

Upvotes: 1

Muhammad Omer Aslam
Muhammad Omer Aslam

Reputation: 23778

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.

Upvotes: 1

Related Questions