Using Yii2’s DetailView in a little bit more advanced way

The simplest and easiest way of rendering DetailView is to declare model and attributes list. This will renders each listed model’s attribute in a cool-looking, responsive table.

This article contains examples and solutions for going a little bit beyond that and using DetailView in a little bit more advanced way.

Values from text area

If you’re displaying values from a <textarea> element, then you most likely want newlines to be converted into line breaks (<br />). To achieve this, simply prepend your attribute name with :ntext phrase on attributes list, for example:

<?= DetailView::widget([
    'model' => $model,
    'attributes' => [
        'code',
        'keywords:ntext',
        'keywords_english:ntext'
    ]
]) ?>

Without this you would end up with all text “glued” toghether, because n characters (as well as all other “white” characters), stored in database, are being ignored when printed inside HTML code. You can read about other formatters, like ntext, in Yii2 docs.

Format value as a link

If you wish to make a clicable link out of URL stored in your model, then one of the easiest ways to do this, is to use following code:

[
    'format' => 'raw',
    'label' => $model->getAttributeLabel('url_main'),
    'value' => Html::a(Html::encode($model->url_main), $model->url_main)
],

Notice nifty usage of $model->getAttributeLabel('url_main') to get label matching model setting even though we’re using raw format here.

Encoding link text using Html::encode($model->url_main) is obligatory here to prevent XSS attacks.

Non-standard or dictionary/lookup-based value

The very same technique you can use to display non standard text related to value stored in DB for particular model’s attribute. For example:

[
    'format' => 'raw',
    'attribute' => 'user_id',
    'value' => $model->getAdminName()
],

Where $model->getAdminName() can be anything like:

public function getAdminName()
{
    if ((int)$this->user_id === 0) {
        return '<em>(n/a)</em>';
    } else {
        return $this->user->name;
    }
}

or anything, you actually want.

Note, that this is an “old” approach to solve this problem, used when you use getAdminName() method in other places in your code. If it is used only for that one particular DetailView then you can consider injecting anonymous function directly into attributes array of DetailView.

Columns width

For default implementation of DetailView columns’ widths / sizes are not set / not controlled by either Yii2 or Twitter Bootstrap framework. It is entirely laid to the browser and thus you can observe, that for different models divider line between two columns (or columns sizes) simply changes, which can produce a not-so-pretty UI effect. To block this, set some permanent values.

You can achieve this by either using CSS styles — i.e. add this to site.css:

table.detail-view th {
    width: 25%;
}

table.detail-view td {
    width: 75%;
}

or by templates and CSS classes:

<?= DetailView::widget([
    'model' => $model,
    'template' => '<tr><th class="col-md-3">{label}</th><td class="col-md-9">{value}</td></tr>',
    'attributes' => [
        'code',
        'keywords:ntext',
        'keywords_english:ntext'
    ]
]) ?>

which allows you (in this example) to use powerful Twitter Bootstrap’s fluid / responsive system.

Filter out empty values

For some developers / end users it is a nasty effect, when DetailView renders rows / attributes with an empty values. You can “fix” this by, again, using template:

'template' => function($attribute, $index, $widget) {
        if(isset($attribute['value']) && $attribute['value'] != '' && $attribute['value'] != ' ') {
            return "<tr><th>{$attribute['label']}</th><td>{$attribute['value']}</td></tr>";
        }
    },

This is also an example of using anonymous function directly in DetailView code, as mentioned above.

Leave a Reply