Here is what you need.
All files path are relative to the [Namespace]/[Module]
folder.
First, the layout handle for your action: view/adminhtml/layout/foo_bar_index.xml
(make sure the name matches the route)
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<update handle="styles"/>
<body>
<referenceContainer name="content">
<uiComponent name="foo_bar_listing"/>
</referenceContainer>
</body>
</page>
Now the ui component view/adminhtml/ui_component/foo_bar_listing.xml
<?xml version="1.0" encoding="UTF-8"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">foo_bar_listing.foo_bar_listing_data_source</item>
<item name="deps" xsi:type="string">foo_bar_listing.foo_bar_listing_data_source</item>
</item>
<item name="spinner" xsi:type="string">foo_bar_columns</item>
<item name="buttons" xsi:type="array"><!-- define buttons you need above the list. you may skip this -->
<item name="add" xsi:type="array">
<item name="name" xsi:type="string">add</item>
<item name="label" xsi:type="string" translate="true">Add New Foo Bar</item>
<item name="class" xsi:type="string">primary</item>
<item name="url" xsi:type="string">*/*/new</item>
</item>
</item>
</argument>
<dataSource name="foo_bar_listing_data_source">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">[Namespace]\[Module]\Ui\DataProvider\FooBarDataProvider</argument>
<argument name="name" xsi:type="string">foo_bar_listing_data_source</argument>
<argument name="primaryFieldName" xsi:type="string">filename_id</argument>
<argument name="requestFieldName" xsi:type="string">filename_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>
<listingToolbar name="listing_top">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="sticky" xsi:type="boolean">true</item>
</item>
</argument>
<bookmark name="bookmarks"/>
<columnsControls name="columns_controls"/>
<filterSearch name="fulltext"/><!-- skip this if you don't want full text search-->
<filters name="listing_filters">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="displayArea" xsi:type="string">dataGridFilters</item>
<item name="dataScope" xsi:type="string">filters</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">foo_bar_listing.foo_bar_listing.listing_top.bookmarks</item>
<item name="namespace" xsi:type="string">current.filters</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="provider" xsi:type="string">foo_bar_listing.foo_bar_listing.listing_top.listing_filters</item>
<item name="imports" xsi:type="array">
<item name="visible" xsi:type="string">foo_bar_listing.foo_bar_listing.listing_top.bookmarks:current.columns.${ $.index }.visible</item>
</item>
</item>
</item>
</argument>
<filterInput name="filename_id">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="dataScope" xsi:type="string">filename_id</item>
<item name="label" xsi:type="string" translate="true">Module Name</item>
</item>
</argument>
</filterInput><!-- additional filters go here -->
</filters>
<paging name="listing_paging"/>
</listingToolbar>
<columns name="foo_bar_columns">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="childDefaults" xsi:type="array">
<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">foo_bar_listing.foo_bar_listing.foo_bar_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="appendTo" xsi:type="string">foo_bar_listing.foo_bar_listing.listing_top.columns_controls</item>
<item name="storageConfig" xsi:type="array">
<item name="provider" xsi:type="string">foo_bar_listing.foo_bar_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>
<column name="filename_id"><!-- feel free to change the column name -->
<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="dataType" xsi:type="string">text</item>
<item name="align" xsi:type="string">left</item>
<item name="label" xsi:type="string" translate="true">File name</item>
<item name="sortOrder" xsi:type="number">10</item>
</item>
</argument>
</column> <!-- additional columns here -->
<actionsColumn name="actions" class="[Namespace]\[Module]\Ui\Component\Listing\Columns\FooBarActions"><!-- Skip this if your module does not have any actions -->
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="sortOrder" xsi:type="number">200</item>
<item name="dataType" xsi:type="string">actions</item>
<item name="align" xsi:type="string">left</item>
<item name="label" xsi:type="string" translate="true">Action</item>
<item name="data_type" xsi:type="string">actions</item>
<item name="filterable" xsi:type="boolean">false</item>
<item name="sortable" xsi:type="boolean">false</item>
</item>
</argument>
</actionsColumn>
</columns>
</listing>
(please don't ask me to explain what every tag does. I barely know what a few of them mean).
Now the controller action for your grid page.
Controllers/Adminhtml/FooBar/Index.php
(make sure the filename matches your route).
<?php
namespace [Namespace]\[Module]\Controller\Adminhtml\FooBar;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
class Index extends Action
{
/**
* page factory reference
*
* @var PageFactory
*/
protected $resultPageFactory;
/**
* @param Context $context
* @param PageFactory $resultPageFactory
*/
public function __construct(
Context $context,
PageFactory $resultPageFactory
)
{
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
}
/**
* run index action
*
* @return \Magento\Framework\View\Result\Page
*/
public function execute()
{
$pageResult = $this->resultPageFactory->create();
$pageResult->getConfig()->getTitle()->set(__('Your Page title'));
$pageResult->getConfig()->getTitle()->prepend(__('FooBar List'));
$this->_setActiveMenu('Your menu id here') //can skip this
->_addBreadcrumb(
__('Your Module Name'),
__('Your Module Name')
)->_addBreadcrumb(
__('FooBar List'),
__('FooBar List')
);
return $pageResult;
}
}
the data provider for the grid: Ui/DataProvider/FooBarDataProvider.php
<?php
namespace [Namespace]\[Module]\Ui\DataProvider;
use Magento\Ui\DataProvider\AbstractDataProvider;
use [Namespace]\[Module]\Model\FooBar\CollectionFactory;
/**
* Class ProductDataProvider
*/
class FooBarDataProvider extends AbstractDataProvider
{
/**
* foobar collection
*
* @var \[Namespace]\[Module]\Model\FooBar\Collection
*/
protected $collection;
/**
* @var \Magento\Ui\DataProvider\AddFieldToCollectionInterface[]
*/
protected $addFieldStrategies;
/**
* @var \Magento\Ui\DataProvider\AddFilterToCollectionInterface[]
*/
protected $addFilterStrategies;
/**
* Construct
*
* @param string $name
* @param string $primaryFieldName
* @param string $requestFieldName
* @param CollectionFactory $collectionFactory
* @param array $meta
* @param array $data
*/
public function __construct(
$name,
$primaryFieldName,
$requestFieldName,
CollectionFactory $collectionFactory,
array $meta = [],
array $data = []
) {
parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
$this->collection = $collectionFactory->create();
}
/**
* Get collection
*
* @return \[Namespace]\[Module]\Model\FooBar\Collection
*/
public function getCollection()
{
return $this->collection;
}
/**
* Get data
*
* @return array
*/
public function getData()
{
if (!$this->getCollection()->isLoaded()) {
$this->getCollection()->load();
}
return $this->getCollection()->toArray();
}
}
the class for adding actions to each row, only if you need it.
Ui/Component/Listing/Columns/FooBarActions.php
<?php
namespace [Namespace]\[Module]\Ui\Component\Listing\Columns;
use Magento\Framework\Filesystem;
use Magento\Framework\View\Element\UiComponent\ContextInterface;
use Magento\Framework\View\Element\UiComponentFactory;
use Magento\Ui\Component\Listing\Columns\Column;
use Magento\Framework\UrlInterface;
use Magento\Framework\App\Filesystem\DirectoryList;
class FooBarActions extends Column
{
/**
* @var UrlInterface
*/
protected $urlBuilder;
/**
* @param ContextInterface $context
* @param UiComponentFactory $uiComponentFactory
* @param UrlInterface $urlBuilder
* @param array $components
* @param array $data
*/
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
UrlInterface $urlBuilder,
array $components = [],
array $data = []
) {
$this->urlBuilder = $urlBuilder;
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* Prepare Data Source
*
* @param array $dataSource
* @return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
//this is just a demo, it might not work on your instance.
foreach ($dataSource['data']['items'] as &$item) {
$item[$this->getData('name')]['edit'] = [
'href' => $this->urlBuilder->getUrl(
'[module]/controller/edit',
['id' => $item['id']]
),
'label' => __('Edit'),
'hidden' => false,
];
}
}
return $dataSource;
}
}
your collection class looks fine, just add this method in it.
/**
* @param $field
* @param $direction
* @return \Magento\Framework\Data\Collection
*/
public function addOrder($field, $direction)
{
return $this->setOrder($field, $direction);
}
That's all I have for now, it this does not solve your problem it should at least push your forward.
I left some comments in the code, read them and take the necessary actions.
Best Answer
Here is what I found out so far.
This is not complete but it can take you on the right path.
I conducted my tests by modifying the cms page listing ui component.
I added this
in
cms_page_listing.xml
inside the dataSource node inside the data/config argument. So now it looks like thisThis allows me to call the url
ROOT/admin/cms/page/index/key/<form_key_here>/?page_id=2
.And I see only the page with id 2.
But it does not work for range filters and the filter value is not filled in. It still shows as blank.
Now here is why this is possible.
The dataProvider class for cms pages is
Magento\Cms\Ui\Component\DataProvider
.This one extends
Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider
.This last one contains a protected method called
prepareUpdateUrl
What this does is to check in the ui component config if there is an element called
filter_url_params
. If there is, it takes all the values inside it and checks the request for values matching what's defined infilter_url_params
.But by default it works only with
eq
.What you can try is to have a custom dataSource for your component where you rewrite the method
prepareUpdateUrl
and make it take into account all the request variables you need and maybe add paging to it and range filtering.Side note: This is a very interesting question. I'm sure a lot of people will need this in the future.