4

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.

1

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).

2

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.