Explanation of UI Component Grid in Magento 2
[EDIT 3 Oct 2018]
Update for links to devdocs: 2.0 - https://devdocs.magento.com/guides/v2.0/ui-components/ui-listing-grid.html and https://devdocs.magento.com/guides/v2.0/ui-components/ui-secondary.html
2.1 - https://devdocs.magento.com/guides/v2.1/ui_comp_guide/components/ui-listing-grid.html
2.2 - https://devdocs.magento.com/guides/v2.2/ui_comp_guide/components/ui-listing-grid.html
[EDIT 21 Jan 2016]
As of 20/01/2016 magento2 devdocs has been updated with extended documentation of UI components. I have not checked it extensively but they may contain more information than the response I gave few days ago, so in the interest of your time you may want to see http://devdocs.magento.com/guides/v2.0/ui-library/ui-library-secondary.html
[/EDIT]
I have been working with Magento2 for over a month now and this is what I noticed about the new way of creating grids.
Magento 2 UI grid component
1) layout file inside Company/Module/view/adminhtml/layout/routerid_controller_action.xml
define grid as uiComponent with:
2) uiComponent is defined in Company/Module/view/adminhtml/ui_component/listing_name.xml
file. File name must be the same as uiComponent name used in layout file. The structure of the file may seem pretty complex at first sight but as always these are some repeating nodes. To make it simple lets slice it. Main node of the component file is <listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
. It is fixed and I believe it requires namespace location attribute. Next there are typically 4 nodes inside <listing />
node: <argument />
, <dataSource />
, <container />
and <columns />
. This is however not a strict setup as <argument />
node might be duplicated to provide more configuration or <container />
as in cms page listing that adds "sticky" container for some reason.
The first node is <argument />
. This node defines data for the component. Usually you need to provide something like this:
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">listing_name.listing_name_data_source</item>
<item name="deps" xsi:type="string">listing_name.listing_name_data_source</item>
</item>
<item name="spinner" xsi:type="string">listing_columns</item>
<item name="buttons" xsi:type="array">
<item name="add" xsi:type="array">
<item name="name" xsi:type="string">add</item>
<item name="label" xsi:type="string" translate="true">Add New Item</item>
<item name="class" xsi:type="string">primary</item>
<item name="url" xsi:type="string">*/*/new</item>
</item>
</item>
</argument>
<argument />
node requires attribute name
. In this case data
defines basic information about the component. It contains multiple <item />
node for each specific part of configuration. js_config
tells the component where are provider of the data and dependencies in the listing xml configuration (which I think is converted into javascript hash). provider
value consist of listing name used in layout file and uniqure data source name that will be used later. In those listings I checked in magento provider
and deps
are the same. I am not sure what is use of having this different. spinner
takes name of the node where columns of the grid are defined. buttons
allows to add buttons to the top of the grid. In most cases it would be just Add new
button. Buttons have few elements: name
used as element id, label
is what the button says, class
is the button class and url
is the link to which it points. Asteriks is replaced by the portion of current url. Other possible <item />
nodes for button are: id
, title
, type
(reset, submit or button), onclick
(instead of url
, it has precedence), style
, value
, disabled
. Button element is rendered by Magento\Ui\Component\Control\Button
class.
Next we have <dataSource />
node:
<dataSource name="listing_name_data_source">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">UniqueNameGridDataProvider</argument>
<argument name="name" xsi:type="string">listing_name_data_source</argument>
<argument name="primaryFieldName" xsi:type="string">database_id</argument>
<argument name="requestFieldName" xsi:type="string">request_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>
name
used in <dataSource />
node must match the one used in argument/js_config/provider
and argument/js_config/deps
. Next node defines which class is responsible for preparing data for the grid. class
argument requires unique name that will be matched in di.xml
. primaryFieldName
relates to the database primary column and requestFieldName
to the variable in http requests. They may be equal but don't have to, CMS page listing uses page_id
as primaryFieldName
and id
as requestFieldName
. update_url
refers to the entry point where ajax calls for filtering and sorting are send. Second argument in <dataSource />
refers to javascript file that handles js part of sending and processing ajax calls for the grid. The default file is Magento/Ui/view/base/web/js/grid/provider.js
.
Another node is <container />
.
<container name="listing_top"> ... </container>
As it contains a lot of data let me divide it as well. Its children are the parts of the entire page. First child <argument />
:
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="template" xsi:type="string">ui/grid/toolbar</item>
</item>
</argument>
It defines knockout template responsible for handling the layout and all actions and by default points to Magento/Ui/view/base/web/templates/grid/toolbar.html
Next node is (or can be) <bookmark />
<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">listing_name</item>
</item>
</item>
</argument>
</bookmark>
This node adds bookmark feature to the grid. It allows admin to set up different "profiles" of the grid which displays different columns. Thanks to that you can add all columns from the table to the grid and let the user decide which information are relevant to him. namespace
must match name used in layout file.
Another node is <component />
<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">listing_name.listing_name.listing_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>
</argument>
</component>
We have 3 values to configure here. First is provider
which follows the pattern [listing_name_from_layout].[listing_name_from_layout].[listing_columns_node_name] (like in node listing/argument/spinner). component
refers to js file that displays grid and by default points to Magento/Ui/view/base/web/js/grid/controls/columns.js
which uses template Magento/Ui/view/base/web/templates/grid/controls/columns.html
. The last item is displayArea
which defines where column controls need to be displayed. It refers to getRegion('dataGridActions')
in file defined in container/argument/config/template
(default: Magento/Ui/view/base/web/templates/grid/toolbar.html
).
Next node is <filterSearch />
<filterSearch name="fulltext">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="provider" xsi:type="string">listing_name.listing_name_data_source</item>
<item name="chipsProvider" xsi:type="string">listing_name.listing_name.listing_top.listing_filters_chips</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">listing_name.listing_name.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.search</item>
</item>
</item>
</argument>
</filterSearch>
This node adds full text search into the page. It is located above the grid as single text input field with "Search by keyword" as a placeholder. I am not sure what options are possible here as I didn't play with this much but listing_filters_chips refers to Magento/Ui/view/base/web/js/grid/filters/chips.js
file.
Next node is <filters />
<filters name="listing_filters">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsProvider" xsi:type="string">listing_name.listing_name.listing_columns</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">listing_name.listing_name.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.filters</item>
</item>
<item name="templates" xsi:type="array">
<item name="filters" 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">listing_name.listing_name.listing_top.listing_filters</item>
<item name="imports" xsi:type="array">
<item name="visible" xsi:type="string">listing_name.listing_name.listing_columns.${ $.index }:visible</item>
</item>
</item>
</item>
<item name="observers" xsi:type="array">
<item name="column" xsi:type="string">column</item>
</item>
</argument>
</filters>
This node defines configuration for column filtering that is visible after clicking "Filters" button at the top right above the grid. columnsProvider
follows similar structure as previous nodes, so [listing_name_from_layout].[listing_name_from_layout].[listing_columns_node_name]. storegeConfig
goes like [listing_name_from_layout].[listing_name_from_layout].[container_node_name][bookmark_node_name]. In templates
item node you can define which files are used to render specific filter options. In this case only select is defined and it uses Magento/Ui/view/base/web/js/form/element/ui-select.js
as component
and Magento/Ui/view/base/web/templates/grid/filters/elements/ui-select.html
as knockout template. Look into Magento/Ui/view/base/web/js/form/element
to see other possibilities. Only select is defined here to override default values: Magento/Ui/view/base/web/js/form/element/select.js
and Magento/Ui/view/base/web/templates/grid/filters/elements/select.html
. Default values for filters and other nodes are defined in Magento/Ui/view/base/ui_component/etc/definition.xml
.
Next node is <massAction />
and allows to add mass action select to the grid
<massaction name="listing_massaction">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="selectProvider" xsi:type="string">listing_name.listing_name.listing_columns.ids</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/tree-massactions</item>
<item name="indexField" xsi:type="string">database_id</item>
</item>
</argument>
<action name="delete">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">delete</item>
<item name="label" xsi:type="string" translate="true">Delete</item>
<item name="url" xsi:type="url" path="*/*/massDelete"/>
<item name="confirm" xsi:type="array">
<item name="title" xsi:type="string" translate="true">Delete items</item>
<item name="message" xsi:type="string" translate="true">Are you sure you wan't to delete selected items?</item>
</item>
</item>
</argument>
</action>
<action name="change_status">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">change_status</item>
<item name="label" xsi:type="string" translate="true">Change Status</item>
</item>
</argument>
<argument name="actions" xsi:type="configurableObject">
<argument name="class" xsi:type="string">Company\Module\Ui\Component\MassAction\Status\Options</argument>
<argument name="data" xsi:type="array">
<item name="confirm" xsi:type="array">
<item name="title" xsi:type="string" translate="true">Change Status</item>
<item name="message" xsi:type="string" translate="true">Are you sure to change status for selected feed(s)?</item>
</item>
</argument>
</argument>
</action>
</massaction>
name
argument should be unique. First child node <argument />
defines basic data. provider
follows the same structure as other nodes and refers to columns node name and its ids column. This column will hold checkboxes with selected items for the mass action to process. component
defines what file is used to render and handle the mass action. Default value is Magento_Ui/js/grid/massactions
(points to Magento/Ui/view/base/web/js/grid/massactions.js
). You can use Magento_Ui/js/grid/tree-massactions
to add tree like structure. In the above case I use it to add "Change status" action which shows "enable" and "disable" options.
After <argument />
node you can add as many <action />
nodes as many actions you want to have. Each <action />
node follows similar scheme. In the first case (delete action) node requires unique name. Then argument
contains configuration where label
is what is visible in select option, url
endpoint to send data and confirm
adds confirmation modal before send. In case of "Change status" action url
in first argument
node is ommited as urls are provided per status by class defined in second argument
node. The class should implement Zend\Stdlib\JsonSerializable interface. Check Magento\Customer\Ui\Component\MassAction\Group\Options
as a reference.
Finally in the <container />
node we have <paging />
node that defines pagination.
<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">listing_name.listing_name.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.paging</item>
</item>
<item name="selectProvider" xsi:type="string">listing_name.listing_name.listing_columns.ids</item>
</item>
</argument>
</paging>
Structure for provider
and selectProvider
should be clear now.
And the last node for basic grid is <columns />
. It contains all definition of columns that are available for use by the admin. Node is defined as
<columns name="listing_columns"> ... </columns>
and the name attribute is used in other nodes when refers to it. First child is
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">listing_name.listing_name.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">listing_name.listing_name.listing_top.bookmarks</item>
<item name="root" xsi:type="string">columns.${ $.index }</item>
<item name="namespace" xsi:type="string">current.${ $.storageConfig.root}</item>
</item>
<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">name_listing.name_listing.listing_columns.actions</item>
<item name="target" xsi:type="string">applyAction</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">edit</item>
<item name="1" xsi:type="string">${ $.$data.rowIndex }</item>
</item>
</item>
</item>
</item>
</argument>
What I did here was only supply correct provider
values following the scheme used in the listing. fieldAction
node allows to define action that is fired when clicked on the cell. Default settings call edit action
Next is <selectionColumns />
<selectionsColumn name="ids">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="resizeEnabled" xsi:type="boolean">false</item>
<item name="resizeDefaultWidth" xsi:type="string">55</item>
<item name="indexField" xsi:type="string">id</item>
</item>
</argument>
</selectionsColumn>
This node defines column with checkboxes for mass action to use. It names is referred to after dot in several nodes described above.
After that you can add any number of columns in the same format:
<column name="name" class="Company\Module\Ui\Component\Listing\Column\Name">
<argument name="data" xsi:type="array">
<item name="options" xsi:type="object">Company\Module\Model\Source\Type</item>
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">select</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
<item name="dataType" xsi:type="string">select</item>
<item name="bodyTmpl" xsi:type="string">ui/grid/cells/html</item>
<item name="label" xsi:type="string" translate="true">Name</item>
<item name="sortOrder" xsi:type="number">80</item>
<item name="visible" xsi:type="boolean">false</item>
</item>
</argument>
</column>
Not all most inner items node are necessary. They are defining:
filter
- filter type of the column. This is used in filters block. Available values are: text, select, dateRange. If select is used <item name="options">...</item>
will be used as a class that provides options for the filter select
component
- defines js files which is used to render column. Available options are in Magento/Ui/view/base/web/js/grid/columns/*
. select is provided is filter is set to select. For text filter this value is not required.
dataType
- provides information of data type used for the column value. For select use select as well, for dateRange use date
bodyTmpl
- defines html file used by knockout to render cell. By default ui/grid/cells/text is used (Magento/Ui/view/base/web/templates/grid/cells/text.html
). Other options are located in Magento/Ui/view/base/web/templates/grid/cells/*
directory. ui/grid/cells/html
allows to use html content in cell.
label
- this will be displayed in the column header and filter block
sortOrder
- ordering
visible
- whether or not display the column. This can be used to defined columns for bookmarks but do not show them by default.
At the end you can add actions column witch actions available to do for the single item
<actionsColumn name="actions" class="Company\Module\Ui\Component\Listing\Column\Actions">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="resizeEnabled" xsi:type="boolean">false</item>
<item name="resizeDefaultWidth" xsi:type="string">107</item>
<item name="indexField" xsi:type="string">database_id</item>
</item>
</argument>
</actionsColumn>
indexField
refers to column name in the database. Actions class should extends Magento\Ui\Component\Listing\Columns\Column
and define prepareDataSource
method. See Magento/Cms/Ui/Component/Listing/Column/PageActions.php
as a reference
3) to finish the grid we need to define some elements in Company/Module/etc/di.xml
First we define provider class that was used in node dataSource/class
<virtualType name="UniqueNameGridDataProvider" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
<arguments>
<argument name="collection" xsi:type="object" shared="false">Company\Module\Model\Resource\Item\Collection</argument>
<argument name="filterPool" xsi:type="object" shared="false">UniqueNameItemIdFilterPool</argument>
</arguments>
</virtualType>
collection
resolves to standard collection class and filerPool
defines new element:
<virtualType name="UniqueNameItemIdFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool">
<arguments>
<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>
</argument>
</arguments>
</virtualType>
This evidently has to do something with filtering and searching. For now I always used the default values.
Now we register our data source:
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="listing_name_data_source" xsi:type="string">Company\Module\Model\Resource\Item\Grid\Collection</item>
</argument>
</arguments>
</type>
In this case node name must match the one used in <dataSource />
node in listing xml and it resolves not to collection but to GridCollection class. It should extend regular collection class and additionally implement Magento\Framework\Api\Search\SearchResultInterface
.
At the end we configure our grid collection (argument names are rather obvious)
<type name="Company\Module\Model\Resource\Item\Grid\Collection">
<arguments>
<argument name="mainTable" xsi:type="string">database_table_name</argument>
<argument name="eventPrefix" xsi:type="string">name_for_events</argument>
<argument name="eventObject" xsi:type="string">event_object_name</argument>
<argument name="resourceModel" xsi:type="string">Company\Module\Model\Resource\Item</argument>
</arguments>
</type>
For grid we need two main files one is ui_component xml and second one is di.xml
I hope you know in layout xml file you need to add uiComponent tag, i.e. -
<?xml version="1.0"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
<update handle="styles"/>
<body>
<referenceContainer name="content">
<uiComponent name="test_lists_listing"/>
</referenceContainer>
</body>
</page>
now you need to create a test_lists_listing.xml
in ui_component folder.
E.g. app\code\Sugarcode\Test\view\adminhtml\ui_component\test_lists_listing.xml
This file has number of tags
<argument name="data" xsi:type="array">
:- need mention arugemnt like button js etc..<dataSource name="test_lists_listing_data_source">
:- this block is used to supply data for grids in this one of arguments is<argument name="class" xsi:type="string">ListsGridDataProvider</argument>
which we need to mention indi.xml
(explained in di.xml part)<container name="listing_top">
:- in this block we mention filters, export, bookmarks (which save data in ui_bookmark table ),massaction, paging, and fulltext (to do fulltext search in setup or table that column should be fulltext index )<columns name="test_lists_columns">
:- in this we need mention all column
Last one is in di.xml
<virtualType name="TestGirdFilterPool" type="Magento\Framework\View\Element\UiComponent\DataProvider\FilterPool">
<arguments>
<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>
</argument>
</arguments>
</virtualType>
<virtualType name="ListsGridDataProvider" type="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
<arguments>
<argument name="collection" xsi:type="object" shared="false">Sugarcode\Test\Model\ResourceModel\Test\Collection</argument>
<argument name="filterPool" xsi:type="object" shared="false">TestGirdFilterPool</argument>
</arguments>
</virtualType>
<type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory">
<arguments>
<argument name="collections" xsi:type="array">
<item name="test_lists_listing_data_source" xsi:type="string">Sugarcode\Test\Model\ResourceModel\Test\Grid\Collection</item>
</argument>
</arguments>
</type>
<type name="Sugarcode\Test\Model\ResourceModel\Test\Grid\Collection">
<arguments>
<argument name="mainTable" xsi:type="string">testtable</argument>
<argument name="eventPrefix" xsi:type="string">test_test_grid_collection</argument>
<argument name="eventObject" xsi:type="string">test_grid_collection</argument>
<argument name="resourceModel" xsi:type="string">Sugarcode\Test\Model\ResourceModel\Test</argument>
</arguments>
</type>
- TestGirdFilterPool :- mention filters
- ListsGridDataProvider :- which i mentioned in ui xml for data provider
- CollectionFactory :- need to mention collection
- Grid/Collection :- in this we need to pass all params like table name, collection etc.
app\code\Sugarcode\Test\view\adminhtml\ui_component\test_lists_listing.xml
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../Ui/etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">test_lists_listing.test_lists_listing_data_source</item>
<item name="deps" xsi:type="string">test_lists_listing.test_lists_listing_data_source</item>
</item>
<item name="spinner" xsi:type="string">test_lists_columns</item>
<item name="buttons" xsi:type="array">
<item name="add" xsi:type="array">
<item name="name" xsi:type="string">add</item>
<item name="label" xsi:type="string" translate="true">Add New Info</item>
<item name="class" xsi:type="string">primary</item>
<item name="url" xsi:type="string">*/*/new</item>
</item>
</item>
</argument>
<dataSource name="test_lists_listing_data_source">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">ListsGridDataProvider</argument>
<argument name="name" xsi:type="string">test_lists_listing_data_source</argument>
<argument name="primaryFieldName" xsi:type="string">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>
<container name="listing_top">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="template" xsi:type="string">ui/grid/toolbar</item>
</item>
</argument>
<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">test_lists_listing</item>
</item>
</item>
</argument>
</bookmark>
<exportButton name="export_button">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="selectProvider" xsi:type="string">test_lists_listing.test_lists_listing.test_lists_columns.ids</item>
</item>
</argument>
</exportButton>
<container 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">test_lists_listing.test_lists_listing.test_lists_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>
</argument>
</container>
<filterSearch name="fulltext">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/search/search</item>
<item name="displayArea" xsi:type="string">dataGridFilters</item>
<item name="provider" xsi:type="string">test_lists_listing.test_lists_listing_data_source</item>
<item name="chipsProvider" xsi:type="string">test_lists_listing.test_lists_listing.listing_top.listing_filters_chips</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">test_lists_listing.test_lists_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.search</item>
</item>
</item>
</argument>
</filterSearch>
<filters name="listing_filters">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="columnsProvider" xsi:type="string">test_lists_listing.test_lists_listing.test_lists_columns</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">test_lists_listing.test_lists_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.filters</item>
</item>
<item name="templates" xsi:type="array">
<item name="filters" 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">test_lists_listing.test_lists_listing.listing_top.listing_filters</item>
<item name="imports" xsi:type="array">
<item name="visible" xsi:type="string">test_lists_listing.test_lists_listing.test_lists_columns.${ $.index }:visible</item>
</item>
</item>
</item>
</argument>
<filterSelect name="status">
<argument name="optionsProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">Sugarcode\Test\Model\Status</argument>
</argument>
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="dataScope" xsi:type="string">status</item>
<item name="caption" xsi:type="string" translate="true">Select...</item>
<item name="label" xsi:type="string" translate="true">Status</item>
</item>
</argument>
</filterSelect>
</filters>
<massaction name="listing_massaction">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="selectProvider" xsi:type="string">test_lists_listing.test_lists_listing.test_lists_columns.ids</item>
<item name="indexField" xsi:type="string">id</item>
</item>
</argument>
<action name="delete">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">delete</item>
<item name="label" xsi:type="string" translate="true">Delete</item>
<item name="url" xsi:type="url" path="test/lists/massDelete"/>
<item name="confirm" xsi:type="array">
<item name="title" xsi:type="string" translate="true">Delete items</item>
<item name="message" xsi:type="string" translate="true">Are you sure you wan't to delete selected items?</item>
</item>
</item>
</argument>
</action>
<action name="edit">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">edit</item>
<item name="label" xsi:type="string" translate="true">Edit</item>
<item name="callback" xsi:type="array">
<item name="provider" xsi:type="string">test_lists_listing.test_lists_listing.test_lists_columns_editor</item>
<item name="target" xsi:type="string">editSelected</item>
</item>
</item>
</argument>
</action>
<action name="disable">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">disable</item>
<item name="label" xsi:type="string" translate="true">Disable</item>
<item name="url" xsi:type="url" path="test/lists/massDisable"/>
</item>
</argument>
</action>
<action name="enable">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="type" xsi:type="string">enable</item>
<item name="label" xsi:type="string" translate="true">Enable</item>
<item name="url" xsi:type="url" path="test/lists/massEnable"/>
</item>
</argument>
</action>
</massaction>
<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">test_lists_listing.test_lists_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.paging</item>
</item>
<item name="selectProvider" xsi:type="string">test_lists_listing.test_lists_listing.test_lists_columns.ids</item>
<item name="displayArea" xsi:type="string">bottom</item>
<item name="options" xsi:type="array">
<item name="20" xsi:type="array">
<item name="value" xsi:type="number">20</item>
<item name="label" xsi:type="string" translate="true">20</item>
</item>
<item name="30" xsi:type="array">
<item name="value" xsi:type="number">30</item>
<item name="label" xsi:type="string" translate="true">30</item>
</item>
<item name="50" xsi:type="array">
<item name="value" xsi:type="number">50</item>
<item name="label" xsi:type="string" translate="true">50</item>
</item>
<item name="100" xsi:type="array">
<item name="value" xsi:type="number">100</item>
<item name="label" xsi:type="string" translate="true">100</item>
</item>
<item name="200" xsi:type="array">
<item name="value" xsi:type="number">200</item>
<item name="label" xsi:type="string" translate="true">200</item>
</item>
</item>
</item>
</argument>
</paging>
</container>
<columns name="test_lists_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">test_lists_listing.test_lists_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current</item>
</item>
<item name="editorConfig" xsi:type="array">
<item name="selectProvider" xsi:type="string">test_lists_listing.test_lists_listing.test_lists_columns.ids</item>
<item name="enabled" xsi:type="boolean">true</item>
<item name="indexField" xsi:type="string">id</item>
<item name="clientConfig" xsi:type="array">
<item name="saveUrl" xsi:type="url" path="test/lists/inlineEdit"/>
<item name="validateBeforeSave" xsi:type="boolean">false</item>
</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">test_lists_listing.test_lists_listing.test_lists_columns.actions</item>
<item name="target" xsi:type="string">applyAction</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">edit</item>
<item name="1" xsi:type="string">${ $.$data.rowIndex }</item>
</item>
</item>
<item name="controlVisibility" xsi:type="boolean">true</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">test_lists_listing.test_lists_listing.listing_top.bookmarks</item>
<item name="root" xsi:type="string">columns.${ $.index }</item>
<item name="namespace" xsi:type="string">current.${ $.storageConfig.root }</item>
</item>
</item>
</item>
</argument>
<selectionsColumn name="ids">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="indexField" xsi:type="string">id</item>
<item name="sortOrder" xsi:type="number">0</item>
</item>
</argument>
</selectionsColumn>
<column name="id">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">textRange</item>
<item name="sorting" xsi:type="string">asc</item>
<item name="label" xsi:type="string" translate="true">ID</item>
<item name="sortOrder" xsi:type="number">10</item>
</item>
</argument>
</column>
<column name="order_id">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">text</item>
<item name="filter_index" xsi:type="string">o.increment_id</item>
<item name="add_field" xsi:type="boolean">false</item>
<item name="label" xsi:type="string" translate="true">Order ID</item>
<item name="sortOrder" xsi:type="number">20</item>
</item>
</argument>
</column>
<column name="title">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="editor" xsi:type="array">
<item name="editorType" xsi:type="string">text</item>
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">true</item>
</item>
</item>
<item name="filter" xsi:type="string">text</item>
<item name="label" xsi:type="string" translate="true">Title</item>
<item name="sortOrder" xsi:type="number">30</item>
</item>
</argument>
</column>
<column name="status">
<argument name="data" xsi:type="array">
<item name="options" xsi:type="object">Sugarcode\Test\Model\Status</item>
<item name="js_config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
</item>
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">select</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
<item name="editor" xsi:type="string">select</item>
<item name="dataType" xsi:type="string">select</item>
<item name="label" xsi:type="string" translate="true">Status</item>
<item name="sortOrder" xsi:type="number">40</item>
</item>
</argument>
</column>
<column name="created_at" class="Magento\Ui\Component\Listing\Columns\Date">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">dateRange</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
<item name="dataType" xsi:type="string">date</item>
<item name="label" xsi:type="string" translate="true">Created</item>
<item name="sortOrder" xsi:type="number">50</item>
</item>
</argument>
</column>
<actionsColumn name="actions" class="Sugarcode\Test\Ui\Component\Listing\Column\TestActions">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="indexField" xsi:type="string">id</item>
</item>
</argument>
</actionsColumn>
</columns>
</listing>
The Supplement of Top Answer
The top answer is great, I follow it to create a listing page. But, it has a problem:
When you apply filter, then removing filter, same row content will repeat in the whole page grid.
Solution
In the <dataSource />
node, below <item name="update_url" xsi:type="url" path="mui/index/render"/>
, add the content:
<item name="storageConfig" xsi:type="array">
<item name="indexField" xsi:type="string">entity_id</item>
</item>
entity_id
is the primary key for the listing collection.