Jump to content

Why I love the Latte Template Engine


bernhard

Recommended Posts

6 minutes ago, dotnetic said:

think you can drop the parentheses after items or content. Also you might want to use a n:if clause on the ul, so the markup is discarded if there are no items.

No, because $block is a rockmatrix block having an items() method but not having an items property. Same goes for content(). The whole block will only render if there are items using RockFrontend's renderIf() method:

$rockfrontend->renderIf("sections/newsitems.latte", $block->items())

 

10 minutes ago, dotnetic said:

Or just use

{dump $page->title}

Thx for letting me know ? Though personally I don't see any benefit in using another syntax if I can simply use plain PHP (which I already know)...

  • Like 1
Link to comment
Share on other sites

19 minutes ago, dotnetic said:

I am also using Latte in an actual project and love it so far. For the integration into ProcessWire I am using  TemplateEngineFactory. It also has a Latte renderer (but with Latte 2 atm, actual version is 3).

Did you run into any breaking changes upgrading from Latte 2 to 3? I haven't yet updated the Latte renderer for TemplateEngineFactory as I haven't had enough time to give Latte 3 a go.

Link to comment
Share on other sites

3 minutes ago, d'Hinnisdaël said:

Did you run into any breaking changes upgrading from Latte 2 to 3

I did not upgrade yet, I am in the process of integration Latte v3 into the TemplateEngineLatte renderer, but still need time for it. Right now I am using v2

Link to comment
Share on other sites

13 minutes ago, bernhard said:

Thx for letting me know ? Though personally I don't see any benefit in using another syntax if I can simply use plain PHP (which I already know)

This is just syntactic sugar and uses Tracys debug bar, similar to using `{bd ($myvar)}.

Using things like tags and filters are also syntactic sugar. 

{$page->intro|noescape}
// or
<div n:if="$features">

Tags have some advantages over plain PHP in the template context. For example they are chainable and easy to read/write:

{$page->intro|noescape|upper} // outputs unescaped HTML content in uppercase

 

Link to comment
Share on other sites

6 minutes ago, dotnetic said:

This is just syntactic sugar and uses Tracys debug bar, similar to using `{bd ($myvar)}.

It still looks like it's doing the exact same thing to me?

  {dump $site}
  {bd($site)}

image.thumb.png.450aea2c3ff4009df5c82f418edc692a.png

I'd hoped that your version might make the click below the dump work so that I end up at the correct line in my latte file, but it always leads me to the cached/compiled version of latte...

Link to comment
Share on other sites

1 hour ago, bernhard said:

It still looks like it's doing the exact same thing to me?

 

Yes exactly but with an easier syntax (imho), but dump is just one example.

I don't know anything about the caching, and yeah it is bad that not the real file is shown in the dump

Link to comment
Share on other sites

  • 1 month later...

PM: "can we have the separator line not only on top of each list item but also after the last one?"

<div n:foreach="$block->steps() as $step">
  {$rockfrontend->render('img/hr.svg')}
  <div>...</div>
  {$rockfrontend->renderIf('img/hr.svg', $iterator->last)}
</div>

The $iterator is available automatically in LATTE loops! And it plays perfectly together with renderIf() ?

See https://latte.nette.org/en/tags#toc-iterator

Link to comment
Share on other sites

PHP is for me unfortunately something that does not come very inuitive (so, I guess you know now I am by no means a hardcore developer).
So it´s always time consuming with lots of trial and error to find (forum, google) code snippets that do what I want to do.

Since I started using LATTE thanks to this thread and thanks to @bernhard and his RockFrontend module, it seems like a gamechanger for me.
Processwire has become extremly more usefull and inutitive for me since. Thank you!

I am currently rebuilding my code templates I use as starter-kits for new projects and convert them to LATTE. 

Does anyone have and is willing to share a skeleton template to build a nav-tree menu (mulitlevel) with LATTE?

Something that produces similar HTML code like this:

<ul class='navbar-nav'>
  <li class='nav-item  active'>
    <a href='/processwire/' class='nav-link'>Home</a>
  </li>
  <li class='nav-item dropdown'>
    <a href='/processwire/kontakt/' class='nav-link dropdown-toggle'>Kontakt</a>
    <ul class='dropdown-menu'>
      <li class='nav-item'>
        <a href='/processwire/kontakt/kontakt-unterseite-a/' class='dropdown-item'>Kontakt Unterseite A</a>
      </li>
    </ul>
  </li>
  <li class='nav-item'>
    <a href='/processwire/seite-b/' class='nav-link'>Seite B</a>
  </li>
</ul>

  • Like 1
Link to comment
Share on other sites

8 minutes ago, maetmar said:

Does anyone have and is willing to share a skeleton template to build a nav-tree menu (mulitlevel) with LATTE?

Just wanted to point out that when it comes to building menus, it can get a bit... complicated. This is why modules such as MarkupMenu exist. I'm not saying that you can't build your own menu logic without a module using custom code, just that it's — in my opinion — one of those things that you don't necessarily need to ?

Depends a lot on your use case, of course; some sites have a super simple menu logic. And sorry for going off-topic, as well as shameless self-promotion via MarkupMenu ?

Link to comment
Share on other sites

On 9/23/2022 at 2:25 PM, maetmar said:

Since I started using LATTE thanks to this thread and thanks to @bernhard and his RockFrontend module, it seems like a gamechanger for me.
Processwire has become extremly more usefull and inutitive for me since. Thank you!

Thx! I'm happy to hear that ? 

This is a completely recursive multi-level menu using LATTE + RockFrontend magic that should get you started ? 

<ul class="uk-nav uk-nav-primary uk-margin-top uk-margin-large-bottom">
  {* define block that is used for recursion *}
  {define items, $items, $first}
    {foreach $items as $item}{* loop all items*}
      {* define variables for inside the loop *}
      {var $active = $rockfrontend->isActive($item)}
      {var $subid = "tm-menu-".$item->id}
      {var $numc = ($item->numChildren() && $item!==$first) }
      {* list item markup *}
      <li n:class="$active ? 'uk-active'">
        <a href="{$item->url}" n:attr="rf-toggle: $numc ? '#'.$subid">
          {$item->title}
          <svg n:if="$numc" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--tabler" width="20" height="20" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m6 9l6 6l6-6"></path></svg>
        </a>
        {* list for child-items *}
        <ul id="{$subid}" n:if="$numc" class="uk-nav-sub" {!$active?'hidden'}>
          {include items, $item->children()->prepend($item), $item}
        </ul>
      </li>
    {/foreach}
  {/define}
  {* now include the block for the first level of items *}
  {include items, $home->children()}
</ul>

 

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Thanks for the menu suggestion @bernhard. I have done a variation on this to do a hierarchical menu which relects the page tree structure. The code below should be considered illustrative, rather than a recipe. In particular, you will need to adapt it to whatever css framework you are using. Also, my "nav.latte" is in the context of a repeater matrix item (part of my page builder framework), so the called methods live in the custom page class of the getForPage() (custom page classes don't work with repeaters directly).

{* in nav.latte *}

{var $selector = ($page->theme_selector) ?: "template=ThemeDisplay"} {* specific to my page builder. Define whatever selector you want for menu items here. *}

{* define block that is used for recursion *}
{define items, $items, $first, $selector, $shown = new ProcessWire\PageArray}
{do $items = $page->getForPage()->treeSort($items)} // or you could put the function in init.php, suitably modified
    {foreach $items as $item}{* loop all items*}
        {* define variables for inside the loop *}
        {var $subid = "tm-menu-" . $item->id}
        {var $numc = ($item->numChildren($selector) && $item !== $first)}
        {var $active = (($item == $rockfrontend->wire->page) || ($numc && $item->children()->has($rockfrontend->wire->page)))}
        {* I had a problem with $rrockfrontend->isActive() not highlighting what I wanted, hence the above custom code *}
        {* list item markup (NB Bulma css) *}
        <a n:if="!$numc && !($shown->has($item) && $item !== $first)" href="{$item->url}" n:class="navbar-item, $active ? 'is-active'">
            {$item->title}
            {do $shown->add($item)}
        </a>
        <div n:if=$numc class="navbar-item has-dropdown is-hoverable">
            <a n:class="navbar-link, $active ? 'is-active'" href="{$item->url}">{$item->title}
            </a>
            {do $shown->add($item)}
            {* list for child-items *}
            <div class="navbar-dropdown">
                {var $children = $item->children($selector)}
                {include items, $children->prepend($item), $item, $selector, $shown}
                {do $shown->add($children)}
            </div>
        </div>
    {/foreach}
{/define}

<nav class="navbar is-fixed-top is-transparent" role="navigation" style="{$page->theme_style|noescape}"
     aria-label="main navigation">
    <div class="navbar-brand">
        <a class="navbar-item" href="{$urls->root}">
            <img src="{$logo->url}" alt="{$logo->description}">
        </a>
        <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false"
           data-target="navbarMain">
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
            <span aria-hidden="true"></span>
        </a>
    </div>
    <div id="navbarMain" class="navbar-menu">
        <div class="navbar-start">

            {* now include the block for the first level of items *}
  {include items, $pages->find($selector), $home, $selector}
        </div>

    </div>
</nav>
/* In the custom page class */

/**
	 * Sort pages by how they appear in the page tree hierarchy
	 *
	 * @param $pages
	 * @return void
	 */
	public function treeSort($pageItems) {
		foreach($pageItems as $index => $item) {
			$item->treeSortVal = $this->treeSortVal($item)[0];
		}
		$pageItems->sort('treeSortVal');
		return $pageItems;
	}

	private function treeSortVal($item) {
		$val = $item->sort + 1;
		$increment = 1;
		if ($item->numParents() > 0 && $item->parent() !== $this->pages()->get('/')) {
			$siblingNum = max($item->siblings()->explode('sort'));
			$parentVal = $this->treeSortVal($item->parent());
			$increment = (1 / ($siblingNum + 2)) * $parentVal[1];
			// (add 2 because we added 1 to sort to get $val, then we want 1 more than the greatest $val so that the fraction is always less than 1)
			$val = ($val * $increment) + $parentVal[0];
		} else {
		}
		return [$val, $increment];
	}

 

  • Like 1
Link to comment
Share on other sites

1 hour ago, MarkE said:
{* I had a problem with $rrockfrontend->isActive() not highlighting what I wanted, hence the above custom code *}

Could you please explain that in detail? This is one of the things that a really believe should not be built custom for every project. They should be done in a place where we can share logic across project and simply call the function that should work everywhere.

What I also do not understand: What do you mean by reflect the page tree hierarchy? And what are the custom page class methods for? Doesn't my menu version do the same just without those helper functions? If I call $page->children() then they are already sorted in the correct order, no?

Link to comment
Share on other sites

55 minutes ago, bernhard said:

Could you please explain that in detail?

isActive() selects the parent page as active as well as the current page. I have a structure where the home ('/') page is the landing page and the (immediate) children need to be at the same level as Home. isActive() always shows Home as active whenever one of the other menu items is active.

55 minutes ago, bernhard said:

What I also do not understand: What do you mean by reflect the page tree hierarchy?

I mean that the menu hierarchy (other than Home) looks exactly like the page tree (but just for pages which are menu items). So if the page tree is:

Home
- Child 1
- Child 2
-- Grandchild 1
-- Grandchild 2
- Child 3 
etc.

the menu will be:

Home     Child1     Child2     Child3
                              - Grandchild 1
                              - Grandchild 2

55 minutes ago, bernhard said:

If I call $page->children() then they are already sorted in the correct order, no?

Not always. If no sort order is specified, they will be in id order (I think) - which is fine if you created them in the order you want. If you specify "sort=sort" then the top level will be correct, but lower levels will get mixed in - see 

 

55 minutes ago, bernhard said:

And what are the custom page class methods for?

So those give a sort order as it appears in the page tree hierarchy: like 1, 2, 3, 4, 4.33, 4.66, 5 where 4.33 and 4.66 are the grandchildren. That way you don't encounter a child in the list before you have had its parent (which messes up the menu).

EDIT: BTW, the $shown var is to prevent children being shown subsequently as higher-level items - not sure if that was fixing a problem in the original code or was just needed in my version.

Link to comment
Share on other sites

4 hours ago, MarkE said:

isActive() selects the parent page as active as well as the current page. I have a structure where the home ('/') page is the landing page and the (immediate) children need to be at the same level as Home. isActive() always shows Home as active whenever one of the other menu items is active.

Thx @MarkE I've modified the behaviour of isActive() so that it treats the homepage different than all other pages. It does now only mark the homepage active if the currently views page is the homepage (id=1). I have not hat that case until now but I think that makes sense 99,9% (maybe 100%) of the time since the homepage would otherwise always be "active" (as any page is a child of the root page), which is likely not what we want.

Thx for the input ? 

4 hours ago, MarkE said:

I mean that the menu hierarchy (other than Home) looks exactly like the page tree (but just for pages which are menu items). So if the page tree is:

Home
- Child 1
- Child 2
-- Grandchild 1
-- Grandchild 2
- Child 3 
etc.

the menu will be:

Home     Child1     Child2     Child3
                              - Grandchild 1
                              - Grandchild 2

Sorry I still don't get it I think. My menus always reflect the page tree. If I want the "home" menu item on the same level as child1, 2 and 3 I simply output it above:

<ul>
  <li><a href=/ n:class="$page->is('/') ? 'uk-active'">HOME</a></li>
  <li n:foreach="$home->children() as $item">
    <a href="{$item->url}" n:class="$rockfrontend->isActive($item) ? 'uk-active'">
      {$item->title}
    </a>
  </li>
</ul>

Is that what you are talking about?

4 hours ago, MarkE said:
5 hours ago, bernhard said:

If I call $page->children() then they are already sorted in the correct order, no?

Not always. If no sort order is specified, they will be in id order (I think) - which is fine if you created them in the order you want. If you specify "sort=sort" then the top level will be correct, but lower levels will get mixed in - see

I think you are making things more complicated than they need to be. $page->children() already return the children in the admin sort order. So if you call $page->children() recursively in your menu than the menu perfectly reflects the page tree.

Quote

How to force pages to sort by their admin order with $pages->find()

Unlike $page->children(), the $pages->find() method does not automatically sort by the order they appear in the site tree. This is because $pages->find() is not limited to finding pages specific to one parent, so it may be pulling pages from multiple places (according to your selector). If your parent page(s) are not already sorting by a specific field, you may still tell the find() to sort by the parent's order by literally specifying sort=sort in your selector. This is the same as saying "sort by whatever order I dragged the pages to in the admin." But note that if the results have multiple parents, the resulting order isn't likely to be that useful.

https://processwire.com/docs/selectors/

So the problem might be that you are using $pages->find() or maybe that you apply a selector to page->children() ?!

For me $page->children() did a perfect job without any extra magic ? 

  • Like 1
Link to comment
Share on other sites

14 hours ago, bernhard said:

I've modified the behaviour of isActive() so that it treats the homepage different than all other pages. It does now only mark the homepage active if the currently views page is the homepage (id=1).

The issue is a bit more complex than that, maybe because of the way my menus work. I tend to have (viewable) pages which have (viewable) children, so the top-level menu items can also be pages, in which case they also appear in the dropdown and can be selected either by clicking the top-level item or the one on the dropdown. If you just fix the home page highlighting then, in my case, the parent will be highighted in the dropdown as well as on the top level. See pics below:

What I want (& my code above gives):

2110417820_Menu1.jpg.f822eaef8597897c8708a6bcc0526439.jpg

What isActive() gives me (currently):

522832155_Menu2.jpg.531857f83bc3648a25d59e117e41e9a8.jpg

14 hours ago, bernhard said:

I think you are making things more complicated than they need to be. $page->children() already return the children in the admin sort order. So if you call $page->children() recursively in your menu than the menu perfectly reflects the page tree.

Aagh! Of course you are right. I changed it to find() so that the home page was also included. Your menu assumed that home was not a menu item (hence also the issue with isActive() ). So now I have changed it to $home->children($selector) and the order is correct and all my complications go away (but I do then need to prepend $first to $items). Never mind it was an interesting exercise and may come in useful one day.

  • Like 1
Link to comment
Share on other sites

  • 1 month later...
  • 6 months later...

@netcarver I've been using Latte in Laravel almost exclusively. Blade is great, but Latte is even better. There's a composer package for integrating Latte, but it was out of date when I last checked so I wrote my own which was quite straightforward. Interestingly, you can mix and match, calling Blade views from Latte and vice versa, which is a lifesaver when dealing with vendor files and third-party packages.

  • Like 3
Link to comment
Share on other sites

Can someone please give me a hint on how to use RockFrontend with Latte and combination with RepeaterMatrix?

I just can't find a way to show the content of my blocks (RepeaterMatrix types).

{* home.latte *}
<ul n:if="$page->blocks">
  {foreach $page->blocks as $block}
  <li>
  {include '../blocks/' . $block->type . '.latte'}
  </li>
  {/foreach}
</ul>

{* oneOfTheBlocks.latte *}
<div>
{* NOPE *}
{$block->headline}
</div>
<div>
{* NOPE *}
{$page->headline}
</div>

 

Link to comment
Share on other sites

It should work just like any other PW api... Does it render empty divs? Does it throw an exception?

I'm never using {include ...}, I'm always using {$rockfrontend->render(...)}, but both should work.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...