Question

In Magento 2.1, the review_listing UI component contains column configurations that look like this

#File: vendor/magento//module-review/view/adminhtml/ui_component/review_listing.xml
<column name="detail">
    <argument name="data" xsi:type="array">
        <item name="config" xsi:type="array">
            <item name="filter" xsi:type="string">text</item>
            <item name="label" xsi:type="string" translate="true">Review</item>
            <item name="sortOrder" xsi:type="number">50</item>
            <item name="truncate" xsi:type="number">50</item>
            <item name="nl2br" xsi:type="boolean">true</item>
            <item name="escape" xsi:type="boolean">true</item>
        </item>
    </argument>
</column>

There are three arguments (truncate, nl2br, and escape) that don't exist in any other UI Component's configuration.

Additionally, the current grid listing available at Marketing -> Reviews is not a UI Component generated grid listing -- its an old school PHP generated grid. i.e. ui_component/review_listing.xml.

All that leads me to the question: Do the truncate, nl2br, and escape attributes actually do anything? If so, where's this functionality implemented? A cursory search of the KnockoutJS template and RequireJS view models for a UI component don't turn up anything obvious -- but it's pretty easy to miss things in a system this complex.

Was it helpful?

Solution

Looking closely at 2.1, I don't think these arguments are used for now, hopefully they will in future releases.

The only way to acheive the truncate effect in a Ui listing I found so far is to act directly on the collection in your data provider.

Let's assume we have an Item with a description attribute; the listing would look something like this (including just the data source and column):

<dataSource name="item_listing_data_source">
    <argument name="dataProvider" xsi:type="configurableObject">
        <argument name="class" xsi:type="string">My\Module\Ui\DataProvider\Item\ItemDataProvider</argument>
        <argument name="name" xsi:type="string">item_listing_data_source</argument>
        <argument name="primaryFieldName" xsi:type="string">entity_id</argument>
        <argument name="requestFieldName" xsi:type="string">id</argument>
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="update_url" xsi:type="url" path="mui/index/render"/>
            </item>
        </argument>
    </argument>
    <argument name="data" xsi:type="array">
        <item name="js_config" xsi:type="array">
            <item name="component" xsi:type="string">Magento_Ui/js/grid/provider</item>
        </item>
    </argument>
</dataSource>
[...]
    <column name="description">
        <argument name="data" xsi:type="array">
            <item name="config" xsi:type="array">
                <item name="filter" xsi:type="string">text</item>
                <item name="label" xsi:type="string" translate="true">Description</item>
                <item name="sortOrder" xsi:type="number">40</item>
            </item>
        </argument>
    </column>

Here i'm using a real class as data source (not a virtual one from di.xml). The Data source would have a getData() function that would look something like this:

public function getData()
{
    if (!$this->getCollection()->isLoaded()) {
        $this->getCollection()->addFieldToSelect('title');
        $this->getCollection()->addFieldToSelect('description');
        $this->getCollection()->load();
    }
    $items = $this->getCollection()->toArray();

    return [
        'totalRecords' => $this->getCollection()->getSize(),
        'items' => array_values($items),
    ];
}

Now let's truncate that description using SQL statements, the new getData() function would now look like this:

public function getData()
{
    if (!$this->getCollection()->isLoaded()) {
        $this->getCollection()->addFieldToSelect('title');
        $this->getCollection()->addExpressionFieldToSelect('description', "CONCAT(SUBSTRING({{description}},1,60), '...')", 'description');
        // Or this way for an EAV collection:
        // $this->getCollection()->addExpressionAttributeToSelect('description', "CONCAT(SUBSTRING({{description}},1,60), '...')", 'description');
        $this->getCollection()->load();
    }
    $items = $this->getCollection()->toArray();

    return [
        'totalRecords' => $this->getCollection()->getSize(),
        'items' => array_values($items),
    ];
}

A couple of things, first, SUBSTRING starts at index 1 and not 0 like PHP, then as you can see I've concatenated '...' at the end of the now truncated description for aesthetics, the description will now be truncated in your listing:

Listing with truncated description

Conclusion: you need to act on the collection first for Ui listings (for now anyway).

OTHER TIPS

Spending so much time with Knockout and Require mislead you ^^

Basically, this is a PHP feature and not a JS feature.

When you implement a text column the renderer used in Magento\Backend\Block\Widget\Grid\Column\Renderer\Longtext, you can find the renderer types in Magento\Backend\Block\Widget\Grid\Column.php :

protected $_rendererTypes = [
    'action' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Action',
    'button' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Button',
    'checkbox' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Checkbox',
    'concat' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Concat',
    'country' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Country',
    'currency' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Currency',
    'date' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Date',
    'datetime' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Datetime',
    'default' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Text',
    'draggable-handle' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\DraggableHandle',
    'input' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Input',
    'massaction' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Massaction',
    'number' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Number',
    'options' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Options',
    'price' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Price',
    'radio' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Radio',
    'select' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Select',
    'store' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Store',
    'text' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Longtext',
    'wrapline' => 'Magento\Backend\Block\Widget\Grid\Column\Renderer\Wrapline',
];

This LongText class is where the truncate, nl2br and escape are implemented in the render() method:

public function render(\Magento\Framework\DataObject $row)
{
    $truncateLength = 250;
    // stringLength() is for legacy purposes
    if ($this->getColumn()->getStringLimit()) {
        $truncateLength = $this->getColumn()->getStringLimit();
    }
    if ($this->getColumn()->getTruncate()) {
        $truncateLength = $this->getColumn()->getTruncate();
    }
    $text = $this->filterManager->truncate(parent::_getValue($row), ['length' => $truncateLength]);
    if (!$this->getColumn()->hasEscape() || $this->getColumn()->getEscape()) {
        $text = $this->escapeHtml($text);
    }
    if ($this->getColumn()->getNl2br()) {
        $text = nl2br($text);
    }
    return $text;
}

I don't think I need to explain that code to you, the only interesting thing I found is that the default truncate length (even if you don't specify a truncate argument) is hardcoded to 250 characters.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top