Playing Next Lesson In
seconds

Let's Learn AdonisJS 7 #2.2

EdgeJS View & Tag Syntax

In This Lesson

Learn all about EdgeJS templating with interpolation, conditionals, loops, variables, and tags. We'll also talk about comments and escaping interpolation and HTML markup.

Created by
@tomgobich
Published

Interpolation

We've already learned a little about interpolation and tags with EdgeJS, but we're going to drive it home here. For starters, we can do more than merely access state with interpolation; we can perform JavaScript expressions as well.

For example, if we wanted to show the total number of points available from our challenges, we could evaluate that aggregation within an interpolation!

@layout()

  <div>
    <h1>Challenges</h1>
    <p>Welcome to the challenges page.</p>

    <div>
      Total Points Available: 
      {{ challenges.reduce((total, challenge) => total + challenge.points, 0) }}
    </div>
  </div>

@end
Copied!
  • resources
  • views
  • pages
  • challenges
  • index.edge

Note these interpolations can span multiple lines as well if you need to do something like a bracketed callback or if you're performing a longer ternary.

@layout()

  <div>
    <h1>Challenges</h1>
    <p>Welcome to the challenges page.</p>

    <div>
      Total Points Available: 
      {{ 
        challenges.reduce((total, challenge) => {
          return total + challenge.points
        }, 0) 
      }}
    </div>
  </div>

@end
Copied!
  • resources
  • views
  • pages
  • challenges
  • index.edge

Conditionals & Loops

EdgeJS supports conditionals and loops as well via tags, so if we wanted to loop over our challenges, we could use @each to do just that!

@layout()

  <div>
    <h1>Challenges</h1>
    <p>Welcome to the challenges page.</p>

    <div>
      Total Points Available: 
      {{ 
        challenges.reduce((total, challenge) => {
          return total + challenge.points
        }, 0) 
      }}
    </div>

    <ul>
      @each(challenge in challenges)
        <li>
          <h3>{{ challenge.text }}</h3>
          <p>Points: {{ challenge.points }}</p>
        </li>
      @endeach
    </ul>
  </div>

@end
Copied!
  • resources
  • views
  • pages
  • challenges
  • index.edge

Here @each starts the loop and @endeach ends the loop. In between, we have access to the individual challenge currently being looped over. Now, as mentioned previously, tags must be on a line of their own. So, something like the below is invalid!

<ul> @each(challenge in challenges) {{ challenge.text }} @endeach </ul>
Copied!

If we wanted to show a message in the event that we don't have any challenges, we can use a conditional!

@layout()

  <div>
    <h1>Challenges</h1>
    <p>Welcome to the challenges page.</p>

    <div>
      Total Points Available: 
      {{ 
        challenges.reduce((total, challenge) => {
          return total + challenge.points
        }, 0) 
      }}
    </div>

    @if (!challenges.length)
      <p>No challenges available.</p>
    @else
      <p>Below are our available challenges:</p>
    @endif

    <ul>
      @each(challenge in challenges)
        <li>
          <h3>{{ challenge.text }}</h3>
          <p>Points: {{ challenge.points }}</p>
        </li>
      @endeach
    </ul>
  </div>

@end
Copied!
  • resources
  • views
  • pages
  • challenges
  • index.edge

We can also utilize @elseif() if needed here as well, and we could use @unless instead of an @if as an alternative.

@unless (challenges.length)
  <p>No challenges available!</p>
@endunless

These native tags all come with supporting named end tags, for example @endif goes with @if. However, if you prefer, @end can be used as a consistent means to end any tag. I tend to prefer the named end tags as it signals what exactly it is we're ending, but to each their own.

Variables

Beyond state, we can also declare variables within our templates as well. We can use the @let tag to declare a new block-level variable and @assign to update a variable's value. This can be used in conjunction with conditionals to do something like the below!

++@let(availablePoints = challenges.reduce((total, challenge) => total + challenge.points, 0))

@if (completedChallenges?.length)
  @let(completedPoints = completedChallenges.reduce((total, challenge) => total + challenge.points, 0))
  @assign(availablePoints = availablePoints - completedPoints)
@endif

@layout()

  <div>
    <h1>Challenges</h1>
    <p>Welcome to the challenges page.</p>

    <div>
      Total Points Available: {{ availablePoints }}
    </div>

    @if (!challenges.length)
      <p>No challenges available.</p>
    @else
      <p>Below are our available challenges:</p>
    @endif

    <ul>
      @each(challenge in challenges)
        <li>
          <h3>{{ challenge.text }}</h3>
          <p>Points: {{ challenge.points }}</p>
        </li>
      @endeach
    </ul>
  </div>

@end
Copied!
  • resources
  • views
  • pages
  • challenges
  • index.edge

Rendering Raw HTML

Now, as a means of security, the double curly-braced interpolation will escape HTML. So, if you find yourself needing to actually render out HTML, you'll want to use three curly braces instead of two! Three won't escape HTML while two will. As we saw in our layout component, the slot is using three. That's because the slot will actually return HTML to be rendered. So, if we were to switch that to just two, we can see exactly how that impacts our page.

<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>
      AdonisJS - A fully featured web framework for Node.js
    </title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
    @stack('dumper')
  </head>
  <body>
    @include('partials/header')
    <main>
      @include('partials/flash_alerts')
      {{ await $slots.main() }}
    </main>
  </body>
</html>
Copied!
  • resources
  • views
  • components
  • layout.edge

With that, now we're just seeing the HTML that should be rendered as HTML instead being rendered as text content. So, let's undo that to get our HTML back!

Escaping Interpolation

If we need to actually render double or triple curly braces on our page, we can use an @ to escape the interpolation to instead render the actual curly braces.

<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>
      AdonisJS - A fully featured web framework for Node.js
    </title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
    @stack('dumper')
  </head>
  <body>
    @include('partials/header')
    <main>
      @include('partials/flash_alerts')
      @{{{ await $slots.main() }}}
    </main>
  </body>
</html>
Copied!
  • resources
  • views
  • components
  • layout.edge

With that, we literally see "{{{ await $slots.main() }}}" printed out on our page, and EdgeJS bypasses the expression altogether. Again, let's undo that to get our slot back to rendering.

Comments

Finally, EdgeJS supports comments as well; the syntax is double curly braces with double hyphens.

<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>
      AdonisJS - A fully featured web framework for Node.js
    </title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
    @stack('dumper')
  </head>
  <body>
    @include('partials/header')
    <main>
      @include('partials/flash_alerts')

      {{-- renders default slot content --}}
      {{{ await $slots.main() }}}
    </main>
  </body>
</html>
Copied!
  • resources
  • views
  • components
  • layout.edge

Join the Discussion 0 comments

Create a free account to join in on the discussion
robot comment bubble

Be the first to comment!