kartik-v / yii2-widget-select2

Enhanced Yii2 wrapper for the Select2 jQuery plugin (sub repo split from yii2-widgets).
http://demos.krajee.com/widget-details/select2
Other
323 stars 145 forks source link

Select2 JS variables not included in response to an AJAX request when using renderAjax() #211

Closed arturszulc closed 7 years ago

arturszulc commented 7 years ago

Let me explain this matter by example.

I have a page where there are 4 static Select2 widgets and all of them use specific configuration arrays in the head section of the page:

<script type="text/javascript">
    /* ... */
    var s2options_7ebc6538 = { /* global configuration array */ };
    window.select2_a521b087 = { /* configuration for widget #1 */};
    window.select2_e805b9ef = { /* configuration for widget #2 */};
    window.select2_6f2127b7 = { /* configuration for widget #3 */};
    window.select2_f7c19e35 = { /* configuration for widget #4 */};
    /* ... */
</script>

On the same page I have a button which sends an AJAX request to a controller, that renders some additional inputs with its own Select2 configuration array. In the controller action code I am using renderAjax() call to render view. When I preview the raw response from the controller I see:

<!-- some rendered html -->
<!-- ... -->
<select id="#myform-0-attribute" class="form-control"
                    name="MyModel[0][attribute]" data-s2-options="s2options_7ebc6538"
                    data-krajee-select2="select2_6059d464" style="display:none">
<!--
...
some asset scripts
initialization script
-->

Reply from controller contains also an initialization script, as follows:

var $el = jQuery("#w0 .kv-hint-special");
if ($el.length) {
    $el.each(function () {
        $(this).activeFieldHint()
    });
}
if (jQuery('#myform-0-attribute').data('select2')) {
    jQuery('#myform-0-attribute').select2('destroy');
}
jQuery.when(jQuery('#myform-0-attribute').select2(select2_6059d464)).done(initS2Loading('myform-0-attribute', 's2options_7ebc6538'));
var inputmask_ffb566cf = {"alias": "currency", "prefix": false};
jQuery("#myform-0-attribute2").inputmask(inputmask_ffb566cf);

The problem is that widget init script is properly returned, however variable select2_6059d464 does not exists anywhere and is not returned in response to the AJAX request. Therefore select2 widget associated with that variable is not loading. So when you look at the JavaScript console you see:

Uncaught ReferenceError: select2_6059d464 is not defined

As you can see, builtin Masked Input widget returns its own configuration array and therefore is properly rendered when using renderAjax().

And similar, when there is no static Select2 widgets on page, so no s2options_7ebc6538 variable would exist - an additional error will be raised.

The solution is to register these variables and return them when using renderAjax().

Would you kindly look into this?

kartik-v commented 7 years ago

The purpose of registering these variables in POS_HEAD was to ensure they can be parsed by JS by any other element on the page at runtime via JS (e.g. check other third party extensions like yii2-dynamic-form)

Registering in POS_READY like masked input will ensure render ajax works but may break scenarios where the variable is being referred earlier but the value has been reassigned due to renderAjax and updated value has not been used.

Will figure out a solution to check this.

arturszulc commented 7 years ago

I think adding

(\Yii::$app->request->isAjax) ? $this->registerWidgetJs("window.{$this->_hashVar} = {$encOptions};\n", View::POS_READY) :
            $this->registerWidgetJs("window.{$this->_hashVar} = {$encOptions};\n", View::POS_HEAD);

in kartik\base\WidgetTrait\registerPluginOptions() and

(\Yii::$app->request->isAjax) ? $view->registerJs("var {$this->_s2OptionsVar} = {$options};", View::POS_READY) : $view->registerJs("var {$this->_s2OptionsVar} = {$options};", View::POS_HEAD);

in kartik\select2\Select2\registerAssets() should do the trick. There might be a better way, however I am not familiar with writing Yii2 widgets/plugins.

kartik-v commented 7 years ago

I am thinking of adding a property to the widget to control this and let developer set for the widget what he/she wants.

kartik-v commented 7 years ago

Resolved via kartik-v/yii2-krajee-base#79. New property hashVarLoadPosition available in all widgets. Defaults to View::POS_HEAD. You can set it to View::POS_READY if needed.

So in your select2 widget rendering code you can set like:

echo Select2::widget([
    'name' => 'something',
    'data' => $data,
    'hashVarLoadPosition' => View::POS_READY
]);