Have hunted round for this but can't find an answer. As the title suggests I want to add a custom attribute to a container.
I currently have a container in layout.xml as such;
<container name="my.custom.container" as="my_custom_container" htmlTag="div" htmlClass="myCustomContainer" htmlId="customContainer">
which generates
<div class="myCustomContainer" id="customContainer"></div>
as expected.
How do I add a custom attribute to this? E.g. (As to to end up with)
<div class="myCustomContainer" id="customContainer" customAttribute></div>
Thanks
3 Answers 3
Unfortunately, there's no way you can do this via XML out of the box.
Magento 2 handles the htmlTag, htmlId and htmlClass via the Magento/Framework/View/Layout.php file under the _renderContainer method:
protected function _renderContainer($name)
{
$html = '';
$children = $this->getChildNames($name);
foreach ($children as $child) {
$html .= $this->renderElement($child);
}
if ($html == '' || !$this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_TAG)) {
return $html;
}
$htmlId = $this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_ID);
if ($htmlId) {
$htmlId = ' id="' . $htmlId . '"';
}
$htmlClass = $this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_CLASS);
if ($htmlClass) {
$htmlClass = ' class="' . $htmlClass . '"';
}
$htmlTag = $this->structure->getAttribute($name, Element::CONTAINER_OPT_HTML_TAG);
$html = sprintf('<%1$s%2$s%3$s>%4$s</%1$s>', $htmlTag, $htmlId, $htmlClass, $html);
return $html;
}
As you can see, this method does not render any extra attributes than the ones I mentionned.
A alternative would be to use JavaScript to add the attribute based on the id you've given to your tag, for example:
jQuery('#customContainer').attr('customAttribute','customValue);
-
@Matt feel free to upvote and/or mark your question as answered so it will help other people with the same issueRaphael at Digital Pianism– Raphael at Digital Pianism2016年04月25日 10:28:27 +00:00Commented Apr 25, 2016 at 10:28
-
Is there a way to extend theses classes to add the functionality?Black– Black2020年01月17日 14:05:39 +00:00Commented Jan 17, 2020 at 14:05
Raphael is right, there is no way to do this out of the box. There is a way if you add some custom functionality though.
It takes a custom module and an event observer to make this happen. You were right that there seems to be nothing out there at all to achieve this, I came to the same conclusion! No more.
Create a module, let's call it Stackoverflow_ContainerAttribute. Add all the standard module declaration. Now the next two files and code are the important parts:
etc/frontend/events.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="core_layout_render_element">
<observer name="core_layout_render_container_attribute" instance="Stackoverflow\ContainerAttribute\Observer\LayoutSchemaAttributesObserver" />
</event>
</config>
Observer/LayoutSchemaAttributesObserver.php
namespace Stackoverflow\ContainerAttribute\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\View\Layout\Element;
class LayoutSchemaAttributesObserver implements ObserverInterface
{
public function execute(\Magento\Framework\Event\Observer $observer)
{
/** @var \Magento\Framework\View\Layout $layout */
$layout = $observer->getEvent()->getLayout();
$elementName = $observer->getEvent()->getElementName();
if($layout->isContainer($elementName) && ($containerTag = $layout->getElementProperty($elementName, Element::CONTAINER_OPT_HTML_TAG))) {
$nodes = $layout->getXpath(sprintf('//*[@name="%s"]/attribute[@name]', $elementName));
if ($nodes) {
/** @var \SimpleXMLElement $_node */
foreach ($nodes as $_node) {
$name = $_node->attributes()->name;
$value = $_node->attributes()->value;
$output = $observer->getEvent()->getTransport()->getOutput();
$output = preg_replace(
"/^(<$containerTag.*?)(>)/",
sprintf("1ドル %s2ドル", ($name && $value) ? sprintf("%s=\"%s\"", $name, $value) : $name),
$output
);
$observer->getEvent()->getTransport()->setOutput($output);
}
}
}
}
}
We're essentially adding custom behaviour to layout XML here. The approach of finding our custom attribute nodes using XPath may look a bit weird, but I personally feel it's perfectly normal and not hacky at all. Magento 2 uses XPaths to gather information about layout XML all over the place in similar ways.
With that in place, in layout XML you can now add nodes that attributes in layout XML or reference a container using referenceContainer and add nodes there. Use the syntax below to add attributes (valued or non-valued):
<container name="my.custom.container" as="my_custom_container" htmlTag="div" htmlClass="myCustomContainer" htmlId="customContainer">
<attribute name="customAttribute" />
<attribute name="customAttributeTwo" value="42" />
</container>
or if it were another container you wanted to add values to:
<referenceContainer name="product.info.main">
<attribute name="customAttribute" />
<attribute name="customAttributeTwo" value="42" />
</referenceContainer>
Note: Don't confuse this new <attribute> node with the <attribute> node that can be placed directly inside of the <body> node in layout XML. That only works out of the box on the body tag. This adds support for containers using the same syntax, the implementation is completely separate.
-
Does not work anymore in 2.2 ... XSD verification make this impossible..Pol Ravalitera– Pol Ravalitera2018年02月05日 12:56:46 +00:00Commented Feb 5, 2018 at 12:56
-
Oh really @PolRavalitera, well that sucks! Sounds like a double edged sword I guess. Better validation is a good thing but it stops stuff like this working. Probably then, the only way to allow this to work would be by implementing something like this: magento.stackexchange.com/questions/102498/…Josh Davenport-Smith– Josh Davenport-Smith2018年02月05日 17:54:52 +00:00Commented Feb 5, 2018 at 17:54
-
In fact, i was already using the @Raphael method, to create "new attributes", like "role", that is not in the XSDs... So i had an horrible idea: putting a string in htmlId, that i parse. To add attributes, to completely remove tag (very pratical to handle containers. It was possible in 2.1, but did not knew it was a bug...) As you say, double edged ;)Pol Ravalitera– Pol Ravalitera2018年02月06日 09:44:02 +00:00Commented Feb 6, 2018 at 9:44
-
2Can we get this added as a PR? I mean, given my experience with core PR's it'll get rejected, but man, that's so useful!Nathaniel Rogers– Nathaniel Rogers2019年08月21日 06:29:13 +00:00Commented Aug 21, 2019 at 6:29
Josh, I think that is a great solution. However, i get an error that shows a bunch of white text on the page starting with
namespace Qode1\ContainerAttribute\Observer; use
Magento\Framework\Event\ObserverInterface; use
Magento\Framework\View\Layout\Element; class
LayoutSchemaAttributesObserver implements ObserverInterface { public
function execute(\Magento\Framework\Event\Observer $observer) {
and continuing for quite a while like this.
This happens after I enable the module and viewing the front page. I must be missing something. I thought the only thing I changed was the namespace.
I believe this is the secret to getting UIkit to work on Magento 2.