6

Using 2.1.3, I am loading admin tab content by calling a block directly in the Tabs.php file:

protected function _beforeToHtml()
{
    $blockClass = "MyNamespace\MyModule\Block\Adminhtml\Section\Edit\Tabs\BlockClassName";
    $this->addTab(
       'tab_title',
        [
            'label' => __('Tab Label'),
            'title' => __('Tab Title'),
            'content' => $this->getLayout()->createBlock($blockClass)->toHtml(),
            'active' => true
        ]
    );
}

Within the template phtml file I've set in the BlockClassName.php file, I'd like to use $block->getChildHtml('child_block_name').

I've tried using the addChild (per Adding a Child Block Programmatically) to the BlockClassName::_construct(), but am getting an error:

vendor/magento/framework/Data/Structure.php(603): Magento\Framework\Data\Structure->_assertElementExists(NULL);

Where it looks like the vendor/magento/framework/View/Element/AbstractBlock::setChild() is trying to pass in the block's layout name (line 380) - do I need to define the block in a layout in order to add a child block?

6

No. Magento uses one central layout tree, not multiple depending where they are defined. However, if you look in \Magento\Framework\View\Layout\Generator\Block::createBlock(), you'll notice that the block is created (and the constructors are called) before the name is set:

public function createBlock($block, $name, array $arguments = [])
{
    $block = $this->getBlockInstance($block, $arguments);
    $block->setType(get_class($block));
    $block->setNameInLayout($name);
    $block->addData(isset($arguments['data']) ? $arguments['data'] : []);
    return $block;
}

The getBlockInstance() method creates the block and calls the constructors. Then, the name is set.

Next, the block is set in layout, in the \Magento\Framework\View\Layout\Generator\Block::process() method. Later on in that method, the setLayout() method is called on each block, which provides the block with an instance of Magento\Framework\View\Layout and calls the _prepareLayout() method on each block. The right place to set children is inside your block's _prepareLayout() method, since this will be called after the name is set, the block is set in layout, and the block has a reference to the layout object.

I'd do it like this:

protected function _prepareLayout()
{
    $childBlock = $this->getLayout()->createBlock('Your\Class');
    $this->addChild($childBlock);
}

Please let me know if you have any questions!

  • I'm currently on 2.1.8, and the addChild signature is actually public function addChild($alias, $block, $data = []), where the alias and block are strings, and the createBlock is the first thing done inside. So you call it like $this->addChild('my-alias', 'Vendor\Module\Block\Name'). – cyk Aug 24 '17 at 17:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.