Listing Movies from their Markdown Files
In this lesson, we'll learn how we can list movies and their details directly from our movies directory using a YAML-based declaration called frontmatter.
- Author
- Tom Gobich
- Published
- Jan 27
- Duration
- 8m 51s
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
Listing Movies from their Markdown Files
-
[music]
-
So whenever we added our movies into our movies directory in our project,
-
we added three different movies. However, in our navigation,
-
we only have two movies accounted for. We have My Awesome Movie
-
and Another Awesome Movie. We don't have our full trilogy here.
-
It also doesn't make a whole lot of sense to list all of our different movies
-
within our navigation, because sooner or later we're going to end up
-
going outside of the realms of our page and having a scroll bar in here.
-
Ideally, instead, what we would do is list them all on our homepage
-
in a vertical style listing. So let's go ahead and focus on that in this lesson.
-
So let's start by adding our browser away, and we're within our routes.ts file.
-
So if we scroll up a little bit here, we have our homepage.
-
So instead of using the on shorthand to define this route, let's switch this to a get,
-
meaning that we can also get rid of this render method.
-
We'll leave the name as home. Then we want to add in an async.
-
We'll have our context for our HTTP context and our method for our route handler.
-
Similarly to how we're fetching an individual movie,
-
we want to reach inside of our resources movies folder,
-
and we want to list out all of the different movies that we have held within there.
-
So let's go ahead and get the underlying URL pointing to that directory.
-
So let's do const URL equals. We can reach for app, make URL again,
-
and provide it resources/movies.
-
Once we have that URL, we can go ahead and use the file system directory from node
-
to read out the different files that we have within our movies folder.
-
So let's do const slugs equals await FS dot,
-
and let's read dir to read a directory.
-
And then we want to pass it the URL.
-
I'm going to go ahead and hit save so that this formats my code.
-
And let's go ahead and add our render call back in.
-
So we'll return ctx view, render, and point it back to pages/home.
-
At this point, let's go ahead and stop and see exactly what we have for our slugs.
-
So we'll pass this into our page state slugs.
-
We'll jump into our page and underneath our h1,
-
let's go ahead and do our double curly braces and let's inspect slugs.
-
We'll give this a save, jump back into our browser, and here we go.
-
So you can see it actually matches the entire file name that we have
-
listed within that movies directory, including the file extension itself.
-
What we want these file names to represent is the slug.
-
So that would be this portion right here without the actual file extension.
-
So let's jump back into our code, jump back into our routes,
-
and after we've read from the directory, let's strip out the file extension.
-
So we'll switch our slugs here to be files and let's do const slugs equals files.
-
Let's map over those. We'll have a file name and we can just do file,
-
call the replace method, and we'll want to replace .md with an empty string.
-
Go ahead and hit save. Let's jump back into our browser now, refresh our page,
-
and there we go. We now have our fully represented slugs
-
without the file extension added in, meaning that whenever we generate
-
our movies.showRoute using the route method in our edge files,
-
we're safe to go ahead and pass these in as our slug parameter.
-
So let's hide away this page, go into our homepage,
-
and let's loop over our slugs and add links in.
-
So for right now, let's go ahead and do an unordered list.
-
And within EdgeJS, we can loop by doing @each, declare our variable.
-
So we'll have a singular slug from our plural slugs,
-
and then we'll want to end our each. For each item, we'll want a list item.
-
And let's also do an anchor tag with an href.
-
And then just as we are within our navigation here, we'll want to call
-
route.movies.show and then pass in the individual slug
-
to generate out our URL. So let's do route.movies.show,
-
and then we can just pass in the slug. Let's end our anchor.
-
And for now, let's just show the slug in a page. We'll see how we can switch this
-
to a full-blown title here in a little bit. So at this point, let's give this a save,
-
jump back into our browser, and there we go. So we get rid of the inspect,
-
and now we instead see each slug listed out on the page,
-
but these also serve as links. They're not highlighted in any way because we have that
-
CSS reset from Tailwind, but they are actually links that we can click.
-
We can go back to our home page, click another, go back to our home page,
-
click the third, and there we go. So these are all working A-OK.
-
Now, we can easily add titles and summaries to each of our movies directly
-
within the file and pull that out using a YAML-based declaration called frontmatter.
-
So let's go ahead and hide our browser once more, and let's dive in through each of our movies.
-
So we'll start with this one. At the top of the file, we want to start a YAML-based declaration
-
for frontmatter, and to do this, we just do three hyphens, and then we'll want to
-
end it by doing another three hyphens. Inside of these two three hyphens is where
-
we'll define our YAML declarations. So this is really just a key-value pair
-
where we can define information. So we'll have a title where we'll put our movie title.
-
So we can go ahead and just cut this out, actually, because we're going to have a separate
-
variable for this, so we don't need it twice within our page, and we'll paste it in here,
-
and then we can do a summary. I'm just going to grab the first line from our ellipsum text
-
and paste it in here to serve as our summary. Let's go ahead and give this a copy, and we can
-
paste this and reuse it in our other movie files. So we'll paste it at the top,
-
copy the title, get rid of it, paste the title in, copy it once more,
-
move down to our third, add it to the top, copy the title, get rid of it, and paste
-
it in once more. We can give this a save. Let's jump back into our routes file, and now
-
we need to actually get that information for each one of the files that we have. So instead of mapping
-
over them, let's instead for loop over them. So to loop over these, let's do for const
-
file of files. Now within our loop, we're going to want to take our file name,
-
let's actually call that file name because that's literally what it is, and append it onto
-
the URL that we're generating here. So let's do const movieURL
-
equals app makeURL, and we'll point it to the same path,
-
so resources/movies/, and we can switch these to backticks
-
so that we can inject the movie's file name directly in here, just like so.
-
Then we're going to want to read the actual file so that we can get the metadata from inside of it. So the
-
const file equals await file system or fs.
-
and read file, pass it in our movieURL, specify that
-
the encoding is utf-8, and then we're going to want to create a new markdown
-
file using this file here. So let's do const md equals
-
new markdown file, and pass in the file, and then we'll tell markdown file to process,
-
so we'll await md process, and if we did everything correctly, this should
-
read the front matter that we've defined inside the start of each of our markdown
-
files, so we'll have access to a front matter property that has our title and our summary
-
populated on it. But we'll want a place to put each one of our different movies, so outside of
-
our loop, let's do const movies. We'll define this as a type of record
-
where the key is a string and the value is any, and we'll say that this is an array.
-
And then we'll instantiate it to an empty array. Now back inside of our loop, we can push
-
into our movies array, so we'll do movies.push, add in an object,
-
we will do title, and then we can do md. as we said, we should have
-
front matter property on here that has our title populated on it, and then we can
-
do the same thing for our summary, so md front matter summary, and then so that
-
we can link into each individual movie within our page, we're also going to want the slug, which just like
-
we're doing within our map, all we want to do is take the file name and remove the
-
md file extension from it. So filename.replace.md
-
and set that to an empty string. Cool. So now we can actually remove our files map,
-
change slugs to movies, and we need to make that same change within our page too.
-
So let's jump into our page, slugs is now movies, instead of looping over
-
an array of strings, we're going to be looping over an array of objects, each object represents
-
a movie, and each movie contains a slug. So we want to actually assign a separate value
-
to slug, specifically for our movie slug, and then we can change the display
-
from displaying the slug to displaying the title, so movie.title. If we did everything
-
correctly, we should be able to save and then jump back into our browser, and
-
it looks like we do have an error. So incomplete explicit mapping pair, a
-
keynote is missing or followed by a non-tabulated empty line at one
-
column 21. And it looks like it's specifically having issues with our
-
trilogy title, likely because it already contains a colon. So let's
-
try to hide our browser back away, and let's dive into that movie. As we were writing it, I did
-
note that the highlighting was a little funky, but I thought maybe it would work. Let's see if we can
-
escape the colon, let's see if that will work. The highlighting says maybe not, but we'll
-
give it a save and give it a try. Jump back into our browser, give it a refresh, doesn't
-
look like it's happy with it. Okay, well so that we can keep moving on, we'll hide our browser back away
-
and let's just get rid of the colon for now. We'll replace it by a hyphen, how's that?
-
So let's give that a save, jump back into our browser, give this a refresh, and there we go. So now we have
-
a list of links to each of our different movie titles. If we click into them, we see
-
our movie details. We can add the title in here next. Let's jump back to our homepage,
-
click the next one, that one works too, and the third. So they all work.
-
Let's go ahead and add the title back in now. Remember we stripped that out of the actual file
-
and moved it up into the Frontmatter data. So all that we need to do is dive into
-
our routes. Inside of our show route, we're only sharing the movie with the route
-
itself. What we want to do is also share the MD, so we can add MD into
-
there. So now we have access to our Frontmatter data. Dive into our show, scroll up a little
-
bit, and just before our movie, let's add an H1 pointing to
-
MD Frontmatter title. Give that a save, and now if we jump back into
-
our page we should see our title which we do.
-
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! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!