Yii2 Notes
August 2, 2019

Yii2 Notes

A collection of solutions to unusual issues with Yii2 / Kartik components.

Proper try-catch in controller action

See: https://github.com/yiisoft/yii2/issues/14285

I had the exact same issue: calling a view inside a controller action try-catch block, with some error handling. If the view crapped out then YII2 would generate <![CDATA[YII-BLOCK-HEAD]]><![CDATA[YII-BLOCK-BODY-BEGIN]]> inside of the response.

I guess this is because View.php uses php buffers to store the generated output and it had already put the CDATA tags in the buffer but never got to endPage which would have replaced the CDATA with the actual head/body etc.

So the solution in this case is to run ob_clean(); to clear/erase the output buffer before generating some useful output to handle your error.

So something like this:

```php
public function actionWhatever()
	{
		$retVal = '';
		try {
			$retVal = $this->renderAjax('device-list', [
			]);
		}
		catch (\Exception $e) {
			if ($e instanceof \yii\base\ExitException) {
			    throw $e;	// Thrown by: Application->end(), must rethrow it
			}
			\Yii::$app->errorHandler->logException($e);
			ob_clean();
			$retVal = $this->renderRaw('@app/views/application/systemportfolio/error', ['exception' => $e]);
		}
		return Json::encode($retVal);
	}
```

Select2

Select2 inside a Bootstrap modal

For anyone struggling with a Select2 not working properly inside of a bootstrap modal, you can read details here:

To summarize, the problem is Bootstrap Modal inserting some js that grabs the focus of anything not inside of the modal. The Select2 drop-down is actually rendered outside of the Modal DOM element. There are three ways to fix this:

1) Make sure that the rendered modal does not have tabIndex="whatever" inside of it. This is the solution employed in the example above:

Modal::begin(['options' => ['id' => 'kartik-modal','tabindex' => false...

This is also the "accepted" solution here: https://stackoverflow.com/q...
Unfortunately, it is a hack and has some side-effects like breaking the ESC key to exit the modal, and scrolling issues if the modal is very long.

2) It is also possible to disable the Bootstrap Modal from trying to prevent focus from other elements when it's shown:

// Do this before you initialize any of your modals
$.fn.modal.Constructor.prototype.enforceFocus = function() {};

This is detailed also on the link above as well as here: https://select2.org/trouble...
But again, this is a hack and has side-effects.

3) The PROPER solution is to let Select2 know that it should attach itself to the modal (or one of it's children, like the modal body).
This is also described in the two links above and mentioned by Kartik here: https://github.com/kartik-v...

To implement the proper solution that doesn't break intended bootstrap modal functionality, do this:

echo Select2::widget([ 
    'pluginOptions' => [
        'dropdownParent' => new JsExpression('$("#modalDialogId")'),
        ],
        // your other select2 widget settings
    ]);

(of course you should substitute modalDialogId with the actual ID of the modal dialog)


Kartik DynaGrid

Change the default page size for a given dynagrid

See https://github.com/kartik-v/yii2-dynagrid/issues/81

If you need to set differing default page sizes for your dynagrids (a common scenario, i would think), then unfortunately something like $dataProvider->setPagination(['pageSize' => 100]); does not work because of line 613 in DynaGrid.php:

if (!isset($this->_pageSize) || $this->_pageSize === null) {
    $this->_pageSize = $this->_module->defaultPageSize;
}

You can, however, set a default page size in your configuration:

$config = 
    ['modules' => 
        ['dynagrid'=> 
            [
                'class'=>'\kartik\dynagrid\Module',
                'defaultPageSize'=>20,....

And you can change this "on-the-fly" in your code before generating your datagrid:

\Yii::$app->getModule('dynagrid')->defaultPageSize = 50;
echo DynaGrid::widget([...

Load multiple dynagrids correctly

If you load more than one dynagrid via ajax, then you MUST set unique ID-s in three places:

echo DynaGrid::widget([
    'options'=>[
        'id'=> $gridID . '-dynagrid',
    ],
    'gridOptions' => [
        'options'=>[
            'id'=> $gridID
        ]
    ],
    // Must also set sortableOptions so that the grid configuration "widget" 
    // does not reuse the same ID when multiple widgets are shown:
    'sortableOptions' => [
        'id'=> $gridID . '-dynagrid-widget'
    ],
]);

The "sortableOptions" is required because that is the ID generated for the UL in the grid configuration dialog, which is then referred to by "yii2-sortable". If it is not unique, and you first click on a grid with more columns and then click on a grid with fewer columns, then you will get this error:

VM11037 html5sortable.js:689 Uncaught TypeError: Cannot read property 'items' of undefined
at _enableSortable (VM11037 html5sortable.js:689)
at Function.sortable.enable (VM11037 html5sortable.js:1086)
at VM11037 html5sortable.js:789
at Array.forEach (<anonymous>)
at sortable (VM11037 html5sortable.js:787)
at KvHtml5Sortable.self.<computed> [as enable] (VM11038 kv-html5-sortable.js:54)
at HTMLUListElement.<anonymous> (VM11038 kv-html5-sortable.js:71)
at Function.each (VM10963 jquery.js:362)
at jQuery.fn.init.each (VM10963 jquery.js:157)
at jQuery.fn.init.$.fn.kvHtml5Sortable (VM11038 kv-html5-sortable.js:63)

Relevant code:
1. Line 34-49 (v1.5.1) in yii2-dynagrid/views/config.php, where $options1 is defined
2. yii2-sortable uses the ID i.e. at line 169 (v1.2.2) of Sortable.php: $this->options['id']
3. For whatever reason, DynaGrid.php maps "sortableOptions" to "widgetOptions" around line 1034 (v1.5.1): $model->widgetOptions = $this->sortableOptions;

Yii2 Notes
Share this