Are truncate, nl2br, and escape real UI Component Column Configurations
-
01-10-2020 - |
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.
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:
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.