Toggling A Movie in an Authenticated User's Watchlist
In this lesson, we'll learn how to add the ability to toggle whether a user has a movie within their watchlist. We'll also query and display whether a movie is in the authenticated user's watchlist as well.
- Author
- Tom Gobich
- Published
- May 10
- Duration
- 9m 56s
![Tom Gobich](/img/1/profile/avatar_1703713552864.jpg)
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
Toggling A Movie in an Authenticated User's Watchlist
-
(upbeat music)
-
So now that we have our watch list defined
-
inside of our database,
-
let's go ahead and make use of it by adding a button
-
within our movie card that allows a user
-
to add a movie to their watch list.
-
So let's go ahead and hide our browser back away.
-
We can hide our database directory there.
-
And let's first go ahead and just jump into our controllers
-
and we should now have a watch list controller right here.
-
Let's grab our HTTP context import
-
and actually come to think of it,
-
we should have created this controller here as a resource.
-
So let's go ahead and just delete it out temporarily,
-
move that to our trash, jump back into our terminal,
-
stop our server, clear that out,
-
node, ace, make, controller, watch list.
-
And we could do hyphen R or hyphen hyphen resource
-
to add in resourceful methods,
-
which would be index, create, update, delete,
-
so on and so forth.
-
But really for this, all that we want is an index
-
for when it comes time to list out the watch list page,
-
a toggle method to allow the user to toggle
-
between having the movie in their watch list
-
and not having it in their watch list.
-
And then we're also gonna want another toggle
-
specifically for just whether or not the movie was watched.
-
Okay, let's go ahead and boot our server back up,
-
npm run dev, hide that away, jump into our new controller,
-
and there we go.
-
So right now our main target is to allow a user to toggle
-
whether or not a movie is inside of their watch list.
-
So what we're gonna want is our response
-
so that we can redirect user back
-
after they perform some action,
-
our params so that we can get the IDs applicable
-
as well as auth so that we can get the user applicable too.
-
So from our params, we're gonna want to be able to get
-
the movie ID and then we're also going to want our user ID
-
from our auth user.id there.
-
And right after our user,
-
we're gonna put an exclamation point
-
because we're gonna use the middleware to ensure
-
that in order for us to reach this method,
-
the user is actually authenticated.
-
And that will just essentially remove
-
the undefined portion from the type.
-
Then we'll wanna check and see whether or not
-
a watch list for that user in this movie already exists.
-
So we do watch list equals await auth.user,
-
we'll add another exclamation point to assert
-
that we do have a user and then do related watch list.
-
And we'll query where we have the movie ID
-
and we only care about the first.
-
So we'll just grab the first there.
-
So there's plenty of ways that you can actually query that.
-
That's one way.
-
You could of course also just do it directly
-
from the watch list model.
-
So for example, we could do await watch list,
-
import that from our model, query where,
-
and then check for both the movie ID
-
as well as the user ID there and grab the first.
-
Whatever your preference is.
-
This one here is probably actually a little bit cleaner.
-
So we'll go ahead and cut that out
-
and paste it in and use this one.
-
Okay, so let's scroll down a little bit.
-
If we have a watch list, so if watch list actually exists,
-
then what we wanna do is just delete it.
-
So we'll await watch list delete.
-
Otherwise, if it doesn't exist,
-
then we just wanna create it.
-
So we can await watch list dot create
-
and pass in our movie ID as well as our user ID there.
-
Once we've done that, we can go ahead and return response,
-
redirect the user right back to where they were.
-
Okay, let's give that a save.
-
Now let's jump into our routes
-
and let's go ahead and put this after our movies.
-
So do router dot, and this will be a post slash,
-
watch lists slash, and we only need the movie ID
-
that we wanna toggle.
-
And then we'll do another slash
-
and specify that we specifically
-
just want this route to be for toggling.
-
Import our watch lists controller,
-
scroll down to there, hit tab to auto import that
-
and pass this our toggle handler as watch lists toggle.
-
All right, give that a save.
-
Then let's go ahead and scroll down to our components,
-
go into our movie card and down to our button.
-
Let's just go ahead and give this button here a copy
-
and we'll put this action above it,
-
but we only wanna show this button if we have an off user.
-
So we'll go ahead and end our if there
-
and paste our button in.
-
For right now, we don't have a way to actually tell
-
whether or not the user has this movie in their watch list.
-
So we'll just do a strict add to watch list button
-
and we'll make that conditional here in a little bit.
-
For our href, we no longer want that to be a route
-
and we probably don't want the class margin top on that
-
either, we'll remove it from our button below
-
and wrap this whole section here in a div
-
with that applied here momentarily.
-
But we do want this button to have a type of submit
-
and we also want to wrap this button in a form method,
-
post action will be route watch lists dot toggle.
-
And we need to specify the movie ID as the param,
-
which is our movie dot ID.
-
And the form add in our CSRF field,
-
give our button an indent and end the form.
-
All right, let's go ahead and pause here,
-
give that a save, hide our text editor back away,
-
jump into our browser and whoops, I did watch lists.
-
Jump back into my text editor here
-
and let me add the L right there.
-
Okay, back into the browser, give that a refresh.
-
There we go.
-
All right, so yeah, we do need to fix the margin there,
-
but if we right click somewhere, inspect,
-
let's jump into our network tab, go into XHR,
-
click the gear, persist our log
-
and let's go ahead and click on add to watch list.
-
Oop, right, that's not an XHR, that's a form.
-
So let's go back to all and we can see
-
that it did a post request out to our slash toggle.
-
If we hover over that toggle,
-
we can see the full URL is watch list slash 420 slash toggle
-
and then that redirected us with a 302
-
right back to where we were, which is our homepage.
-
So that does seem to be working.
-
Let's go ahead and hide this back away
-
and give ourselves a way to determine
-
whether or not this movie is in the user's watch list.
-
So let's jump back into our text editor
-
and we're gonna wanna find all of the methods
-
where we're using movie card.
-
So I'm gonna hold command shift F to open up find.
-
It'll be a little search icon right around over here
-
if you have that toolbar enabled
-
and we'll do a search for at exclamation point movie.card
-
and it looks like we have this on,
-
expand this out here momentarily,
-
our resources views homepage,
-
our director's show page,
-
our movies index page and our writer's show page.
-
So first let's go ahead and jump into our home controller
-
and we'll take care of that one first.
-
And this is just gonna be a conditional preload
-
based off of whether or not we have an authenticated user.
-
So if auth user, if we have an authenticated user,
-
then we'll want to apply to our query,
-
query.preload our watch list.
-
I'm gonna go ahead and break this down
-
so that it's a little easier to follow
-
because inside of our preload watch list,
-
we're also going to have another query
-
where we want to limit the watch lists that are preloaded
-
to just those where the user ID
-
is our auth user exclamation point.id.
-
So if we have a user at all,
-
then we want to add to our entire movie query
-
a preload for our watch list
-
and then specifically to our watch list preload,
-
we want to limit those just where the user ID
-
is the currently authenticated user.
-
This should only ever be one
-
and we can actually enforce that
-
at the database level here momentarily too,
-
but we'll go ahead and leave it as a list
-
because that's what the relationship here will be expecting.
-
So we'll give that a save, it'll do its formatting.
-
I think it's complaining there
-
because we already used query.
-
Yep, so I'll just call that maybe watch list
-
since it's the watch list specific query.
-
There we go.
-
And now we just want to give this section here a copy.
-
We also need to apply it to our recently released here too.
-
So we can paste it there,
-
go into our director's controller down in the show method.
-
We're gonna want to break this query down a little bit
-
and paste our if inside of there.
-
It's like we also need to grab auth out of the HTTP context,
-
give that a save.
-
There we go.
-
This is our movies controller.
-
All right, this one should just be the index method
-
and it looks like we're using our get filtered there.
-
So let's go ahead and dive into there
-
by command clicking into it.
-
And at the end of our if chain here,
-
let's just go ahead and paste one more in.
-
And now we need to make our auth user accessible
-
within here.
-
We can either pass that in as a parameter,
-
which would be the most straightforward way,
-
or we can inject our HTTP context into the service.
-
Since we have this as a static method,
-
we'll go ahead and add it as a parameter for right now.
-
So at the end of our parameter list,
-
we'll add in user of type user and import that from a model
-
or have that be undefined and default it to undefined.
-
Give that a save so that it can do its formatting.
-
And then we just need to switch this to if user
-
and where user.
-
Okay, and then lastly, we have our writers controller
-
and it should be the show method right there.
-
So we'll give it one more paste
-
and grab auth out of the HTTP context there as well.
-
Nope, we did this one here a little bit different.
-
If won't be here,
-
but rather it will be within the movies written preload.
-
So we want to do query there, query,
-
break that down and paste it in there like so.
-
Give it a safe set of formats
-
and we'll go ahead and switch this one to written
-
since it is specifically for our written movies.
-
All right, so now we should have our watch list preloaded
-
everywhere that we're using our movie card.
-
So now within our movie card,
-
we should be able to tell whether or not the movie
-
is in their watch list by checking to see whether or not
-
watch list has a length.
-
So we can cut that out, do double curly braces
-
and check whether movie.watchlist has a length.
-
If it does, then we'll say maybe in your watch list,
-
otherwise we'll paste in our add to watch list text.
-
Give that a save.
-
And now if we jump into our browser
-
with our coming soon section,
-
we can now see in your watch list here for our button.
-
If we give that another click,
-
it's now back to add to watch list.
-
Give it another click in the watch list.
-
And so now we can scroll through here
-
and give some random clicks to add some to
-
and from our watch list.
-
Let's go ahead and jump into our directors page,
-
go into one specific director.
-
Looks like everything's rendering okay here.
-
Let's add one to our watch list.
-
All right, seems to have worked there.
-
Let's check out our writers too.
-
Go into someone here to add that to our watch list.
-
Cool.
-
And let's dive into our movies.
-
And okay, it looks like we do have something going on here.
-
So since it's reading length
-
and saying that it's reading from undefined,
-
that means watch list is undefined here.
-
So something's up with our preload for that query.
-
Now you know what it is?
-
We forgot to add user into the call.
-
So if we go back into our movies controller,
-
yep, right here, get filtered.
-
We need to add our auth user into that.
-
So go ahead and add auth there.
-
Since we do have an authenticated user,
-
the if was coming through within our card,
-
but we weren't actually populating the user here.
-
So watch list was never actually getting preloaded.
-
To safeguard that,
-
let's go ahead and jump back into our card
-
and do if auth user and movie.watchlist.
-
Since watch list will always be an array,
-
if it's populated,
-
we're okay to leave that as is within that if check.
-
All right, jump back into our browser,
-
try giving this a refresh and cool.
-
There we go.
-
So now we have our list.
-
Let's see if any are in our watch list already here.
-
Nope, but let's add one to it.
-
See whether or not that worked.
-
And it sure did.
-
Awesome.
-
So everything does seem to be working.
-
Real quick, before we run this out,
-
let's fix our card spacing.
-
So let's just wrap both of our buttons here in a div.
-
So we'll do div class margin top of four,
-
lex, lex all gap of maybe two.
-
Add some spacing in between the two buttons
-
if there are two buttons there.
-
And then right here,
-
we just remove that class altogether.
-
Cool.
-
Give that a save, jump back into our browser
-
and let's see how that looks.
-
Yeah, that's better.
-
Introduction
-
Fundamentals
-
2.0Routes and How To Create Them5m 24s
-
2.1Rendering a View for a Route6m 30s
-
2.2Linking Between Routes7m 52s
-
2.3Loading A Movie Using Route Parameters9m 18s
-
2.4Validating Route Parameters6m 7s
-
2.5Vite and Our Assets6m 39s
-
2.6Setting Up Tailwind CSS7m 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 8s
-
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
-
8.2Filtering Our List by Movie Status5m 47s
-
8.3How To Apply A Dynamic Sort Filter To Your Query7m 12s
-
8.4Joining SQL Tables To Order By A Related Column4m 49s
-
8.5Validating Query String Filter Values7m 24s
-
8.6How To Paginate Filtered Query Results9m 15s
-
8.7Pagination First, Last, Next, and Previous Buttons4m 3s
-
-
User Watchlist
-
9.0An Alternative Approach to Many-To-Many Relationships4m 56s
-
9.1Toggling A Movie in an Authenticated User's Watchlist9m 56s
-
9.2Listing and Filtering User Watchlist Items7m 31s
-
9.3Allowing Users To Toggle A Movie As Watched4m 44s
-
9.4Filtering By User's Watched Status6m 7s
-
9.5Defining A Composite Unique Constraint4m 47s
-
9.6Persist Filters Easily with Lucid's Query String Method3m 58s
-
-
User Profiles
-
10.0How to Create and Fix Missing User Profiles in Your Application7m 37s
-
10.1Using Dependency Injection to Update A User's Profile9m 46s
-
10.2Saving All Or Nothing with Database Transactions5m 15s
-
10.3Uploading and Displaying User Avatars15m 29s
-
10.4Displaying A User's Profile6m 1s
-
10.5Filtering, Preloading, and Sorting By Relationship7m 6s
-
-
Admin Panel
-
11.0Creating An Admin Layout7m 14s
-
11.1Counting Stats for our Admin Dashboard5m 43s
-
11.2Paginated Admin Movie Table13m 2s
-
11.3Allowing Admins to Create Movies16m 39s
-
11.4Allowing Admins to Update Movies and Clear Values13m 27s
-
11.5How To Use One Form to Create or Edit Movies5m 32s
-
Uploading Movie Cover Images in our Create or Edit Form10m 29s
-
Using A Wildcard Route Param to Download Storage Images7m 57s
-
Posting Objects, Arrays, and an Array of Objects in HTML Forms26m 26s
-
Managed Transactions and Syncing Movie Cast Members15m 55s
-
Allowing Admins to Delete Movies and their Relationships7m 42s
-
Thank You for Watching!0m 31s
-
Join The Discussion! (0 Comments)
Please sign in or sign up for free to join in on the dicussion.
Be the first to Comment!