I'm struggling to add a tab and a grid to magento's admin customer edit.
I can see how they are doing it with orders / invoices etc, but it goes against Magento's own best practices.
The goal :
Create a new tab called 'Orders Two' and duplicate the customer order grid using UI components.
Current error is : Invalid method Vendor\Namespace\Block\Adminhtml\Customer\View\Tab\Orderstwo::isAjaxLoaded
But this is most likely just because of bad xml. I'm a bit lost
So far I have attempted to do it in the same way you create a tab and add a grid in the sales order view :
view/adminhtml/layout/customer_index_edit.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<body>
<referenceBlock name="customer_form">
<block class="Module\Namespace\Block\Adminhtml\Customer\View\Tab\Orderstwo" name="customer_edit_tab_orderstwo">
<action method="setTabLabel">
<argument name="label" xsi:type="string">Orders Two</argument>
</action>
</block>
</referenceBlock>
<referenceBlock name="customer_edit_tab_orderstwo">
<uiComponent name="customer_view_orderstwo_grid"/>
</referenceBlock>
</body>
</page>
Block/Adminhtml/Customer/View/Tab/Orderstwo.php
<?php
namespace Module\Namespace\Block\Adminhtml\Customer\View\Tab;
use Magento\Framework\View\Element\Context;
use Magento\Customer\Controller\RegistryConstants;
use Magento\Framework\Registry;
use Magento\Framework\AuthorizationInterface;
use Magento\Framework\View\Element\Text\ListText;
use Magento\Backend\Block\Widget\Tab\TabInterface;
class Orderstwo extends ListText implements TabInterface
{
/**
* Core registry
*
* @var Registry
*/
protected $_coreRegistry = null;
/**
* @var bool
*/
protected $isAjaxLoaded = true;
/**
* Orderstwo constructor.
*
* @param Context $context
* @param AuthorizationInterface $authorization
* @param Registry $registry
* @param array $data
*/
public function __construct(
Context $context,
AuthorizationInterface $authorization,
Registry $registry,
array $data = []
) {
$this->_authorization = $authorization;
$this->_coreRegistry = $registry;
parent::__construct($context, $data);
}
/**
* @inheritdoc
*/
public function canShowTab()
{
return true;
}
/**
* {@inheritdoc}
*/
public function getTabLabel()
{
return __('Orders Two');
}
/**
* {@inheritdoc}
*/
public function getTabTitle()
{
return __('Orders Two');
}
/**
* {@inheritdoc}
*/
public function isHidden()
{
return false;
}
}
My UI component xml isn't really relevant because it's not even being loaded at the moment.
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* Copyright © 2015 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<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">customer_view_orderstwo_grid.sales_order_view_orderstwo_grid_data_source</item>
<item name="deps" xsi:type="string">customer_view_orderstwo_grid.sales_order_view_orderstwo_grid_data_source</item>
</item>
<item name="spinner" xsi:type="string">customer_view_orderstwo_grid_columns</item>
</argument>
<dataSource name="sales_order_view_orderstwo_grid_data_source">
<argument name="dataProvider" xsi:type="configurableObject">
<argument name="class" xsi:type="string">Vendor\Namespace\Ui\DataProvider\Customer\View\Orderstwo\Grid\DataProvider</argument>
<argument name="name" xsi:type="string">sales_order_view_orderstwo_grid_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 name="storageConfig" xsi:type="array">
<item name="indexField" xsi:type="string">entity_id</item>
</item>
</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="component" xsi:type="string">Magento_Ui/js/grid/controls/bookmarks/bookmarks</item>
<item name="displayArea" xsi:type="string">dataGridActions</item>
<item name="storageConfig" xsi:type="array">
<item name="saveUrl" xsi:type="url" path="mui/bookmark/save"/>
<item name="deleteUrl" xsi:type="url" path="mui/bookmark/delete"/>
<item name="namespace" xsi:type="string">customer_view_orderstwo_grid</item>
</item>
</item>
</argument>
</bookmark>
<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">customer_view_orderstwo_grid.customer_view_orderstwo_grid.customer_view_orderstwo_grid_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>
<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">
customer_view_orderstwo_grid.customer_view_orderstwo_grid.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">
customer_view_orderstwo_grid.customer_view_orderstwo_grid.listing_top.listing_filters
</item>
<item name="imports" xsi:type="array">
<item name="visible" xsi:type="string">
customer_view_orderstwo_grid.customer_view_orderstwo_grid.listing_top.bookmarks:current.columns.${ $.index }.visible
</item>
</item>
</item -->
</item>
</argument>
<filterRange name="entity_id">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="dataScope" xsi:type="string">entity_id</item>
<item name="label" xsi:type="string" translate="true">ID</item>
<item name="childDefaults" xsi:type="array">
<item name="provider" xsi:type="string">
customer_view_orderstwo_grid.customer_view_orderstwo_grid.listing_top.listing_filters
</item>
</item>
</item>
</argument>
<filterInput name="from">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="dataScope" xsi:type="string">from</item>
<item name="label" xsi:type="string" translate="true">from</item>
<item name="placeholder" xsi:type="string" translate="true">From</item>
</item>
</argument>
</filterInput>
<filterInput name="to">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="dataScope" xsi:type="string">to</item>
<item name="label" xsi:type="string" translate="true">to</item>
<item name="placeholder" xsi:type="string" translate="true">To</item>
</item>
</argument>
</filterInput>
</filterRange>
<filterRange name="update_time" class="Magento\Ui\Component\Filters\Type\DateRange">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="dataScope" xsi:type="string">created_at</item>
<item name="label" xsi:type="string" translate="true">Released Date</item>
<item name="childDefaults" xsi:type="array">
<item name="provider" xsi:type="string">
gene_productqa_answer_listing.gene_productqa_answer_listing.listing_top.listing_filters
</item>
</item>
</item>
</argument>
<filterDate name="from">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="dataScope" xsi:type="string">from</item>
<item name="label" xsi:type="string" translate="true">from</item>
<item name="placeholder" xsi:type="string" translate="true">From</item>
<item name="dateFormat" xsi:type="string" translate="true">MM/dd/YYYY</item>
</item>
</argument>
</filterDate>
<filterDate name="to">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="dataScope" xsi:type="string">to</item>
<item name="label" xsi:type="string" translate="true">to</item>
<item name="placeholder" xsi:type="string" translate="true">To</item>
<item name="dateFormat" xsi:type="string" translate="true">MM/dd/YYYY</item>
</item>
</argument>
</filterDate>
</filterRange>
</filters>
<massaction name="listing_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">
customer_view_orderstwo_grid.customer_view_orderstwo_grid.listing_top.bookmarks
</item>
<item name="namespace" xsi:type="string">current.paging</item>
</item>
<item name="selectProvider" xsi:type="string">
customer_view_orderstwo_grid.customer_view_orderstwo_grid.customer_view_orderstwo_grid_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="customer_view_orderstwo_grid_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">customer_view_orderstwo_grid.customer_view_orderstwo_grid.listing_top.bookmarks
</item>
<item name="namespace" xsi:type="string">current</item>
</item>
<item name="childDefaults" xsi:type="array">
<item name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">
customer_view_orderstwo_grid.customer_view_orderstwo_grid.customer_view_orderstwo_grid_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">
customer_view_orderstwo_grid.customer_view_orderstwo_grid.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="resizeEnabled" xsi:type="boolean">false</item>
<item name="resizeDefaultWidth" xsi:type="string">55</item>
<item name="indexField" xsi:type="string">entity_id</item>
</item>
</argument>
</selectionsColumn>
<column name="entity_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>
</argument>
</column>
</columns>
</listing>
Best Answer
I'm going to answer my own question here. After a lot of digging through core... The Magento_Customer module is just not built properly yet. Unlike the sales order view the customer view does not render the grids on the page using knockout but rather makes separate ajax request when you select the different tabs. For now I think you just need to do it the M1 way by defining your fields etc in a block class and rendering that out to html.
Eventually however, i'm sure Magento will get around to adhering to their own standards and it'll stop working. So just be ready for that. Here's a tutorial for doing it the wrong way : http://www.ibnab.com/en/blog/magento-2/magento-2-backend-create-custom-tab-in-customer-view-and-load-grid-inside-admin