Adicionando um link de não categoria aos links de navegação no magento 2

29

Não tenho certeza do que estou fazendo de errado aqui. O bloco que contém os links da categoria é mencionado como navigation.sections. Pensei em direcionar os seguintes argumentos para o contêiner que eu seria capaz de criar um novo link sob ele. Qualquer ajuda é apreciada.

<referenceContainer name="navigation.sections">
            <block class="Magento\Framework\View\Element\Html\Links" name="mylink">
                    <arguments>
                        <argument name="label" xsi:type="string">Mylink</argument>
                        <argument name="path" xsi:type="string">mypath</argument>
                        <argument name="css_class" xsi:type="string">mycss</argument>
                    </arguments>
            </block>
</referenceContainer>
o homem que conhece o homem
fonte
Estou pensando o mesmo .. Você encontrou uma solução para isso?
Ambas as soluções listadas funcionaram para mim.
themanwhoknowstheman
Em qual versão do Magento você está trabalhando?
Razvan Zamfir 26/10

Respostas:

34

[EDIT]
Aparentemente, nas versões mais recentes do M2 isso não funciona mais.
Obrigado a Max por apontar isso.
Para a versão posterior, você precisa adicionar um plugin para, em Magento\Theme\Block\Html\Topmenuvez de um observador.
Adicione isto aetc/frontend/di.xml

<type name="Magento\Theme\Block\Html\Topmenu">
    <plugin name="[module]-topmenu" type="[Namespace]\[Module]\Plugin\Block\Topmenu" />
</type>

e crie o arquivo de classe do plug-in [Namespace]/[Module]/Plugin/Block/Topmenu.php

<?php 

namespace [Namespace]\[Module]\Plugin\Block;

use Magento\Framework\Data\Tree\NodeFactory;

class Topmenu
{
    /**
     * @var NodeFactory
     */
    protected $nodeFactory;

    public function __construct(
        NodeFactory $nodeFactory
    ) {
        $this->nodeFactory = $nodeFactory;
    }

    public function beforeGetHtml(
        \Magento\Theme\Block\Html\Topmenu $subject,
        $outermostClass = '',
        $childrenWrapClass = '',
        $limit = 0
    ) {
        $node = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray(),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $subject->getMenu()->addChild($node);
    }

    protected function getNodeAsArray()
    {
        return [
            'name' => __('Label goes here'),
            'id' => 'some-unique-id-here',
            'url' => 'http://www.example.com/',
            'has_active' => false,
            'is_active' => false // (expression to determine if menu item is selected or not)
        ];
    }
}

[/ EDIT]
Resposta original:
você pode adicionar elementos ao menu superior usando o evento page_block_html_topmenu_gethtml_before.

Portanto, você precisa criar um módulo com esses arquivos (todos os arquivos devem estar dentro app/code/[Namespace]/[Module]):

etc/module.xml - o arquivo de declaração do módulo

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="[Namespace]_[Module]" setup_version="2.0.0">
        <sequence>
            <module name="Magento_Theme"/>
        </sequence>
    </module>
</config>

registration.php - o arquivo de registro

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    '[Namespace]_[Module]',
    __DIR__
);

etc/frontend/events.xml - o arquivo de declaração de eventos

<?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="page_block_html_topmenu_gethtml_before">
        <observer name="[namespace]_[module]_observer" instance="[Namespace]\[Module]\Observer\Topmenu" />
    </event>
</config>

Observer/Topmenu.php - o observador real

<?php
namespace [Namespace]\[Module]\Observer;
use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Data\Tree\Node;
use Magento\Framework\Event\ObserverInterface;
class Topmenu implements ObserverInterface
{
    public function __construct(
        ...//add dependencies here if needed
    )
    {
    ...
    }
    /**
     * @param EventObserver $observer
     * @return $this
     */
    public function execute(EventObserver $observer)
    {
        /** @var \Magento\Framework\Data\Tree\Node $menu */
        $menu = $observer->getMenu();
        $tree = $menu->getTree();
        $data = [
            'name'      => __('Menu item label here'),
            'id'        => 'some-unique-id-here',
            'url'       => 'url goes here',
            'is_active' => (expression to determine if menu item is selected or not)
        ];
        $node = new Node($data, 'id', $tree, $menu);
        $menu->addChild($node);
        return $this;
    }
}

Agora execute o cli php bin/magento setup:upgradepara instalar o módulo e você estará pronto.

Marius
fonte
O Topmenu.php está faltando parte do código?
Themanwhoknowstheman
1
@Solide. A ordem dos links depende da ordem em que os observadores são executados. Se o observador da sua página inicial for executado antes do catálogo, o link da página inicial deve ser adicionado primeiro. Caso contrário, você pode dar uma olhada nesta abordagem para alterar a ordem dos links: magento.stackexchange.com/q/7329/146 . a abordagem é para o Magento1, mas você pode traduzi-lo para o código M2.
Marius
1
@ Marius: qual deve ser o 'is_active'. Por favor, adicione alguns exemplos. Quero link ativo nesta página.
precisa
1
Um observador é usado em um evento. Um plug-in pode funcionar em qualquer método público. Eu recomendaria usar a abordagem de plug-in, pois essa é usada no núcleo para adicionar as categorias ao menu superior.
Marius
1
Desculpe, me sinto um idiota, mas como você pode adicionar mais de um menu? Se eu usar $menu->addChild($node)mais de uma vez, o último substituirá os outros. Ele mostra apenas um menu (o último).
pinicio 23/02
17

Por que todo mundo sempre quer escrever um módulo. Eu fiz isso no meu layout.xmle funcionou como um encanto:

    <referenceBlock name="catalog.topnav">
        <block class="Magento\Framework\View\Element\Html\Link" name="contact-link">
            <arguments>
                <argument name="label" xsi:type="string" translate="true">Contact us</argument>
                <argument name="path" xsi:type="string" translate="true">contact</argument>
            </arguments>
        </block>
    </referenceBlock>
Johnny Longneck
fonte
como abrir esse link em uma nova guia?
jafar pinjar
Boa pergunta. Encontrei algo no código. Talvez tente o seguinte: <argument name = "attribute" xsi: type = "array"> <item name = "target" xsi: type = "string"> _ blank </item> </argument> Não testado, mas existe o opção de atributos disponível.
Johnny Longneck 28/09
Criar um módulo o torna muito mais dinâmico. Muitos clientes com quem trabalho desejam fazer as próprias coisas, como neste caso, criar páginas e adicioná-las ao menu principal em uma ordem específica.
Roy Jeurissen 22/03
6

Outra solução fora da criação de um módulo é substituir topmenu.phtml. Observarei que a solução fornecida pelo @Marius é a melhor maneira de fazer isso se você pretende que seus links herdem as classes de navegação. Isso aparece no menu móvel do Magento, apenas sem o css adequado. Você pode usar o argumento css_class para estilizar adequadamente.

YourTheme / Magento_Theme / templates / html / topmenu.phtml

<?php $columnsLimit = $block->getColumnsLimit() ?: 0; ?>
<?php $_menu = $block->getHtml('level-top', 'submenu', $columnsLimit) ?>

<nav class="navigation" role="navigation">
    <ul data-mage-init='{"menu":{"responsive":true, "expanded":true, "position":{"my":"left top","at":"left bottom"}}}'>
        <?php /* @escapeNotVerified */ echo $_menu; ?>
        <?php echo $block->getChildHtml() ?>
    </ul>
</nav>

YourTheme / Magento_Theme / layout / default.xml

<referenceContainer name="catalog.topnav">
               <block class="Magento\Framework\View\Element\Html\Link\Current" name="your.link">
                    <arguments>
                        <argument name="label" xsi:type="string">Link-name</argument>
                        <argument name="path" xsi:type="string">Link-url</argument>
                    </arguments>
              </block>
</referenceContainer>
o homem que conhece o homem
fonte
Onde posso encontrar um exemplo do argumento da classe css?
camdixon
como você vincular arquivo de modelo para arquivo xml ..
Sarvesh Tiwari
6

Esta resposta é fornecida por Marius ♦ Acabei de modificá-la para adicionar categoria filho no menu da guia categoria. Você pode consultar a resposta de Marius ♦. Acabei de modificar o arquivo filho Topmenu.php para adicionar a categoria filho na categoria principal

<?php 

namespace Ktpl\Navigationlink\Plugin\Block;

use Magento\Framework\UrlInterface;
use Magento\Framework\Data\Tree\NodeFactory;
use Magento\Store\Model\StoreManagerInterface;

class Topmenu
{
    /**
     * @var NodeFactory
     */
    protected $nodeFactory;
    protected $urlBuilder;
    protected $_storeManager;

    public function __construct(
        UrlInterface $urlBuilder,
        NodeFactory $nodeFactory,
        StoreManagerInterface $storeManager
    ) {
        $this->urlBuilder = $urlBuilder;
        $this->nodeFactory = $nodeFactory;
        $this->_storeManager = $storeManager;
    }

    public function beforeGetHtml(
        \Magento\Theme\Block\Html\Topmenu $subject,
        $outermostClass = '',
        $childrenWrapClass = '',
        $limit = 0
    ) {
        // condition for store
        if($this->getStoreCode() == 'store_id'):
        $productNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Products','products'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $stockistsNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Stockists','stockists'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $ourstoryNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Our Story','ourstory'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $contactsNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Customer Care','contacts'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        /******* contacts's child *******/
        $warrantyRegistrationNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Warranty Registration','warranty-registration'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $faqNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Frequently Asked Questions','faq'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $ourProductGuaranteeNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Our Product Guarantee','our-product-guarantee'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $warrantiesNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Warranties, Repairs & Spare Parts','warranties-repairs-spare-parts'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $termsNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Terms & Conditions','terms-and-conditions'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $privacyPolicyNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Our Privacy Policy','privacy-policy'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );
        $bookNode = $this->nodeFactory->create(
            [
                'data' => $this->getNodeAsArray('Book A Viewing','book-a-viewing'),
                'idField' => 'id',
                'tree' => $subject->getMenu()->getTree()
            ]
        );

        $contactsNode->addChild($warrantyRegistrationNode);
        $contactsNode->addChild($faqNode);
        $contactsNode->addChild($ourProductGuaranteeNode);
        $contactsNode->addChild($warrantiesNode);
        $contactsNode->addChild($termsNode);
        $contactsNode->addChild($privacyPolicyNode);
        $contactsNode->addChild($bookNode);
        /******* end contacts's child *******/

        $subject->getMenu()->addChild($productNode);
        $subject->getMenu()->addChild($stockistsNode);
        $subject->getMenu()->addChild($ourstoryNode);
        $subject->getMenu()->addChild($contactsNode);
        endif;
    }

    protected function getNodeAsArray($name,$id)
    {
        return [
            'name' => __($name),
            'id' => $id,
            'url' => $this->urlBuilder->getUrl($id),
            'has_active' => false,
            'is_active' => false // (expression to determine if menu item is selected or not)
        ];
    }

    public function getStoreCode()
    {
        return $this->_storeManager->getStore()->getCode();
    }
}

Você precisa criar um nó para a categoria pai e para a categoria filho e, depois disso, pode atribuir a categoria filho à categoria pai usando o método addChild. Aqui está um exemplo

$contactsNode->addChild($warrantyRegistrationNode);
Vaibhav Ahalpara
fonte
Obrigado! não sabia que era tão fácil adicionar o submenu!
Juliano Vargas
e Sir, se eu quiser mostrar minha div personalizada no link personalizado que eu adicionei Topmenu. Como quando passe o mouse sobre o link, ele mostra minha div
Asad Khan
1

Usando a resposta acima de Marius, adicionei itens de submenu. Também mostro uma maneira de editar a árvore antes da criação do html e, em seguida, como editar o html diretamente, uma vez criado. Funciona no Magento 2.1. Atualize o Topmenu.php com isso:

<?php
namespace Seatup\Navigation\Observer;
use Magento\Framework\Event\Observer as EventObserver;
use Magento\Framework\Data\Tree\Node;
use Magento\Framework\Event\ObserverInterface;
class Topmenu implements ObserverInterface
{
    protected $_cmsBlock;

    public function __construct(
        \Magento\Cms\Block\Block $cmsBlock
    )
    {
        $this->_cmsBlock = $cmsBlock;
    }
    /**
     * @param EventObserver $observer
     * @return $this
     */
    public function execute(EventObserver $observer)
    {
        /** @var \Magento\Framework\Data\Tree\Node $menu */
        $eventName = $observer->getEvent()->getName();
        if($eventName == 'page_block_html_topmenu_gethtml_before'){
            // With the event name you can edit the tree here
            $menu = $observer->getMenu();
            $tree = $menu->getTree();
            $children = $menu->getChildren();

            foreach ($children as $child) {
                if($child->getChildren()->count() > 0){ //Only add menu items if it already has a dropdown (this could be removed)
                    $childTree = $child->getTree();
                    $data1 = [
                        'name'      => __('Menu item label here'),
                        'id'        => 'some-unique-id-here',
                        'url'       => 'url goes here',
                        'is_active' => FALSE
                    ];
                    $node1 = new Node($data1, 'id', $childTree, $child);
                    $childTree->addNode($node1, $child);
                }
            }
            return $this;
        } else if($eventName == 'page_block_html_topmenu_gethtml_after'){
            // With the event name you can edit the HTML output here
            $transport = $observer['transportObject'];

            //get the HTML
            $old_html = $transport->getHtml();

            //render the block. I am using a CMS block
            $new_output = $this->_cmsBlock->getLayout()->createBlock('Magento\Cms\Block\Block')->setBlockId('cms_block_identifier')->toHtml();
            //the transport now contains html for the group/class block
            //which doesn't matter, because we already extracted the HTML into a 
            //string primitive variable
            $new_html = str_replace('to find', $new_output , $old_html);    
            $transport->setHtml($new_html);
        }
    }
}
Cypher909
fonte
1

Deseja adicionar um link à navegação superior em <header>
Adicionando um link à página do CMS, Galeria

Edite / coloque default.xml aqui:

app/design/frontend/Vendor/theme/Magento_Theme/layout/default.xml

Adicione o seguinte código:

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="catalog.topnav">
           <block class="Magento\Framework\View\Element\Html\Link\Current" name="gallery.link">
                <arguments>
                    <argument name="label" xsi:type="string">Gallery</argument>
                    <argument name="path" xsi:type="string">gallery</argument>
                </arguments>
          </block> 
       </referenceContainer>
    </body>
</page>

Isso adiciona um link à página do CMS, Galeria, com as seguintes configurações:

Title = Gallery
Url Key = gallery
Link = https://example.com/gallery/

Adicione o seguinte estilo para garantir que o novo link esteja alinhado corretamente:

.navigation .nav.item {
margin: 0 10px 0 0;
display: inline-block;
position: relative;
}

Resultados do código (Os produtos são configurados como uma categoria, por exemplo)

Joshua34
fonte
0

Para aqueles que procuram adicionar is_activeexpressão, especialmente @zed Barba Negra, que perguntou acima.

Eu costumava vincular o contato e ele também funcionará com o módulo personalizado, pois estou vinculando a um.

'is_active' => ($ this-> request-> getFrontName () == 'contato'? true: false)

// (expressão para determinar se o item de menu está selecionado ou não)

Espero que ajude alguém.

Juliano Vargas
fonte
0

Esta também é uma boa opção:

app / design / frontend / Vender / yourtheme / Magento_Theme / layout / default.xml

<referenceBlock name="header.links">
    <block class="Magento\Framework\View\Element\Html\Link" name="yourlinkname" before='wish-list-link'>
        <arguments>
            <argument name="label" xsi:type="string" translate="true">yourlink</argument>
            <argument name="path" xsi:type="string" translate="true">yourlink</argument>
        </arguments>
    </block>
</referenceBlock>
Sarfaraz bheda
fonte
-1

Apenas para um link do menu de navegação, não há muito o que fazer, encontrei um breve tutorial sobre isso, implica um tema que substitui o topmenu.phtmlarquivo do Magento_Thememódulo: https://linkstraffic.net/adding-custom- menu-item-inside-magento2 / Eu testei com sucesso, então eu compartilho com vocês.

Jimmy
fonte
Bem-vindo ao Magento SE. Se você postar links em uma resposta, verifique se a resposta ainda é valiosa, se o link ficar morto em algum momento: Por exemplo, resuma o artigo vinculado ou cite as partes relevantes. Isso é importante porque o StackExchange pretende ser um banco de dados de conhecimento, não um fórum de suporte que ajude uma pessoa no momento. Os futuros visitantes ainda devem se beneficiar das perguntas e respostas.
Siarhey Uchukhlebau