Atualizando a grade padrão do Magento 2 com componentes de interface do usuário (parte 2)
Publicados: 2016-10-18No artigo anterior, descrevemos como criar um módulo com as condições básicas e a interface que nos permitirá trabalhar com elas. Durante o processo de criação, usamos os blocos padrão do Magento. No entanto, o Magento 2 é capaz de fazer muito mais.
Estou falando da possibilidade de melhorar a interface com a ajuda de componentes de interface do usuário. Esses componentes são adicionados com o módulo Magento/UI.
*embora esses componentes possam ser encontrados na v.2.0, recomendamos fortemente que você use a versão 2.1.
Neste artigo, você aprenderá a refazer o Grid padrão (localizado no layout: app/code/Vendor/Rules/view/adminhtml/layout/vendor_rules_example_rule_index.xml ) e enriquecê-lo com componentes de interface do usuário.
Basta comparar. Esta é uma grade antiga:

com um novo, feito com componentes UX:

Como você pode ver, um Grid atualizado será muito mais fácil de usar e economizar tempo, mais fácil de dimensionar, tem muitos recursos extras excelentes (por exemplo, marcadores que são capazes de salvar o estado atual do Grid) e definir filtros inteligentes.
Então vamos começar.
Primeiro, você precisa fazer algumas alterações no módulo. Abaixo está como fazer isso:
1) Crie um novo arquivo para declarar os componentes necessários:
> app/code/Vendor/Rules/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<virtualType name="VendorRulesRuleGridDataProvider" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
<argumentos>
<argument name="collection" xsi:type="object" shared="false">Fornecedor\Rules\Model\ResourceModel\Rule\Collection</argument>
<argument name="filterPool" xsi:type="object" shared="false">VendorRulesRuleGridFilterPool</argument>
</argumentos>
</virtualType>
<virtualType name="VendorRulesRuleGridFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool">
<argumentos>
<argument name="appliers" xsi:type="array">
<item name="regular" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\RegularFilter</item>
<item name="fulltext" xsi:type="object">Magento\Framework\View\Element\UiComponent\DataProvider\FulltextFilter</item>
</argumento>
</argumentos>
</virtualType>
<type name="Fornecedor\Rules\Model\ResourceModel\Rule\Grid\Collection">
<argumentos>
<argument name="mainTable" xsi:type="string">vendor_rules</argument>
<argument name="eventPrefix" xsi:type="string">vendor_rules_rule_grid_collection</argument>
<argument name="eventObject" xsi:type="string">rule_grid_collection</argument>
<argument name="resourceModel" xsi:type="string">Fornecedor\Rules\Model\ResourceModel\Rule</argument>
</argumentos>
</tipo>
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<argumentos>
<argument name="collections" xsi:type="array">
<item name="vendor_rules_rule_listing_data_source" xsi:type="string">Fornecedor\Rules\Model\ResourceModel\Rule\Grid\Collection</item>
</argumento>
</argumentos>
</tipo>
</config>
VendorRulesRuleGridDataProvide — esse tipo virtual fornece dados para a grade de regras da interface do usuário. VendorRulesRuleGridFilterPool , por sua vez, adiciona a funcionalidade de filtragem que permite adicionar/modificar quaisquer filtros existentes.
Observe que, para que o Grid funcione corretamente com essa coleção específica, você precisa adicioná-la à lista de todas as coleções disponíveis. Para fazer isso, adicione vendor_rules_rule_listing_data_source com a classe baseada em valor de coleção: Vendor\Rules\Model\ResourceModel\Rule\Grid\Collection em coleções Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory.
2) Para UI Grid, precisamos de uma coleção separada que representará a interface `Magento\Framework\Api\Search\SearchResultInterface` .
Ele contém os métodos padrão que permitem trabalhar com a Grade e os filtros. Essa classe pode ser personalizada de acordo com suas necessidades pessoais, alterando a maneira como a pesquisa por coleções funciona.
> app/code/Vendor/Rules/Model/ResourceModel/Rule/Grid/Collection.php
<?php
namespace Vendor\Rules\Model\ResourceModel\Rule\Grid;
use Vendor\Rules\Model\ResourceModel\Rule\Collection como RuleCollection;
use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
use Magento\Framework\Data\Collection\Db\FetchStrategyInterface;
use Magento\Framework\Data\Collection\EntityFactory;
use Magento\Framework\Event\ManagerInterface;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
use Psr\Log\LoggerInterface;
class Collection estende RuleCollection e implementa SearchResultInterface
{
/**
* Agregações
*
* @var \Magento\Framework\Search\AggregationInterface
*/
$agregações protegidas;
/**
* construtor
*
* @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
* @param \Psr\Log\LoggerInterface $logger
* @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
* @param \Magento\Framework\Event\ManagerInterface $eventManager
* @param $mainTable
* @param $eventPrefix
* @param $eventObject
* @param $resourceModel
* @param $model
* @param $conexão
* @param \Magento\Framework\Model\ResourceModel\Db\AbstractDb $resource
*/
função pública __construct(
EntityFactory $entityFactory,
LoggerInterface $logger,
FetchStrategyInterface $fetchStrategy,
ManagerInterface $eventManager,
$mainTable,
$eventPrefix,
$eventoObjeto,
$resourceModel,
$model = 'Magento\Framework\View\Element\UiComponent\DataProvider\Document',
$ conexão = null,
AbstractDb $resource = null
) {
parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $connection, $resource);
$this->_eventPrefix = $eventPrefix;
$this->_eventObject = $eventObject;
$this->_init($model, $resourceModel);
$this->setMainTable($mainTable);
}
/**
* @return \Magento\Framework\Search\AggregationInterface
*/
função pública getAgregações()
{
return $this->agregações;
}
/**
* @param \Magento\Framework\Search\AggregationInterface $aggregations
* @return $isto
*/
função pública setAggregations($aggregations)
{
$this->agregações = $agregações;
}
/**
* Recupere todos os ids para coleta
* Compatibilidade retroativa com a coleção EAV
*
* @param int $limit
* @param int $offset
* @return array
*/
função pública getAllIds($limit = null, $offset = null)
{
return $this->getConnection()->fetchCol($this->_getAllIdsSelect($limit, $offset), $this->_bindParams);
}
/**
* Obter critérios de pesquisa.
*
* @return \Magento\Framework\Api\SearchCriteriaInterface|null
*/
função pública getSearchCriteria()
{
retornar nulo;
}
/**
* Definir critérios de pesquisa.
*
* @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
* @return $isto
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
função pública setSearchCriteria(SearchCriteriaInterface $searchCriteria = null)
{
retorne $isto;
}
/**
* Obter contagem total.
*
* @return int
*/
função pública getTotalCount()
{
return $this->getSize();
}
/**
* Defina a contagem total.
*
* @param int $totalCount
* @return $isto
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
função pública setTotalCount($totalCount)
{
retorne $isto;
}
/**
* Definir lista de itens.
*
* @param \Magento\Framework\Api\ExtensibleDataInterface[] $items
* @return $isto
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
função pública setItems(array $items = null)
{
retorne $isto;
}
}
?>3) Modifique a coleção principal da maneira descrita abaixo (é importante, pois nossa coleção personalizada é herdada dela). Você deve fazer as seguintes alterações:
> app/code/Vendor/Rules/Model/ResourceModel/Rule/Collection.php
<?php
namespace Vendor\Rules\Model\ResourceModel\Rule;
class Collection estende \Magento\Rule\Model\ResourceModel\Rule\Collection\AbstractCollection
{
/**
* Defina o modelo de recursos e determine o mapeamento de campos
*
* @return nulo
*/
função protegida _construct()
{
$this->_init('Fornecedor\Rules\Model\Rule', 'Fornecedor\Rules\Model\ResourceModel\Rule');
}
/**
* Filtre a coleta por data especificada.
* Filtre a coleção apenas para regras ativas.
*
* @param string|null $now
* @use $this->addStoreGroupDateFilter()
* @return $isto
*/
função pública setValidationFilter($now = null)
{
if (!$this->getFlag('validation_filter')) {
$this->addDateFilter($agora);
$this->addIsActiveFilter();
$this->setOrder('sort_order', self::SORT_ORDER_DESC);
$this->setFlag('validation_filter', true);
}
retorne $isto;
}
/**
* Filtro de data ou data
*
* @param $agora
* @return $isto
*/
função pública addDateFilter($now)
{
$this->getSelect()->where(
'from_date is null ou from_date <= ?',
$ agora
)->onde(
'to_date é nulo ou to_date >= ?',
$ agora
);
retorne $isto;
}
}
?>
4) Em seguida, remova a marcação antiga do layout de grade e adicione uma lista de interface do usuário lá:
> app/code/Vendor/Rules/view/adminhtml/layout/vendor_rules_example_rule_index.xml
<?xml version="1.0"?>
<page xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<corpo>
<referenceBlock name="menu">
<action method="setActive">
<argument name="itemId" xsi:type="string">Vendor_Rules::vendor_rules</argument>
</action>
</referenceBlock>
<referenceBlock name="page.title">
<action method="setTitleClass">
<argument name="class" xsi:type="string">complexo</argument>
</action>
</referenceBlock>
<referenceContainer name="content">
<uiComponent name="vendor_rules_rule_listing"/>
</referenceContainer>
</body>
</page>Basicamente, apenas adicionamos o mencionado ` vendor_rules_rule_listing ` no conteúdo da página (a ação principal), alteramos o status do menu do nosso produto para 'Ativo' e definimos a classe de título.
5) Na próxima etapa, criamos a listagem da interface do usuário, que será colocada aqui:
> app/code/Vendor/Rules/view/adminhtml/ui_component/vendor_rules_rule_listing.xml
<?xml version="1.0"?>
<listing xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Ui/etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing_data_source</item>
<item name="deps" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing_data_source</item>
</item>
<item name="spinner" xsi:type="string">vendor_rules_rule_columns</item>
<item name="botões" xsi:type="array">
<item name="adicionar" xsi:type="array">
<item name="name" xsi:type="string">adicionar</item>
<item name="label" xsi:type="string" translate="true">Adicionar nova regra</item>
<item name="class" xsi:type="string">primário</item>
<item name="url" xsi:type="string">*/*/newaction</item>
</item>
</item>
</argumento>
<dataSource name="vendor_rules_rule_listing_data_source">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">VendorRulesRuleGridDataProvider</argument>
<argument name="name" xsi:type="string">vendor_rules_rule_listing_data_source</argument>
<argument name="primaryFieldName" xsi:type="string">rule_id</argument>
<argument name="requestFieldName" xsi:type="string">rule_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>
</argumento>
</argumento>
<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>
</argumento>
</dataSource>
<container name="listing_top">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="template" xsi:type="string">ui/grid/barra de ferramentas</item>
<item name="stickyTmpl" xsi:type="string">ui/grid/sticky/toolbar</item>
</item>
</argumento>
<bookmark name="bookmarks">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="namespace" xsi:type="string">vendor_rules_rule_listing</item>
</item>
</item>
</argumento>
</bookmark>
<component name="columns_controls">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsData" xsi:type="array">
<item name="provider" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing.vendor_rules_rule_columns</item>
</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/controls/columns</item>
<item name="displayArea" xsi:type="string">dataGridActions</item>
</item>
</argumento>
</component>
<filters name="listing_filters">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsProvider" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing.vendor_rules_rule_columns</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.filters</item>
</item>
<item name="templates" xsi:type="array">
<item name="filtros" xsi:type="array">
<item name="select" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/form/element/ui-select</item>
<item name="template" xsi:type="string">ui/grid/filters/elements/ui-select</item>
</item>
</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="provider" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing.listing_top.listing_filters</item>
<item name="imports" xsi:type="array">
<item name="visible" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing.vendor_rules_rule_columns.${ $.index }:visible</item>
</item>
</item>
</item>
<item name="observadores" xsi:type="array">
<item name="coluna" xsi:type="string">coluna</item>
</item>
</argumento>
</filtros>
<paging name="listing_paging">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.paging</item>
</item>
<item name="selectProvider" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing.vendor_rules_rule_columns.ids</item>
</item>
</argumento>
</paging>
</container>
<columns name="vendor_rules_rule_columns">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">atual</item>
</item>
</item>
</argumento>
<selectionsColumn name="ids">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="resizeEnabled" xsi:type="boolean">falso</item>
<item name="resizeDefaultWidth" xsi:type="string">55</item>
<item name="indexField" xsi:type="string">rule_id</item>
</item>
</argumento>
</selectionsColumn>
<column name="rule_id">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/column</item>
</item>
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">textRange</item>
<item name="dataType" xsi:type="string">texto</item>
<item name="sorting" xsi:type="string">asc</item>
<item name="align" xsi:type="string">esquerda</item>
<item name="label" xsi:type="string" translate="true">ID</item>
<item name="sortOrder" xsi:type="number">1</item>
</item>
</argumento>
</coluna>
<nome da coluna="nome">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/column</item>
</item>
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">texto</item>
<item name="dataType" xsi:type="string">texto</item>
<item name="align" xsi:type="string">esquerda</item>
<item name="label" xsi:type="string" translate="true">Nome</item>
</item>
</argumento>
</coluna>
<column name="is_active">
<argument name="data" xsi:type="array">
<item name="options" xsi:type="array">
<item name="active" xsi:type="array">
<item name="value" xsi:type="string">1</item>
<item name="label" xsi:type="string" translate="true">Ativo</item>
</item>
<item name="inativo" xsi:type="array">
<item name="valor" xsi:type="string">0</item>
<item name="label" xsi:type="string" translate="true">Inativo</item>
</item>
</item>
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">selecionar</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
<item name="editor" xsi:type="string">selecionar</item>
<item name="dataType" xsi:type="string">selecionar</item>
<item name="label" xsi:type="string" translate="true">Está ativo</item>
<item name="sortOrder" xsi:type="number">65</item>
</item>
</argumento>
</coluna>
<column name="sort_order">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/column</item>
</item>
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">texto</item>
<item name="dataType" xsi:type="string">número</item>
<item name="align" xsi:type="string">esquerda</item>
<item name="label" xsi:type="string" translate="true">Prioridade</item>
</item>
</argumento>
</coluna>
<column name="from_date" class="Magento\Ui\Component\Listing\Columns\Date">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="sorting" xsi:type="string">desc</item>
<item name="filter" xsi:type="string">dateRange</item>
<item name="dataType" xsi:type="string">data</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
<item name="label" xsi:type="string" translate="true">Iniciar</item>
</item>
</argumento>
</coluna>
<column name="to_date" class="Magento\Ui\Component\Listing\Columns\Date">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="sorting" xsi:type="string">desc</item>
<item name="filter" xsi:type="string">dateRange</item>
<item name="dataType" xsi:type="string">data</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
<item name="label" xsi:type="string" translate="true">Fim</item>
</item>
</argumento>
</coluna>
<actionsColumn name="actions" class="Fornecedor\Rules\Ui\Component\Listing\Column\RuleActions">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="indexField" xsi:type="string">rule_id</item>
<item name="urlEntityParamName" xsi:type="string">rule_id</item>
<item name="sortOrder" xsi:type="number">70</item>
</item>
</argumento>
</actionsColumn>
</columns>
<container name="sticky">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/sticky/sticky</item>
<item name="toolbarProvider" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing.listing_top</item>
<item name="listingProvider" xsi:type="string">vendor_rules_rule_listing.vendor_rules_rule_listing.vendor_rules_rule_columns</item>
</item>
</argumento>
</container>
</listagem>
A listagem ` dataSource ` contém um link para ` dataProvider ` – o tipo que criamos em ` di.xml `. Ele contém os dados de entrada necessários para o Grid.

A partir daqui, precisamos definir a chave arquivada — no nosso caso é ` rule_id `.
Além disso, você também pode adicionar seus botões personalizados, basta descrevê-los na seção 'botões'. Em nosso exemplo, adicionamos o botão padrão 'Adicionar' com um endereço ` */*/newaction ` ( * no caminho corresponde ao significado atual).
` container name=”listing_top” ` contém alguns componentes de listagem extras: filtros, paginação, etc. Você pode alterá-los de acordo com suas necessidades pessoais.
` columns name=“vendor_rules_rule_columns” ` contém as colunas que são quase as mesmas, como no Grid padrão. A única diferença significativa é uma nova coluna ` actionsColumn ` que apresenta um conjunto de ações: editar e excluir. Essas ações também podem ser estendidas, se necessário.
Como você deve ter notado, esta coluna tem uma nova classe. Vamos aprender como podemos criá-lo:
> app/code/Vendor/Rules/Ui/Component/Listing/Column/RuleActions.php
<?php
namespace Vendor\Rules\Ui\Component\Listing\Column;
class RuleActions estende \Magento\Ui\Component\Listing\Columns\Column
{
/**
* Caminho de URL para editar
*
* @var string
*/
const URL_PATH_EDIT = 'vendor_rules/example_rule/edit';
/**
* Caminho do URL para excluir
*
* @var string
*/
const URL_PATH_DELETE = 'vendor_rules/example_rule/delete';
/**
* Construtor de URL
*
* @var \Magento\Framework\UrlInterface
*/
protegido $urlBuilder;
/**
* Construtor
*
* @param \Magento\Framework\UrlInterface $urlBuilder
* @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context
* @param \Magento\Framework\View\Element\UiComponentFactory $uiComponentFactory
* @param array $componentes
* @param array $data
*/
função pública __construct(
\Magento\Framework\UrlInterface $urlBuilder,
\Magento\Framework\View\Element\UiComponent\ContextInterface $context,
\Magento\Framework\View\Element\UiComponentFactory $uiComponentFactory,
array $componentes = [],
matriz $dados = []
) {
$this->urlBuilder = $urlBuilder;
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* Preparar fonte de dados
*
* @param array $dataSource
* @return array
*/
função pública prepareDataSource(array $dataSource)
{
if (!isset($dataSource['data']['items'])) {
return $dataSource;
}
foreach ($dataSource['data']['items'] as &$item) {
if (!isset($item['rule_id'])) {
Prosseguir;
}
$item[$this->getData('nome')] = [
'editar' => [
'href' => $this->urlBuilder->getUrl(
estático::URL_PATH_EDIT,
[
'id' => $item['rule_id'],
]
),
'label' => __('Editar'),
],
'excluir' => [
'href' => $this->urlBuilder->getUrl(
estático::URL_PATH_DELETE,
[
'id' => $item['rule_id'],
]
),
'label' => __('Excluir'),
'confirmar' => [
'title' => __('Excluir "${ $.$data.name }"'),
'message' => __('Tem certeza de que deseja excluir a regra "${ $.$data.name }" ?'),
],
],
];
}
return $dataSource;
}
}
?>Esta classe é responsável pelo processamento das ações do Grid. A partir daqui, é possível alterar as URLs ou o nome do parâmetro transmitido. No nosso caso, a chave é rule_id (é transmitida sob o nome 'id', para facilitar o entendimento de seu valor).
Se parece com isso:

Se você fez tudo do jeito certo, sua grade deve ficar assim:

Como podemos ver no exemplo, é muito fácil transformar o Grid padrão existente em UI. Além de estender a funcionalidade padrão do Grid, também permite simplificar o trabalho com esta parte da funcionalidade do Magento 2.
PS No próximo artigo da série, descreveremos a possibilidade de adicionar ações em massa e colunas extras.
Fique ligado!
