Q:

Admin tab content that has grid table but also other blocks. How to?

I need to create custom tab on admin customer edit page, that has grid table(with search, filters, sortings etc) and also another blocks (template block and custom form). Here is part of layout xml for this tab
 <layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
 <container name="root" label="Root">
   <block class="VendorName\ModuleName\Block\Adminhtml\Edit\Customer\Tab\View\SomeBlock" name="someblock1" template="VendorName_ModuleName::customer/tab/mytemplate.phtml" />
   <block class="VendorName\ModuleName\Block\Adminhtml\Edit\Customer\Tab\View\MyForm" name="someblock2" />
   <block class="Magento\Backend\Block\Widget\Grid" name="customer.transaction.edit.tab">
        <arguments>
            <argument name="dataSource" xsi:type="object">VendorName\ModuleName\Model\ResourceModel\CustomModel\Collection\Grid</argument>
            <argument name="id" xsi:type="string">Grid</argument>
            <argument name="use_ajax" xsi:type="string">true</argument>
            <argument name="grid_url" xsi:type="url" path="*/*/path-to-this-tab">
                <param name="_current">1</param>
            </argument>
        </arguments>
        <block class="Magento\Backend\Block\Widget\Grid\ColumnSet" as="grid.columnSet" name="customer.edit.tab.columnSet">
.
.
.

So here you see I have two blocks and grid description. It works until you try to interact with grid (sort columns, search etc) because grid uses grid_url argument for ajax requests so it loads whole layout again what causes duplication of other blocks(template and custom form).  
Then I created separate layout xml and controller for grid and specify its path in grid_url(in both layouts) This way it works good, but now I have the same piece of xml code to determine the grid that repeats twice in two layouts, which is not good, but I don't know how to solve this problem another way. Maybe you can suggest better solution. Thank you

A:

Ok, let's start a story. A bunch of files we'll need.
1) Adding new tab in layout file (for example customer_index_edit.xml)
<?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">
    <body>
        <referenceBlock name="customer_form">
            <block class="Amasty\Rewards\Block\Adminhtml\RewardsTab" name="amrewards" />
        </referenceBlock>
    </body>
</page>

2) Creating tab class 

Amasty/Rewards/Block/Adminhtml/RewardsTab.php

which's main part of code is:
public function getTabUrl()
{
    return $this->getUrl('amasty_rewards/rewards/index/', ['_current' => true]);
}

3) Creating new layout file amasty_rewards_rewards_index.xml where we'll specify blocks and grid:
<?xml version="1.0"?>
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
    <update handle="amasty_rewards_rewards_ajaxgrid" />
    <container name="root">
        <block class="Amasty\Rewards\Block\Adminhtml\Edit\Tab\View\PersonalInfo" name="personal_info" template="Amasty_Rewards::tab/view/personal_info.phtml"/>
        <block class="Amasty\Rewards\Block\Adminhtml\Rewards\Edit\NewReward" name="add-points" template="Amasty_Rewards::rewards/edit/new/form.phtml"/>
    </container>
</layout>

4) Creating grid layout file amasty_rewards_rewards_grid.xml:
<?xml version="1.0"?>
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
    <update handle="amasty_rewards_rewards_ajaxgrid"/>
    <container name="root">
    </container>
</layout>

5) And another one amasty_rewards_rewards_ajaxgrid.xml:
<?xml version="1.0"?>
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
    <referenceContainer name="root">
        <block class="\Magento\Backend\Block\Widget\Grid" after="add-points" name="customer.rewards.edit.tab">
            <arguments>
                <argument name="dataSource" xsi:type="object">Amasty\Rewards\Model\ResourceModel\Rewards\Collection\Grid</argument>
                <argument name="id" xsi:type="string">rewardsGrid</argument>
                <argument name="use_ajax" xsi:type="string">true</argument>
                <argument name="default_sort" xsi:type="string">id</argument>
                <argument name="grid_url" xsi:type="url" path="*/*/grid">
                    <param name="_current">1</param>
                </argument>
                <argument name="product_configuration_helpers" xsi:type="array">
                    <item name="default" xsi:type="string">Magento\Catalog\Helper\Product\Configuration</item>
                </argument>
            </arguments>
            <block class="Magento\Backend\Block\Widget\Grid\ColumnSet" as="grid.columnSet" name="customer.rewards.edit.tab.columnSet">
                <arguments>
                    <argument name="empty_text" xsi:type="string" translate="true">No Items Found</argument>
                </arguments>

                <block class="Magento\Backend\Block\Widget\Grid\Column" as="action_date">
                    <arguments>
                        <argument name="header" xsi:type="string" translate="true">Date</argument>
                        <argument name="index" xsi:type="string">action_date</argument>
                        <argument name="gmtoffset" xsi:type="string">1</argument>
                        <argument name="type" xsi:type="string">date</argument>
                        <argument name="id" xsi:type="string">action_date</argument>
                    </arguments>
                </block>
...

We're done with layouts. Now we need Controllers.
6) Grid controller

app/code/Amasty/Rewards/Controller/Adminhtml/Rewards/Grid.php

<?php

namespace Amasty\Rewards\Controller\Adminhtml\Rewards;

class Grid extends \Amasty\Rewards\Controller\Adminhtml\Rewards
{
    public function execute()
    {
        $this->initCurrentCustomer();
        $this->_view->loadLayout();
        $this->_view->renderLayout();
    }
}

7) And index controller

app/code/Amasty/Rewards/Controller/Adminhtml/Rewards/Index.php

<?php

namespace Amasty\Rewards\Controller\Adminhtml\Rewards;

class Index extends \Amasty\Rewards\Controller\Adminhtml\Rewards
{

    public function execute()
    {
        $customerId = $this->initCurrentCustomer();

        $model = $this->_objectManager->create('Amasty\Rewards\Model\Rewards');
        $statistic = $model->getStatistic($customerId);

        $this->_coreRegistry->register('current_amasty_rewards_statistic', $statistic);

        $resultLayout = $this->resultLayoutFactory->create();
        return $resultLayout;
    }
}

And finally here is the result:

