EdgeJS Templating Basics
In this lesson, we'll learn the basics of AdonisJS's homegrown template engine EdgeJS. We'll cover interpolation, conditional statements, looping, variables, state, and more!
- Author
- Tom Gobich
- Published
- Feb 10
- Duration
- 8m 49s
Developer, dog lover, and burrito eater. Currently teaching AdonisJS, a fully featured NodeJS framework, and running Adocasts where I post new lessons weekly. Professionally, I work with JavaScript, .Net C#, and SQL Server.
Adocasts
Burlington, KY
Transcript
EdgeJS Templating Basics
-
(upbeat music)
-
So whenever we select the web starter kit with AdonisJS 6,
-
it's going to come with the EdgeJS template engine
-
alongside it.
-
We've already worked with this a little bit.
-
It's actually what's rendering out our page
-
within our application so far.
-
Additionally, if we dive back into our text editor
-
and we head into our homepage,
-
it's also what's allowing us to loop over our movies,
-
declare an href for our route
-
and build out the actual route from our route method,
-
as well as plop the actual movie title
-
directly into the HTML using interpolation.
-
We've also already learned on our movie show page,
-
by default, whenever we use two curly braces,
-
it will encode anything that we plop into the HTML.
-
But if we include three curly braces,
-
we're telling it that we trust the underlying value
-
that we're injecting into our HTML therein,
-
and it will render it out raw.
-
We've seen this already, but for example,
-
if we remove our third curly brace
-
and switch our movie abstract back to just two,
-
and we dive into one of our movie show pages,
-
we see the underlying HTML because it's being encoded.
-
Whereas if we add that third curly brace back in,
-
instead the HTML is directly applied into our HTML document,
-
and this is now a valid paragraph.
-
The EdgeJS template engine is doing all that work
-
underneath the hood for us,
-
and it's integrating deeply with AdonisJS
-
to make that a seamless process.
-
If we head into edgejs.dev,
-
we'll see the documentations for EdgeJS itself.
-
We see an example right off the bat
-
for the interpolation that we've been using so far.
-
And in addition to that,
-
we can actually use valid JavaScript expressions
-
within these interpolated curly braces here as well.
-
Not only that, but we can define our own stateful variables
-
within EdgeJS, apply conditionals,
-
we've already seen loops,
-
and then it has support for components as well.
-
Additionally, whenever we use that inspect method
-
to actually view the underlying properties of an object
-
and all of its values, that was from EdgeJS as well.
-
So let's slide back to our text editor here,
-
and we can, underneath our movie abstract,
-
inspect, call that as a method,
-
and pass in our movie, give that a save,
-
and we'll see once more,
-
we see all of the different properties
-
that are on our movie.
-
Now, we can actually do that exact same thing
-
for the entirety of our state
-
that we have within EdgeJS as well.
-
This is all held within a property called state.
-
So let's go ahead and give this a save,
-
and let's inspect our state.
-
So out of the box, we're gonna see
-
that this has quite a lot added to it.
-
A lot of these that we see so far are all helper methods.
-
We have the inspect method right here.
-
We can truncate string, get an excerpt for a string,
-
HTML helpers, JavaScript helpers,
-
switch text to camel case, snake case, dash case,
-
all the various cases, pretty print milliseconds.
-
And then as we scroll down a little bit further,
-
we start getting into app information, configuration, byte.
-
We have our request information.
-
So within our request, we can see the parse URL
-
and see that we're at the path name movie
-
slash another awesome movie.
-
We have contextual information from our HTTP context.
-
And we just keep scrolling and scrolling and scrolling.
-
This is just gonna go on and on and on.
-
Our state holds quite a bit,
-
particularly within the request
-
and response objects of our state.
-
Lastly, down here at the bottom,
-
we actually see the movie that's on our state,
-
which matches exactly what we saw
-
whenever we inspected just our movie.
-
So if you ever do need to dive into your state
-
and see what all is available to you,
-
now you know how to do that.
-
Just give it an inspect.
-
So let's go ahead and get rid of this
-
because it's taking up quite a bit of space
-
and give this a save.
-
There we go, that's better.
-
Okay, so within our movie model,
-
we made our movies abstract optional.
-
So if we scroll up to where we have the properties defined,
-
the abstract is actually optional,
-
meaning it could be a string or it could be null.
-
So if we dive into one of our movies,
-
let's go into our resources, movies,
-
and we're at another awesome movie.
-
So we'll do this one for right now.
-
We can get rid of this abstract altogether.
-
And now all that we have is our YAML front matter data.
-
We don't have any additional information within this file.
-
We'll go ahead and give it a save.
-
And so that we actually see this
-
without having to deal with clearing out our cache,
-
let's dive into our movie model once more.
-
And let's actually just disable the cache check for right now.
-
So we'll just give this a comment out, give this a save,
-
jump back into our browser and give it a refresh.
-
So presently this is working okay
-
because ultimately we're reading the value from a file
-
and that file is gonna return back some kind of a value,
-
regardless of whether or not we've defined the value there.
-
But if you see here, we have nothing additional
-
within our HTML markup besides our H1.
-
This could become problematic.
-
So let's dive back into our show page.
-
If we start wrapping this information
-
within say a div class, margin Y eight,
-
maybe we give it a background too.
-
So background slate 100, rounded Excel,
-
and maybe P8 as well.
-
So if we start applying styling based around this content,
-
let's give this a save, jump back into our browser.
-
You'll see that we have nothing inside of it,
-
but if we dive back to one of our other movies,
-
there's our abstract.
-
Okay, so let's dive back into another movie.
-
So let's use a conditional to actually show this
-
only when we have an abstract.
-
So let's dive back into our markup
-
and we can do @if movie.abstract,
-
and then we can indent our div here,
-
and then we can end our if by doing @endif.
-
I'll get this line break there as well.
-
We can give this a save, jump back into our browser.
-
And now that background color is gone
-
because we don't have an abstract on this particular movie.
-
But if we jump back to our first movie,
-
it's still there A-okay.
-
And if you need to, you can do an if else as well.
-
So let's dive back into this movie.
-
Let's go ahead and get rid of our inspector,
-
dive back into our code base, and let's do an @else.
-
So if we have a movie abstract,
-
our div with the background will be applied.
-
Otherwise, let's just say no abstract.
-
Give this a save, and there we see no abstract.
-
Cool, so now we know how to do if else,
-
and we had already previously covered loops
-
with our homepage whenever we looped over
-
all of the movies that we have
-
to show them within our unordered list.
-
Now for both of our pages here so far,
-
we are including the navigation
-
and we're redefining the navigation for each of our pages.
-
So we have our full navigation here,
-
and we have our full navigation
-
on the movie show page as well.
-
Well, we can actually extract this navigation out
-
into what's called a partial,
-
which is essentially just a partial markup file,
-
allowing us to define the markup in one spot
-
and reference it elsewhere throughout our EdgeJS files.
-
So within our views directory,
-
let's go ahead and right-click it, create a new file.
-
We'll call this partials.
-
And for this, we'll call our file nav.edge.
-
Give that a save.
-
And now our views directory has both the pages
-
and partials directory within it.
-
Within our pages directory, let's dive back to our homepage.
-
Let's give our navigation here a copy.
-
Within our nav partial, we can now paste this in,
-
fix the indenting there, give that a save.
-
And now we can jump back into our homepage.
-
And instead of manually defining the navigation,
-
now all that we need to do is add, include,
-
and then specify the path to the partial
-
from our views directory, which will be partials/nav.
-
So partials/nav.
-
Now we can give this a save.
-
Let's go ahead and jump back to our browser,
-
head to our homepage,
-
and we still have our navigation here.
-
So let's go ahead and do the same thing
-
on our movie show page.
-
So let's give our include a copy,
-
jump into our movie show page,
-
replace the navigation here with our include,
-
give it a save, jump back into the browser.
-
And now we should be able to see the exact same thing
-
on each and every one of our pages, which we do.
-
So now within our homepage, we can do one more thing.
-
We can add in our movie summaries
-
underneath the actual movie title.
-
So let's go and jump back into our text editor,
-
jump back into our homepage,
-
scroll down to where we're looping over each of our files.
-
And underneath our anchor,
-
let's go and add in a paragraph and inject movie.summary
-
using interpolation here with double curly braces.
-
We can add in a class, text, slate,
-
maybe six or 700 and give it a save.
-
Let's jump back into our browser.
-
And okay, so we have our movie summary
-
showing up underneath there.
-
Let's give this a little bit of spacing too.
-
So let's bring our text editor back up on our UL.
-
Maybe we'll do class flex, flex column, gap of four,
-
and give that a save, jump back into here.
-
Okay, it's looking a little bit better.
-
Let's push this from 700 to 600
-
and let's make the movie title bold.
-
So we'll do class font bold, give that a save.
-
All right, so that's looking better.
-
Now let's say that we wanted to truncate
-
this text down a little bit.
-
Maybe it's a little bit too long for our liking.
-
We can do that using one of the helpers from EdgeJS.
-
So we dive back into the documentation and scroll on down.
-
There's a digging deeper section with helpers within it.
-
For this in particular, we see two different helpers.
-
We see truncate and we see excerpt,
-
which is just below truncate.
-
The primary difference here is that excerpt
-
will also strip out HTML markup.
-
Now we've already removed the HTML markup from our summaries.
-
So we're a-okay there.
-
But if we wanted to, we could use this excerpt
-
to extract a summary from our abstract
-
and strip out the paragraph markup that we have within it
-
or any additional HTML that we have within our abstract.
-
But since we already have a summary,
-
we're a-okay to go ahead and just use truncate here.
-
The first argument will be the value
-
that we wanna pass in and shorten.
-
The second argument will be the number of characters
-
that we wanna include.
-
And we could conditionally provide a third argument,
-
which is a configuration.
-
We can specify it to only truncate at complete words,
-
so it won't cut a word in half.
-
And we could also add in a suffix as well.
-
Okay, so let's go ahead and dive back into our application.
-
Let's jump back into our text editor.
-
Within our summary, we'll call truncate here,
-
provide our movie summary as the first argument.
-
I'm not quite sure how many characters we have,
-
so we'll just go ahead and cut it off at 25.
-
I think we have a little bit more than that.
-
So we should be good.
-
And we'll say complete words and set that to true.
-
And then let's also end our function call there.
-
Okay, so we should be good to give this a save.
-
Jump back into our browser.
-
Oh yeah, 25's kinda short, but yeah.
-
We can see it actually working here.
-
So now we have just lorem ipsum doler sit emet,
-
and it cuts off the rest of the text there.
-
So maybe we switch that to 50 instead of 25.
-
So let's switch this to 50, give that a save.
-
There we go, that's a little bit better.
-
Introduction
-
Fundamentals
-
2.0Routes and How To Create Them5m 23s
-
2.1Rendering a View for a Route6m 29s
-
2.2Linking Between Routes7m 51s
-
2.3Loading A Movie Using Route Parameters9m 17s
-
2.4Validating Route Parameters6m 6s
-
2.5Vite and Our Assets6m 38s
-
2.6Setting Up Tailwind CSS9m 5s
-
2.7Reading and Supporting Markdown Content4m 32s
-
2.8Listing Movies from their Markdown Files8m 51s
-
2.9Extracting Reusable Code with Services7m 4s
-
2.10Cleaning Up Routes with Controllers4m 52s
-
2.11Defining A Structure for our Movie using Models9m 38s
-
2.12Singleton Services and the Idea of Caching6m 11s
-
2.13Environment Variables and their Validation4m 16s
-
2.14Improved Caching with Redis10m 44s
-
2.15Deleting Items and Flushing our Redis Cache6m 46s
-
2.16Quick Start Apps with Custom Starter Kits6m 28s
-
2.17Easy Imports with NodeJS Subpath Imports8m 40s
-
-
Building Views with EdgeJS
-
3.0EdgeJS Templating Basics8m 49s
-
3.1HTML Attribute and Class Utilities6m 9s
-
3.2Making A Reusable Movie Card Component10m 24s
-
3.3Component Tags, State, and Props4m 53s
-
3.4Use Slots To Make A Button Component6m 56s
-
3.5Extracting A Layout Component5m 13s
-
3.6State vs Share Data Flow2m 59s
-
3.7Share vs Global Data Flow6m 7s
-
3.8Form Basics and CSRF Protection6m 13s
-
3.9HTTP Method Spoofing HTML Forms3m 3s
-
3.10Easy SVG Icons with Edge Iconify7m 57s
-
-
Database and Lucid ORM Basics
-
4.0Configuring Lucid and our Database Connection4m 3s
-
4.1Understanding our Database Schema9m 35s
-
4.2Introducing and Defining Database Migrations18m 35s
-
4.3The Flow of Migrations8m 28s
-
4.4Introducing Lucid Models5m 43s
-
4.5Defining Our Models6m 49s
-
4.6The Basics of CRUD11m 56s
-
4.7Defining Required Data with Seeders11m 11s
-
4.8Stubbing Fake Data with Model Factories13m 48s
-
4.9Querying Our Movies with the Query Builder15m 30s
-
4.10Unmapped and Computed Model Properties3m 24s
-
4.11Altering Tables with Migrations7m 6s
-
4.12Adding A Profile Model, Migration, Factory, and Controller2m 57s
-
4.13SQL Parameters and Injection Protection9m 19s
-
4.14Reusable Query Statements with Model Query Scopes8m 11s
-
4.15Tapping into Model Factory States9m 15s
-
4.16Querying Recently Released and Coming Soon Movies4m 59s
-
4.17Generating A Unique Movie Slug With Model Hooks7m 59s
-
-
Lucid ORM Relationships
-
5.0Defining One to One Relationships Within Lucid Models5m 49s
-
5.1Model Factory Relationships2m 54s
-
5.2Querying Relationships and Eager Vs Lazy Loading5m 17s
-
5.3Cascading and Deleting Model Relationships5m 16s
-
5.4Defining One to Many Relationships with Lucid Models6m 56s
-
5.5Seeding Movies with One to Many Model Factory Relationships5m 24s
-
5.6Listing A Director's Movies with Relationship Existence Queries8m 41s
-
5.7Listing and Counting a Writer's Movies8m 41s
-
5.8Using Eager and Lazy Loading to Load A Movie's Writer and Director5m 18s
-
5.9Defining Many-To-Many Relationships and Pivot Columns9m 48s
-
5.10Many-To-Many Model Factory Relationships4m 50s
-
5.11A Deep Dive Into Relationship CRUD with Models18m 5s
-
5.12How To Create Factory Relationships from a Pool of Data13m 55s
-
5.13How To Query, Sort, and Filter by Pivot Table Data9m 47s
-
-
Working With Forms
-
6.0Accepting Form Data12m 15s
-
6.1Validating Form Data with VineJS9m 29s
-
6.2Displaying Validation Errors and Validating from our Request7m 16s
-
6.3Reusing Old Form Values After A Validation Error2m 3s
-
6.4Creating An EdgeJS Form Input Component5m 28s
-
6.5Creating A Login Form and Validator5m 1s
-
6.6How To Create A Custom VineJS Validation Rule9m 7s
-
-
Authentication & Middleware
-
7.0The Flow of Middleware7m 49s
-
7.1Authenticating A Newly Registered User4m 14s
-
7.2Checking For and Populating an Authenticated User2m 10s
-
7.3Logging Out An Authenticated User2m 24s
-
7.4Logging In An Existing User6m 54s
-
7.5Remembering A User's Authenticated Session6m 55s
-
7.6Protecting Routes with Auth, Guest, and Admin Middleware5m 36s
-
-
Filtering and Paginating Queries
-
8.0Creating A Movie List Page3m 43s
-
8.1Filtering A Query By Pattern Likeness7m 9s
-
Filtering Our List by Movie Status5m 47s
-
How To Apply A Dynamic Sort Filter To Your Query7m 12s
-
Joining SQL Tables To Order By A Related Column4m 49s
-
Validating Query String Filter Values7m 23s
-
How To Paginate Filtered Query Results9m 15s
-
Pagination First, Last, Next, and Previous Buttons4m 2s
-
-
User Watchlist
Join The Discussion! (11 Comments)
Please sign in or sign up for free to join in on the dicussion.
frp
Hi, I am getting the Cannot lookup rote Edge error when I try to use an edge template with the route() helper in it.
That route shows up in list:routes:
GET /join (moms.join) .................................................. #controllers/join_controller.join
What am I doing wrong? Please let me know what additional information is needed.
Thank you.
Please sign in or sign up for free to reply
frp
I should probably add that the 'moms' at the start of that route name is for a domain:
Please sign in or sign up for free to reply
tomgobich
I haven't yet worked with subdomains in AdonisJS 6, but I believe they work the same as in v5. When building the route's url using the
route()
method, the third argument accepts the domain.The subdomain behavior is siloed, so you can have the same route defined outside the subdomain as well, and they can also be dynamic. Due to these reasons, that's why the
domain
will need specified when you're specifically looking to build a route for that domain.Hope this helps!
Please sign in or sign up for free to reply
frp
Wow thanks. I did not get that from the documentation anywhere. Hey, question: Do you know of any full-featured V6 example site on Github? It would be soooo helpful to see a full example of all this stuff, service providers, the container, everything.
Please sign in or sign up for free to reply
tomgobich
Anytime!! This site is public on GitHub and uses AdonisJS 6
https://github.com/adocasts/adocasts
Beyond that, I'm not aware of any that are currently public.
Please sign in or sign up for free to reply
frp
So this still is not working. I tried it just like that, console.log confirmed it, and it still says it "cannot look up" the route.
How can I recreate the route outside of an Edge template? I don't see a helper for that. Laravel had a route helper you could use anywhere in the app to craft a url string. If I had that I could just craft the string in the service and feed it into Edge. Failing that, I will just have to manually create the URL strings. I can, but for all the reasons you know, would rather not.
Thanks for all your help.
Please sign in or sign up for free to reply
tomgobich
I just pushed up a working example of subdomain usage you can find at the link below. If you're linking into your subdomain, you'll want to prefix the generated url with your subdomain and include the port if you're local.
https://github.com/adocasts/lets-learn-adonisjs-6/blob/subdomain_example
The specific files of interest here are the route definition and the url generation inside of EdgeJS. Within the route definition I also added examples for both the URL builder and the
makeUrl
method AdonisJS provides. Theroute
method inside EdgeJS is the same as therouter.makeUrl
method, so the arguments should match 1:1. Apologies, my formatter went a little extreme with themakeUrl
usage lol.Hope this helps!! :)
Please sign in or sign up for free to reply
frp
Thanks, I will check it out. I thought I had found the answer (I found "URL Builder" in the docs) but it still won't look up the routes inside Edge. And one clarification: I'm not doing subdomains, I'm doing different domains. It works fine on the routing end - everything routes correctly - but it's just inside Edge that it won't work. I will look at what you did and can probably kitbash it to my usage. I really appreciate all your time. You're a mensch.
Just FYI, this is how I generate the routes in my routes.ts file:
Please sign in or sign up for free to reply
tomgobich
Ah - gotcha, so you're using one server to serve multiple sites? If the routes are shared by all site, then you could most likely omit the
.domain()
usage as that essentially just namespaces the routes.Anytime! Hope you were able to get things working! :)
Please sign in or sign up for free to reply
frp
The URL builder in Adonis works fine, so I generate the route strings in my publish service and put the strings in the Edge template. Works great. Thanks!
Please sign in or sign up for free to reply
tomgobich
Oh good, happy to hear you got everything working! :)
Please sign in or sign up for free to reply