🕰️ Chapters
- Defining Our Profile Edit Page
- Adding Textarea Support To Our Form Input Component
- Defining Our Profile Edit Form Fields
- Profiles Update PUT Route Definition
- Defining Our Profiles Update Validator
- Accessing HttpContext Within Our Service Using Dependency Injection
- Injecting Our Service Into Our Controller Using Dependency Injection
- Updating The User's Profile
- Spoofing PUT Request
- Testing Our Profile Update
Using Dependency Injection to Update A User's Profile
In this lesson, we'll learn how to allow users to edit their profiles. We'll also cover how we can inject the HttpContext into a service instance using Dependency Injection (DI).
- Author
- Tom Gobich
- Published
- May 29
- Duration
- 9m 46s
![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
Using Dependency Injection to Update A User's Profile
-
[MUSIC]
-
So for our profiles edit page, let's go and use our login or
-
register page as our starting point.
-
As this already has our layout and it has some form of a form inside of it.
-
So we'll go ahead and paste that inside of here.
-
And first thing we'll want to update is the h1.
-
So we'll do something like edit your profile.
-
We don't have an action for this quite yet, so
-
I'm gonna replace that with a hash.
-
And we're not gonna have an invalid credentials error.
-
And each error that we do have should go strictly onto our form input.
-
So we should be able to get rid of this error altogether.
-
And our first input field is going to be for our user's name.
-
And our users use full name for that.
-
So for the label, we'll put full name.
-
And for name, we'll put full name.
-
And the type will just be string, so
-
we can use the default there by getting rid of the type altogether.
-
Then we're gonna want a field for our biography.
-
And for that, we're gonna want to use a text area.
-
So let's jump into our components, form, input.
-
We can add something relatively similar to what we're doing for
-
our select, specifically for a text area.
-
So we can give this a copy, paste it just before our else.
-
Switch the if to an else if, change this type to text area.
-
And we can get rid of that else.
-
And then we'll want to add in our text area with a name of the field's name or
-
an empty string.
-
And then we'll pop the value directly inside or
-
default it to an empty string as well.
-
Keeping no spaces in between the start and end tag of our text area so
-
that no spaces get injected with our value.
-
So let's give that a save, jump back into our edit.
-
And now we can do biography or about you or something like that.
-
I can't quite recall what we actually named this, so
-
let's scroll up to our profiles.
-
Looks like it's description, so let's hide pgAdmin back away.
-
Call this field description and set our type to that new text area that we've now
-
supported inside of our form input.
-
We also need to add the default values into those inputs.
-
Before we do that, though, we will not need our remember me, so
-
we can get rid of that.
-
And let's switch our button text to update profile.
-
Okay, so let's scroll back up and let's take care of the default values.
-
So for our full name, we'll put value and
-
this comes directly off of our auth user, it's not on our profile.
-
So we'll just wanna do auth.user.fullname there.
-
However, our biography does come off of our profile.
-
So we can do value and we've added profile directly onto our state.
-
So we just do profile.description.
-
Let's give that a save and let's go see what we get.
-
So let's jump back into our browser.
-
That's from our last lesson whenever we were adding in that link
-
before it actually existed.
-
Let's give this a refresh.
-
There we go.
-
Now we can click on Edit Profile and we see Edit Your Profile with our full name.
-
Our biography is there, it just doesn't have a border on it.
-
So maybe we'll jump into our text editor.
-
That should be within our CSS, app.css.
-
And right down here where we have our input and select styles,
-
we'll just add text area to those elements there as well.
-
Add that back away, jump back into our browser.
-
And there we go, that looks better.
-
So now we need to add in the action for this form.
-
So let's go do that next.
-
First, let's jump into our routes, create a new route, router.
-
And we'll call this one put as we're strictly just updating information.
-
And we'll do /profiles.
-
Again, since this route is not user facing,
-
we'll keep with the resourceful naming and add the S to the end there.
-
Then we'll do profiles controller update is profiles.update.
-
And we're going to want to restrict this to authenticated users as well.
-
So we can give that a copy and paste it here,
-
or we can create a group and wrap those in a group.
-
Whenever we go to show a profile though,
-
we will not want that to be auth restricted.
-
So I think I'll leave this middleware as is for right now.
-
Give that a save.
-
And now let's jump into our profiles controller
-
where we need to add our update method.
-
We'll use our request, response, and auth HTTP context.
-
All right, we're going to want a validator.
-
So let's hit command or control shift P to open up our command palette.
-
And it looks like make validator was the last one that I used,
-
but if it isn't for you,
-
should be able to do AdonisJS make validator.
-
Since we have the AdonisJS extension added into Visual Studio code,
-
run that, it'll ask for a name.
-
We'll just call this our profile validator.
-
And there we go.
-
Export const profile update validator
-
equals vine.compile, vine.object.
-
We're going to have a full name of vine string.
-
Now we're going to want this to also match
-
exactly what we have inside of our auth validator right here.
-
So we could copy this,
-
but then we'd have to make sure that they always stay in sync
-
if they were to ever change.
-
So what we might do instead is take this validation rule,
-
export const full name rule equals,
-
and just apply that rule directly to this variable
-
and export it so that we can access it within our other validators.
-
And then just apply our full name rule to the full name field here.
-
Give that a save, jump back into our profile validator
-
and switch this to now use our full name rule as well
-
and import that from our auth file.
-
You could also create a new folder or file called shared rules
-
and place all your shareable rules within there.
-
This might be the only shared rule that we have.
-
So we'll leave this as is for us.
-
And then we have our description field.
-
This one will be a vine.string and it'll be optional.
-
Okay, that should do it.
-
Let's jump back into our profiles controller.
-
Const data equals await request validate using our profile update validator
-
and import that from our validator file.
-
Now the full name field goes directly on the user
-
and the description goes on the actual profile.
-
So since we need those to go in two different places,
-
let's extract the full name and the description
-
just directly out of the validated fields.
-
We're then going to want to grab our user's profile,
-
which would look exactly the same as this line right up here.
-
So rather than copying and pasting that,
-
let's show how we can create a service for that.
-
So right click our services, new file, call this profile service.ts,
-
export default class profile service.
-
And we can actually make our HTTP context directly accessible
-
within an instance of our profile service class
-
by injecting it in using dependency injection.
-
For that, what we'll want to do is decorate this class
-
with @inject from the AdonisJS core package.
-
Looks just like so.
-
Add a constructor onto the class.
-
It is a protected property and call this variable ctx
-
for our HTTP context and type hint it as HTTP context.
-
Now, whenever we instantiate a new instance of our profile service,
-
we're going to have the HTTP context directly accessible
-
within this class, thanks to this inject decorator.
-
AdonisJS will spy that we want to use the HTTP context
-
and directly provide it on the class instance,
-
meaning that we can now do async.
-
We'll create a method to get the authenticated user's profile.
-
Since this is our profile service, we can just call this find.
-
We can now do return this.ctx.auth.user,
-
or we could add a getter, so get user,
-
and return this ctx.auth.user to simplify our call there a little bit
-
to just this.user.
-
And then we'll just want to replicate our related profile query
-
first or fail call.
-
And now, since we're using dependency injection
-
to inject our HTTP context directly into this class instance,
-
we no longer need to provide user information
-
into the find method, but we can instead directly access it
-
off of the instance using the HTTP context.
-
Now, adding it this way does hinder the places
-
where we can actually make use of the profile service,
-
since it will now require an HTTP context.
-
So if you were to need this service in something like web sockets,
-
event handlers, model hooks, and the like,
-
you will not or may not have an HTTP context in those environments,
-
meaning that with this approach, you would not be able to use
-
your profile service.
-
If your application just strictly works with HTTP requests,
-
that's not something that you need to worry about,
-
but do keep that in mind.
-
Okay, so we can follow the exact same approach that we did
-
actually with this HTTP context to inject this profile service
-
into our profiles controller.
-
So we'll add the inject decorator from AdonisJS Core
-
onto our profiles controller, add a constructor for protected,
-
call this our profile service, and type in it as our profile service.
-
You can also use this inject decorator at a method level as well.
-
So if you just needed it within edit or update here,
-
you could add inject onto this particular method,
-
and the injected property will come through as the second parameter.
-
So it would be something like profile service here,
-
profile service, and add inject there.
-
We need it in both of ours, so we'll put it at our controller class level.
-
And we can now replace this call with this profile service dot find.
-
And we can now do the exact same thing down here as well.
-
So const profile equals wait this profile service dot find.
-
And we've now removed the need for auth from our HTTP context
-
inside of our controller handler.
-
We can go ahead and give this controller here a save,
-
jump back into our browser, and give this page a refresh
-
just to verify that everything here is actually working.
-
And it looks like everything worked a-okay. Awesome.
-
So now that we have our profile information in our update call,
-
we can either do profile dot description equals description,
-
and then await profile dot save to persist that change to the database.
-
Or we can do profile dot merge, which accepts in an object
-
of any properties that we want to update on the profile itself.
-
And in our case, that would just be our description.
-
It will leave any properties that we do not provide within this object untouched,
-
and only update those that we actually provide.
-
And furthermore, the merge call is actually chainable,
-
so we can chain directly off of it our save call and just await this chain.
-
Then we also need to update our user's profile.
-
And for that, we do need to pull auth back into this method.
-
So we'll want to await auth user exclamation point to assert it.
-
And we could do the exact same thing here.
-
So we can merge in the full name and save that change.
-
Once we've saved those changes, we can return, response, redirect, right on back.
-
Give that a save.
-
Let's jump back into our profile edit page and update our action.
-
So let's see.
-
This is route profiles update, but we also need to spoof it.
-
So we'll do query string underscore method put to spoof this method as a put request.
-
So with that saved, we should now be able to jump back into our browser.
-
Let's do John Doe edit.
-
And this is a little about John Doe.
-
Let's try to update our profile.
-
Okay.
-
And it seems like everything worked.
-
And we can actually verify that because now our logout says John Doe edit rather than just John
-
Doe.
-
We can do a hard refresh and everything here is still populated.
-
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!