Agregar un enlace que no sea de categoría a los enlaces de navegación en magento 2

29

No estoy seguro de qué estoy haciendo mal aquí. Se hace referencia al bloque que contiene los enlaces de categoría como navigation.sections. Pensé que si dirigía los siguientes argumentos hacia el contenedor, podría crear un nuevo enlace debajo de él. Cualquier ayuda es 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>
themanwhoknowstheman
fuente
Me pregunto lo mismo ... ¿Has encontrado una solución para ello?
Ambas soluciones enumeradas han funcionado para mí.
themanwhoknowstheman
¿En qué versión de Magento estás trabajando?
Razvan Zamfir

Respuestas:

34

[EDITAR]
Aparentemente, en las últimas versiones de M2 ​​esto ya no funciona.
Gracias a Max por señalar esto.
Para la versión posterior, debe agregar un complemento en Magento\Theme\Block\Html\Topmenulugar de un observador.
Agregue esto aetc/frontend/di.xml

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

y crea el archivo de clase de complemento [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)
        ];
    }
}

[/ EDITAR]
Respuesta original:
puede agregar elementos al menú superior utilizando el evento page_block_html_topmenu_gethtml_before.

Por lo tanto, debe crear un módulo con estos archivos (todos los archivos deben estar en app/code/[Namespace]/[Module]):

etc/module.xml - el archivo de declaración del 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 - el archivo de registro

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

etc/frontend/events.xml - el archivo de declaración 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 - el 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;
    }
}

Ahora ejecute en el cli php bin/magento setup:upgradepara instalar el módulo y ya está listo.

Marius
fuente
¿Falta Topmenu.php parte del código?
themanwhoknowstheman
1
@Solide. El orden de los enlaces depende del orden en que se ejecutan los observadores. Si su observador de la página de inicio se ejecuta antes del catálogo, entonces el enlace de la página de inicio debe agregarse primero. Si no, puede echar un vistazo a este enfoque para cambiar el orden de los enlaces: magento.stackexchange.com/q/7329/146 . el enfoque es para Magento1, pero puede traducirlo al código M2.
Marius
1
@ Mario: ¿cuál debería ser el 'is_active'. Por favor agregue algún ejemplo. Quiero un enlace activo en esta página.
zed Blackbeard
1
Se usa un observador en un evento. Un complemento puede funcionar en cualquier método público. Yo recomendaría usar el enfoque de complemento ya que ese se usa en el núcleo para agregar las categorías al menú superior.
Marius
1
Lo siento, me siento como un idiota, pero ¿cómo puedes agregar más de un menú? Si uso $menu->addChild($node)más de una vez, el último anula a los otros. Solo muestra un menú (el último).
pinicio
17

¿Por qué todo el mundo siempre quiere escribir un módulo? Hice esto en mi layout.xmly funcionó a las mil maravillas:

    <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
fuente
¿Cómo abrir ese enlace en una nueva pestaña?
jafar pinjar
Buena pregunta. Encontré algo en el código. Tal vez intente esto: <argumento name = "atributos" xsi: type = "array"> <item name = "target" xsi: type = "string"> _ blank </item> </argument> No probado, pero existe el Opción de atributos disponible.
Johnny Longneck
Crear un módulo lo hace mucho más dinámico. Muchos clientes con los que trabajo quieren hacer cosas ellos mismos, como en este caso crear páginas y agregarlas al menú superior en un orden específico.
Roy Jeurissen
6

Otra solución fuera de crear un módulo es sobrescribir topmenu.phtml. Notaré que la solución provista por @Marius es la mejor manera de hacer esto si desea que sus enlaces hereden las clases de navegación. Esto se muestra en el menú móvil de Magento, solo que sin el CSS apropiado. Podría usar el argumento css_class para diseñar en consecuencia.

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>
themanwhoknowstheman
fuente
¿Dónde puedo encontrar un ejemplo del argumento de la clase css?
camdixon
¿Cómo se vincula el archivo de plantilla al archivo xml ..
Sarvesh Tiwari
6

Esta respuesta es proporcionada por Marius ♦. La acabo de modificar para agregar una categoría secundaria en el menú de pestaña de categoría. Puede consultar la respuesta de Marius ♦. Acabo de modificar el archivo infantil Topmenu.php para agregar una categoría infantil en la categoría 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();
    }
}

Debe crear un nodo para la categoría principal y para la categoría secundaria y, después de eso, puede asignar una categoría secundaria a la categoría principal mediante el método addChild. A continuación se muestra un ejemplo.

$contactsNode->addChild($warrantyRegistrationNode);
Vaibhav Ahalpara
fuente
¡Gracias! ¡no me di cuenta de que era tan fácil agregar Submenú!
Juliano Vargas
y señor si quiero mostrar mi div personalizado en el enlace personalizado que agregué Topmenu. Al igual que cuando paso el mouse sobre el Enlace, muestra mi div
Asad Khan
1

Usando la respuesta anterior de Marius, agregué elementos de submenú. También muestro una forma en que puede editar el árbol antes de que se cree el html y luego cómo editar el html directamente una vez que se crea. Funciona en Magento 2.1. Actualice Topmenu.php con esto:

<?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
fuente
1

Desea agregar un enlace a la navegación superior dentro de <header>
Agregar un enlace a la página CMS, Galería

Editar / colocar default.xml aquí:

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

Agregue el siguiente 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>

Esto agrega un enlace a la página CMS, Galería, con la siguiente configuración:

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

Agregue el siguiente estilo para asegurarse de que el nuevo enlace se alinee correctamente:

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

Resultados del codigo (Los productos se configuran como una categoría para un ejemplo)

Joshua34
fuente
0

Para aquellos que buscan agregar is_activeexpresión, especialmente @zed Blackbeard que preguntó anteriormente.

Solía ​​vincular el contacto y también funcionará con un módulo personalizado, ya que estoy vinculando a uno.

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

// (expresión para determinar si el elemento del menú está seleccionado o no)

Espero que ayude a cualquiera.

Juliano Vargas
fuente
0

Esta también es una buena opción:

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
fuente
-1

Solo para un enlace del menú de navegación, no hay muchos pasos para lograr, he encontrado un breve tutorial sobre cómo hacerlo, implica un tema que anula el topmenu.phtmlarchivo del Magento_Thememódulo: https://linkstraffic.net/adding-custom- menu-item-inside-magento2 / Lo he probado con éxito, así que lo comparto con ustedes.

Palanqueta
fuente
Bienvenido a Magento SE. Si publica enlaces en una respuesta, asegúrese de que la respuesta siga siendo valiosa, si el enlace deja de funcionar en algún momento: por ejemplo, resuma el artículo vinculado o cite las partes relevantes. Esto es importante porque StackExchange pretende ser una base de datos de conocimiento, no un foro de soporte que ayude a una persona en este momento. Los visitantes futuros aún deberían beneficiarse de las preguntas y respuestas.
Siarhey Uchukhlebau