Show active menu items when using Structured Fields

Hi,

I defined my menu using a structured field.
I want to highlight the active link.

<?php
  $items = $site->menu_items()->toStructure();
  foreach ($items as $item): ?>
  <li>
    <a <?php e($item->isOpen(), ' class="active-link"') ?> href="<?= $site->url() ?><?= $item->item_link() ?>">
      <?= $item->item_name() ?>
    </a>
  </li>
<?php endforeach ?>

I think the “isOpen” only works with “<?php foreach ($site->children()->listed() as $item): ?>” right?
How is it possible to highlight an active link when i use a structured field?

Thank you for yor help.

What do your menu items actually link to? Pages?

Hi Sonja,

some items link to pages and some items are anchors.

Well, unfortunately you didn’t post your structure field, but for the actual page links, you can convert the field value to page with toPage(). For this object you can then test if the item is open.

If the value is just a string, it really depend to which page they actually link. Is it just a URL in that case?

Otherwise, you would have to check the URL to determine which link is active.

My structure field:

menu_items:
  label: Menü-Punkte
  type: structure
  fields:
    item_name:
      label: Beschriftung
      type: text
    item_link:
      label: link
      type: text

Ok, since you are not storing pages but only text or urls, you have to check if the current urls is the same as your item link.

Can you please provide an example of how to use the Url::current() object? I’m not having much luck with it.

I’m afraid I don’t quite understand the question. Url::current() is a method (not an object) that is just used like that, no params, no nothing., so you just echo it (or use it for comparisions):

<?= Url::current() ?>

and will return the current Url (with query strings or params etc. if applicable)

I see… the below works similar to isActive(), but I need it to work like isOpen():

<?php e(Url::current() == $item->link()->toUrl(), 'aria-current="page"') ?>

Could you please provide more context. The original TO asked about structure field items… What would be your equivalent to a parent page in this context? what is $item?

I’m trying to rebuild the starterkit menu using structured mixed links as shown on the menu builder page.

With a nested structure field? Which example exactly? Or please post your code…

One level, not nested. The code below works fine when on the actual page, but the active state does not remain when drilling down into a section. For example, /works vs /works?filter=Residential

<?php $items = $site->headerMenu()->toStructure(); ?>
  <?php if ($items->isNotEmpty()) : ?>
    <nav class="menu">
      <ul role="list" class="flex gap-x-6 md:gap-x-9 lg:gap-x-11">
        <?php foreach ($items as $item) : ?>
          <li>
            <a href="<?= $item->link()->toUrl() ?>" <?php e(Url::current() == $item->link()->toUrl(), 'aria-current="page"') ?>>
              <?= $item->linkTitle()->or($item->link()->html()) ?>
            </a>
          </li>
        <?php endforeach ?>
      </ul>
    </nav>
  <?php endif ?>

Really sorry, maybe we have a wording problem here. Your example uses a url query string, that cannot possibly work when you compare a nomal page link with Url::current() which returns exactly a Url with the query string included.

Don’t think there is a method for that, you either have to construct it from the uri object, or use

$_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']

Not sure what you mean by “drilling down into a section”.

If you are using only pages as links, you could compare to the current $page->url(), if you are only dealing with pages, you could also convert the stored UUID to a page object, which would allow you to use $page->isActive() and isOpen

Maybe I should extend the recipe a bit…

I’m using mixed links.

Using isOpen() on a normal menu using pages will set the link active no matter what level you’re at. For example:

/projects
/projects/project-name

…but with a mixed link structured menu, there’s no way to do it. Using Url::current() only sets the link active at the top level /projects, similar to how isActive() works on normal menus.

<?php $items = $site->headerMenu()->toStructure(); ?>
  <?php if ($items->isNotEmpty()) : ?>
    <nav class="menu">
      <ul role="list" class="flex gap-x-6 md:gap-x-9 lg:gap-x-11">
        <?php foreach ($items as $item) : ?>
          <li>
            <a <?= ($p = $item->link()->toPage()) && $p->isOpen() ? 'aria-current="page"' : '' ?> href="<?= $item->link()->toUrl() ?>">
              <?= $item->linkTitle()->or($item->link()->html()) ?>
            </a>
          </li>
        <?php endforeach ?>
      </ul>
    </nav>
  <?php endif ?>

That works, thank you!

Where is the $p coming from? It’s an abbreviation for the $page object?

It’s a variable I define inline here : $p = $item->link()->toPage(), checking if this is a page object.

1 Like