Full Stack TypeScript - Let's build a "Threads" app with Angular & Nest JS

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello everyone and welcome to this tutorial I'm Mohammad essan and I'm a Google acknowledge expert in angular and I'm a software architect and in this video you're going to learn about an app that we are going to build it's called thread surprise surprise because everybody's talking about it Facebook Twitter and now us it's just a joke of course so I'm going to be building something simple but it would include threads and comments and we're going to make this a full stack application before we actually start I want to show you something if you know someone who understands Urdu or Hindi or if you do and you want to get started with web development with the very very basic like just installing the softwares from the beginning we have developed a free course that you can use right now it's called the web development basics of course and we have more than 340 students right now and when you go inside the course you find a lot of lectures I actually counted it to more than 100 videos right there which include a bunch of Concepts including I don't know talk about JavaScript CSS talk about other stuff as well but it's super nice to have all of that in your bag when you're starting with web development so having said that let's actually jump to actually what we are going to build so I created a rough diagram on how it should look so what we're going to build is going to be an app that essentially shows all of this so what is this really these orange boxes are sort of threads or posts if you use stagger flow which I hope you do you might have seen questions so you can assume this top level thing called question we are still going to call in the comment so we will have top level comment and then nested comments so everything inside this threads application is going to be a comment so a top level comment is a one which doesn't have a pattern so it's on the top then inside that we could have a nested comment so you could imagine looking at it being something like this right and then obviously the other two if I remove this one this one this is actually gonna look like this so these two are actually the comments or the nested comments of this comment this particular comment so you could also imagine being like this maybe that makes much more sense when we draw it like that so can I do this no yes no no because now this is a group so it's going to be a problem if I try to do it but you get the idea essentially in this particular situation if we have a particular rectangle it should oh it should look something like this this and not really so now this is what I meant so a particular comment can have writing something into it it could also contain nested comments as well and then you have another comment and then another comment blah blah blah so I hope you now get the idea overall what we are looking at okay so having said that let's actually copy this to put it here put it like this yes cool makes much more sense so this is what we are going to build now that you understand this is how it's going to look we're going to use angular for the front end we're going to use nest.js for the back end and we're going to use mongodb for the database so we can either start with the back end creating all the entities all the endpoints or we can start with the front end just laying out different stuff so we can go either way in this case what I would like to do in this particular case is to start with the back end so we can write some stuff write some API calls and then actually make things work so we'll this will also allow us to test our API even without having a front end so for nest.js what you can do is you can always go to nest.js official documentation and see the default getting started so when you go there you can go to introduction or overview First Steps so here you need to install nest.js yscli so you can either do that and then you can create a nest new project so what I'm going to do is I'm going to actually ins not install it globally but rather run it even without it so what I'm going to do is I'm going to bring up my terminal let's open that and then we'll go to desktop and then we're going to create an app so we can say npx in scli new and I think that's pretty much it we're going to call this threads be or threads backend and this should use the nest jscli and create the new application for us and it will ask the relevant questions so I'm right now on a slower internet so I'm going to just let this finish and then we'll go from there so what package manager do I want to use I'm going to go with the yarn at the moment let's see what else this asks us cool so this is going to just install everything and we are going to continue right after cool now that we have the project created we can simply do code spreads be and this should open up our nsjs project so I'm going to just zoom in a bit so we can see all the things oh we have a new release of vs code as well nice now that I have this what I want to do is to quickly go ahead and serve the application so we are able to see the nest.js app out of the box so npm run Dev and I hope we have a Dev script do we have a Dev script no we had a start Dev so let's do npm Run start Dev this should start the nest.js app in the dev mode and we should be able to access some apis now if you look at the source code or the source folder this is how it looks so if you're familiar with angular already you're going to be like aha this is something I already know because it follows the same pattern we have controllers we have modules we have Services well in angular we don't really have modules anymore but it sort of took inspiration from that so if you look at the code so we have a controller which should have some routes so it has a hello route when this is when we send a call to localhost 3000 which this runs on by the way we get something back what do we get we get something from the service what do we get back we get hello work so if I go to my browser now and if I go to localhost colon 3000 you see the hello world where is this coming from really this actually comes from the service version hello so what happens really is the controller defines the route where you want to go so if it's nothing for example if it was hello then I would have to go to the hello route to get this information let's go back and try it out so if I refresh the page now you can see it says get cannot get the backslash because I just changed the route and now if I go to Hello then it's gonna work and now it says hello world so you can see that if I don't give it anything it just runs on the default route let's keep it that way and we're going to add more stuff so how should things look like in our backend the first thing that we want to do is to have some sort of data the sort of data that we want on our front end is essentially an array of comments so I can I can basically render a lot of comments on my screen when I see the front end if I look at the common object it should actually look something like this so if you have a look at this I'm going to actually go down there was one more yeah so this is how the array should look like every comment should have a text it should have the user who actually created the comment it should have a parent ID for the comments which are top level comments which are this one so the orange ones they will not have any pattern because those are at the top but any nested comment like this blue one has the parent ID of the parent comment so every component or every every comment can have either a parent ID or not if it doesn't it's a top level comment if it has then it's a nested common so a parent ID null in this case means this is a top level comment every comment would have its own ID this will be generated from mongodb and can have unnumbered likes so we're not gonna work with likes at all in this application so you can expand on your own and you can work on it uh um after you have finished the tutorial but let's have a look at another one so if this had the id4a the other comment could have the id4a as a parent ID which means that this one is going to be a nested comment in this case so we have now seen two different examples of how the comments should like or should look like we have parent ID null and then parent ID exactly pointing to the ID of another component or not component with comment now that we know how the structure should look like let's actually try to analyze what kind of information or what kind of uh structure of our database would be before we actually go there I want you to think of the application itself because that's where you get the information of what kind of things should be in my database so first of all if you look at the card here and the UI here you're gonna notice that we have a couple of things first we have this whole comment which has a bunch of likes so first of all we have comments in the database secondly we have a user who actually comments so we have the user ID being mentioned there or the username sort of being mentioned there and that actually is something that we also need in database so in our database we just have two things we have if I write it down right here we have in our mongodb database we have the user and then we also have the comment let's actually make this a bit smaller so yes those are the two things that we actually want so we want users and we want comments in our mongodb and that's pretty much it now that we have this let's actually look at how do we try to create users or get users or get comments blah blah blah so the first thing that you would look into in SGS is you're going to look at controllers for example so every route is a controller so if I want a new route let's say if I want to handle comments or users I would have to create a controller and then I would have to have a service because this is the route definition and the route definition or the route itself is supposed to get the information from the database via a service so you can imagine the backend architecture being multi-layered so what happens usually is you have this situation where you have for example if I zoom out a bit we essentially have are how can I make it smaller so this is going to be font size medium small maybe so this is going to be the controller so you have a controller which is your route then you have a service and then you have your database so this controller is the one that is the route definition so this is your route or your you say you can also call this API call so when you have your API call triggered what this basically does it tries to go from there to service or the service file and then the service goes to the database the database essentially responds back and then the service responds this back to the controller and this all of this essentially goes finally to your app so if I say app here this is essentially what happens your app tries to request from your backend which is essentially your controller and then everything gets sent back to the front end does that make sense so far I hope this does so now that we know this is how it should work let's actually create those things one quick way of creating because if you look at the app itself it has a controller it has a module it has a service blah blah so many things right so what you could actually do is if if you want to create a new sort of a thing I'm calling it a thing but in nest.js we call it resource so when we have a new resource we can actually create it really easily with the crud generator so here you look at documentation it says Hey for quickly creating a current controller with a validation built in you can use the CLI Square gender called Nest G resource and then the name so since we were using npx what I can do now I can open a new terminal and here I can say npx nest JS slash CLI and then I can say uh what was it generate so generate resource and what is the name of the resource I'm generating it's actually either users or comments so let's go with I don't know let's go with users first so I'm going to say users and once you do so you can also run dry run so when you run this with dry run it's going to show you how it would work but it will not actually execute anything so no files are created so let's say this was supposed to be a endpoint or rest API endpoint and do I want crud entry endpoints yes I do now you can see dry run enable no files were written to the list but what would happen really if I run this command and remove the dry run then this would actually happen so the files will be created now you don't see any files at the moment but what it would do is it would create a user's folder inside source it would create controller it would create a module service the test file for those and then it will also create some dtos the dtos are essentially data transfer objects and and then we also create a user entity we may sort of remove this user entity later on when you work with a or mongolo database because that's going to be the entity in that case and then also it updates the package.json and updates the app module so any new thing that we create for example users has to be imported into the app module right here in the Imports because this is the entry point so when you go into a building and you want to go to a particular door you enter the building's door that is the first thing that you enter and then you find all right which is the apartment that I want to go to so you can imagine your app being a building and then app module is the door of your building the main door so you go inside there you look at Imports and then you you start finding more things within your application that the app has to work with so if I just remove the dry run oops let's do it now then it should ask me the same questions and we should be able to just create the file so rest API yes credit endpoints yes so now you can see that it quickly goes ahead and creates this user folder and automatically updates the app module as well with users module this is important if you don't import this then your nest.js app doesn't actually understand that you have a new thing that it should work with so just running this command from the CLI makes it possible for us to create the files so I don't have to create them manually and still keep the good instructor that we have now if I go to the users route you can see that a lot of routes are already created now you can remove the ones that you don't really like for example you have to get route to get all the users you have the get ID which is a dynamic route which uses the param ID to get a particular route or a particular user then you have to updating a particular user deleting a particular user and creating a new user as well so all of these routes are there then also in the service all the methods are already there now what I would do is I would just replace this hard-coded string with actual database query to create a user to get the user and what not so still the structure Remains the Same you have your route calling to the service and then the database so in this case I have the user's route or user controller that talks to user service to get the information and then this is supposed to talk to the database okay so now that we have this route also created let's actually try this out since we just created this so if I go to localhost 3000 and slash users you can see this says this action returns all users now you could also say user slash one two three and then it returns one to three users so whatever URL you have here it actually triggers the right endpoint and everything should be working cool so now that we have this let's also create the other module which is going to be comments so I'm going to run the same command but I'm just going to say comments instead of users and that should create the comments module for me as well so it's going to be rest API yes crud and now you can see that it adds comments and it also imported the comment into the app module automatically so now we should also have the comments route and it's you can see it says returns all comments now that we have these routes created we essentially have our API created already now this was really quick right we are already like in 18 minutes in and we have a back end running we have um yeah we have all the apis as well ready we just need to change the code to make everything work as it should but that's a different story now that we have the apis ready let's actually go to see how we can include Mongoose or mongodb in our application and we are going to use something called Mongoose so if you search Mongoose you should be able to see mongodb Mongoose getting started and you should be able to see how to actually work with mongodb right here in this article you'll learn how to create yeah obviously so in this case what I want to do is let me see if I can find the right one now maybe this is not the one so mongodb this should be right here techniques Mongol this is the right one so it should be techniques and mongodb now what you want to do here is to install all of these packages so it's an sjs Mongoose Mongoose so since I'm using yarn I'm going to use the different command instead of npmi so I'm going to go here and say hey I'm gonna say yarn add and then sjs Mongoose and Mongoose I'm gonna actually install both of those packages and then I'm gonna use them so once you have the packages installed what you have to do is inside your app module you need to do a mongoose module dot for root and then the database URL that you have so what I'm going to do is I'm going to copy this I'm going to go here and then inside the Imports I can actually add the mongoose a statement that we just talked about so this is going to be Mongoose module for root and then you can import this from nest.js Mongoose and once you do so all you need to do is to just replace this URL I don't have my database running on localhost so what I'm going to do is I'm going to go to a cloud database for mongodb so for that you can go to Atlas And it allows you a free tier that you can use so I already am signed in with this one or not signed in but I do have an account so I'm going to go to sign in but you can just sign up and when you do so you can sign up with either Google or your GitHub so I already have an account and I also have a database so I'm gonna show you how to connect with the database so once I sign in I actually see this right here and you can see that I have a cluster you might need to create a cluster so you can click create and then this should create a new cluster you can select the free tier AWS blah blah blah once you are done just select the free tier create a cluster and then you should be good to go one once you have a cluster ready what you should see is something like this so you have this sort of database or maybe if I go to code with s and LMS this is probably what you would see you would have a project that you can work with and then for example I have this LMS here for some reason I don't know why I named it LMS but here is my cluster now if I want to connect my nest.js application with this database what I would do is I would go to connect and then I would have to copy the connection URL so when you go to drivers you should be able to see the URL that you have so you scroll down you copy the URL from here and once you do so you'll also need a user and a password so if you don't see a URL here or if Atlas asks you to create a user what you should be doing is you go to database access and then you create a user so here you can say add new database user you create the user with password so you can name the user you can give a password and then when you're going to connect with that a particular database instance you're going to be able to use that so for the sake of Simplicity let's say we create a new database user here we say something like threads user threads um we have a password that we also use um we we can also Auto generate a secure password and I can copy that password cool and then what we can do is we can just add this user so now I have this threads user that I can work with and I'm gonna obviously delete this user later on so what I can do right now is maybe open notepad just to copy the password so this is how it looks like so far and then what I have right here is a user that I can work with now it has Atlas admin blah blah we don't need to look into that but when you go to the database then we should be able to access it by the username and password but one more thing that you would want to do is if your app runs on a particular because you're going to be working from home from work from your I don't know University or college if you go to network access you would have to make sure that you can allow this zero zero zero zero slash zero which means that it allows access to this database from anywhere now this is not the most secure thing that you do because when we deploy our applications to production we usually just use the IP of our production but you can just put this so you don't get into issues like your database is not being connected by your backend so once you have the database access and the network access figured out now you can go your database then connect and then you go to drivers and in here you can see the URL string so I'm going to copy this now and I'm going to go to my code and actually use this one so now instead of this for root I'm gonna replace this with my entire URL and you will notice that it's says username and password so you need to use the username and password so I'm going to be using the username threads user thread stash user and then the password itself so I'm going to copy it from notepad and now I have my URL string ready to be used so I can now just move away from this file and I should be able to connect with mongodatabase easily now that we have set this actually let's look into how can we create a schema so I'm going to just be lazy and copy paste stuff from here so in here you can see that we have schemas CAD schema.ts that has this particular code so I'm going to copy this from here and then in my code I'm going to go to users and inside entities we have user entity so I'm going to actually delete this entity because we don't use it but inside users I'm going to say schemas and inside here I could say something like user.schema.ts and then in here I'm just gonna paste everything so for some reason I don't know why but uh the the linter doesn't like this so everything is red even though there's no issue with the code so what I'm going to do is I'm going to actually run npm run lint and that should fix it I don't know what it changes but it still fixes it somehow you can see that it fixed it automatically maybe there's some white space or something but anyways so now that I have this I'm going to call this not cat document but I'm going to call this user document so I can select cat make it like this and then just replace with user so user document user user schema and then create for class user now in my particular user all I want is a particular name so I don't want the age or breed so I'm going to remove that and this is the schema so all my user has to have right now is a username and that's pretty much it now that I have this model created let's see what happens next so after the model has been created I should be able to just use it in uh in my services so here I'm gonna skip all of this and then what I can do is I can say Mongoose module for feature and then this is what I have to import in my cats module or I don't have a catch module but I have a user's module so I'm going to actually copy this statement go to my users module and inside here I need to use Imports and the array would have this here so I'm gonna import Mongoose module for feature the name is gonna be user dot name and I'm gonna import the user from schemas user and then I need to use the user schema just like this and this should be good now now that we have this let's actually have a look at the documentation so in my service I'm supposed to inject the model using this Constructor and then I should be able to use it in my functions so what I'm going to do is I'm going to just copy this go to my code and inside my service I can now use this function so I'm going to inject model imported from lux.js moves I'm gonna or next is Mongoose and here I'm going to say user dot name this user should come from the schemas user schema this is going to be called user model and then the model is going to be user from the same file so in this case and now the model should also be import from Mongoose now that I have this injected I can now use my model so what I can do now is find all means I just go ahead and say this dot um what was it user model dot find and I'm finding dot exec and that's pretty much it this should return an array now if I go to my database here and if I go to my collections browse collections I should automatically be able to see the threads app once I start working with that or create something there so if I go to localhost 3000 users let's see what happens you can see that I got an error let's see what's wrong all right let's see what's wrong so in here if I go right here it says user is not allowed to do action find on users aha that's interesting that means that this user doesn't have any rights so that means I need to go to my database access and in here I need to assign some rules so in this case we have built-in role for this user let's say at built-in role and we can say read and write to any database so let's do that update the user and if I now refresh does it still work no [Music] um is threads user that makes sense to read and write to any database as admin I think this should work but I'm gonna restart my server to reconnect with mongodatabase so let's quickly check this out all right now if I try to do the same thing there we go now we get the array so the key was to assign a particular role to this user that I just created so now that I have this I get an empty array that means if I now go to my database and if I go to my collections you can see that still I don't have the threads collection or the threads um even the threads database not the collection the threads database that is because nothing has been created so far so what I want to do now is to be able to create a particular user so let's say I go here and in the create method I say something like hey this or you can say something like um const saved user equals new this dot user model dot or new user model and in here I can provide a document which means here all I need is a name so I can say SN ayaz is an is the name that I want to create for this user and then I just say save user.save or user to save and then that means I get a user created automatically now this only works when we want to do a post call so this wouldn't work when we have a get call so how do we make it work now you could have some rest API endpoints that you could make uh make it work so there's an extension called rest curl or something if I remember it correctly if I can find it that would be super awesome I have so many extensions is addressed uh curl dress client this is the one so what you want to have in this rest client is if I go to the documentation and look at the calls you can create a particular file that has these endpoints and then you don't need a front-end to do it and you also don't need to leave vs code so what I can do here is I can create a folder and I can call this rest inside here I could create another folder called users for example and then inside users I could then just or I could just say users.hdb to be honest and delete this folder so delete user inside here I could say users.http and inside here we have this so I can say something like http localhost 3000 and slash users no ID and I can remove this guy this here and then we can also have the same URL but with a post so I'm gonna remove the get from here and then I'm posting a user with the name of the user so I can say something like I can provide the name or not provide a name it's up to me right now we do have the user service using the data hard-coded but we could also receive it just from the create user D2 as well um let's do it properly so what I'm going to do is I'm going to go to user's controller and we receive the request body when we create a user so from the front end or from the stress client I'm supposed to send a request body of what is the name of the user so if I open create user dto you see it's empty but what I want to do from here is actually something like this I can do hey this is gonna actually have a name that's going to be of type string okay that means now my route is configured to receive a name and once I have that name in my controller right here I actually pass it to the create function in the service I get that create user detail and then I can just assign this create user video automatically and this should work so now that I have all of this let's actually try this out so here I can say SN I ask and let's see what happens when I run this request so if I get the user you can see we get an array if I try to send requests on a post you can see that we get a response back from the API that has the name of the user and ID of the user and the version of this document so if I go back to my database and if I try to do a refresh you should be able to see now that we have the threads oh we don't have the threads and I know why because I'm using the test database and here is my user as an iOS so by default if you don't give your url string or your mongodb uh in your back end a particular database name it just connects to test so what you need to do is when you try to copy this uh let me actually show you so when you try to connect with the string here right after or right before this question mark you need to add the database name as well so I'm going to go to my code and inside here when we had defined it the module and the URL here I want to say that this is gonna be of database threads and this now creates a threads database so now if I go back here and we try to do the same thing so I try to do users HTTP and create a user I still get a new user with the same name and if I now go to my database I go to browse collections I should be able to see the threads database inside that we have the user's collection and that now has this particular user right so this works flawlessly what happens if I try to not pass this at all what would happen then so if I try to send a request then still everything works I get a response now we would actually have two users but one without a username and that is stupid we shouldn't be having that so this requires some validation so I'm going to first delete this because we don't want that second you can see validation so we need to be able to validate that the front end actually sent us a particular information so we're gonna use this class validator and then we can use the validation pipe automatically so in this particular case if I wanted to show you what would happen is you would have to install these two packages class validator class Transformer so what I can do is I can go to the terminal and then I do this so I can say npm or not npm but yarn add and class validator and class Transformer once you do that you're gonna do this so you don't need validate pipe options blah blah blah no need to do that in my main TS file where I have the bootstrap so if I go to my main TS file right here this is sorry I'm going to close this close this and then open main TS again so in here you can see that we have the bootstrap function so in here you need to use this use Global pipes new validation pipe so in this case you go here and then here you can use the validation pipe imported from this common and that's pretty much it now our application can use validation out of the box now how do we really validate anything n or dto we can use these validators like it's not empty is email is a string blah blah blah so if I go to my create user dto just above this name I can say is string which means that this should be a string and this should be provided if there's no name then the request would start failing so if I go to users HTTP and if I try to submit this now the name must be a string yay so now we have validation it automatically throws the 400 sends the error and actually shows what should be done so if then I provide name here but I provide this as a number then it should also fail so if I say send request name must be a string it's still the same thing but as soon as I provide a name let's say I say something like moxin I ask now if I do this send the request you can see we have a new user because this was a string it could work now if I go to my database I should be able to see two users instead of one one is me and second one is my brother in this case cool so now we know how to actually work with users if I try to now go to this endpoint or even go from here if I do send request you can see that now we get an array of two users so our users endpoint or API is actually done now let's have a quick look at the comments so we can wrap this up so what I'm going to do is I'm going to go to the comments and in here I'm going to also create a schema so I'm going to be lazy copying schema folder deleting entities we don't want that and then I'm going to call this uh file I'm gonna call this comment oops comment Dot schema.ts and in this particular case let's replace user with comment make sure you have this ticked in so it only affects the same case so here the common document hydrated document comment comment then comment all right looks awesome now that we have this we want to see what a comment has so what you're going to do is if I go back to exactly draw here you can see that any object that is a comment has to have a text which is a string has to have a user ID which is supposed to point to a particular user should have a parent ID and then the ID itself is generated automatically we don't need that in the schema and then the likes so that means here the first thing is text because we want a particular text of the comment itself then what we want is essentially the number of likes so a prop that is likes which should be a number then what we want is to be able to connect to a user because a user posts a comment so this should be a relation between the user and the comment how we do it is that we create a prop and then we use mongoose and then here we ref to user so we are saying hey this is supposed to be a user or you can call it owner you can call it user and this is supposed to be of type user so you're going to import the user type from user.chema so in this case what we are saying that hey this comment has a user by default when we save this into the database this becomes the user ID but we are defining hey what type is this so this is supposed to be object ID but it's going to reference the user and when our application requires it it's going to send back the user object itself now we are also going to define the parent so instead of using Looper so instead of using parent ID and user ID we are actually going to use user and parent in this case so user and parent just to keep it simple all right now that we have this let's go back to the code so now here look at this one now we are seeing the parent of this comment can be either a comment or null so this is sort of the type type but in the database it's going to be an object ID and it's going to reference the comment collection so this is the comment collection which means apparent of comment can be a comment so it's going to reference the same collection but we have this property right there finally what you also want is to be able to I don't know uh create timestamps for this one I don't know if there's an option that is that we can provide right here maybe we can timestamps no all right let's have a look at this this whole thing maybe we can do so when we do a comment schema we say schema Factory create for class so actually let's have a look at uh time stamps now let's Google quickly so nest.js mongoose timestamps uh uh let's see um so this should say schema timestamps true that's the one that we want cool that makes sense so if I go with the schema here I can say time stamps aha beautiful so here each comment that we create is gonna have a created ad or uh I think it's created or updated or both actually so we're gonna have a look when we actually get to that so now that we have the scheme already we want this schema to actually work with our our resource our comments so our controller services so we want it to work inside our module so if you remember we did something with users module and we imported Mongoose module.feature uh and then we import it just like this so we're going to do the same for comments so I'm going to go to comments module and inside here gonna import this so I'm gonna import Mongoose module and then here I'm gonna say or not but comment which is supposed to be imported from schemas comment dot schema and then here I'm going to say comment schema now this should work now we should be able to access Mongoose module the final thing that we need is similar to what we did in user service we injected the model inside our service our service can talk to the database so we did this with user service we're going to do the same with common service so in here I can go ahead and inject it I'm going to inject the model called command make sure to import it from schemas and then here this should be comment as well and this should come from Mongoose and this should be Capital C in this case so now we have injected the model we can Now say find all and we should be able to say return this Dot and we can also call this comment model instead of user model so this dot comment model dot find dot exec so we execute this query this should return an array so if I go to my browser and change the url to comments it should also give me an empty array since there's no comment there now if I want to create a particular comment I want to be able to provide certain things from there right so the create comment dto now looks something like this so we would have to have what a text of type string this should be is spring so we're gonna say all right in this particular situation we should have is a string that should come from the class validator so I'm going to actually go to user service not user service but user dto so create user dto I'm going to copy this from there go to create common dto and paste copy pasting is what I do for a living so now here we have the is a string of course now apart from the string what we want to do is we also want this to not be empty so it could be a string but we want is not empty as well so if I go here I can say is not empty and this should also be the case so it should not be empty it should be a string and then it should work and then finally we should also have when we create a comment we should be providing the user ID so is a string and this should be a user ID or we could also say is a string or is not empty in this case or both actually so it's not empty and then this should be user ID of type string and then we have the parent ID now the parent ID could be null or could be string so it could be null it could be string and it's not really something that is mandatory so we're gonna just leave it as it is not that we have the create common dto we're gonna see all right how do I create a comment when the time comes so if I go to the particular service we're gonna do something different compared to user service so in the user service you're gonna notice that here we just said new this dot user model and then we just said user to save save but in this case we are going to do a bit differently and I'm gonna tell you later why so if I go to particular uh uh file which is comment service and here now what I can do is I can say return this dot comment model dot insert now you could use insert before but now it's not there so you can use create and here what you should be able to do is you have a create common dto and if you provide it let's see what happens it doesn't complain really because it expects you to provide a comment component but there's a big difference because in here in the create comment dto you have user ID and parent ID in the schema you have the user and parent so no user ID or parent ID so we need to sort of map those things so what I'm going to do is we're going to use something like this so if I go to create common dto not there but comment service instead of doing this what I'm going to do first is to do something like this so we do something like common model dot create we take the text from the create common dto we take the parent ID if it's provided from the dto or from the front end or from our rest client or we assign null and then we have the user so the user ID should be there or validation basically make sure that it's there so once we have this then we can just say hey we have a created comment so just return created comment and that should be it now that we have this let's actually try this out so what I'm going to do is I'm going to go and shamelessly copy this paste this again and call this comments dot http changes to comments and change this to comments as well and then we add some text so we can say first comment and then we should have a user ID but I'm gonna keep this empty for now so what happens if I do send requests empty array when we try to get comments if I create a comment what happens then user ID should not be empty so I have the user ID as a string but it's empty so it should not be empty now which user should I create the comment from so if I go to the users I can actually try to see hey which uses do I have I have two users so let's say we copy the ID of motion and then the comment is now supposed to be created by motion okay so we say first comment now if I try to send the request yay you can see that I got the database and recreated we have text first comment the user is there parent is null we have the ID created and updated ad all of those things are there which means that if I go to my database I should be able to see them so if I go to my database and if I try to go to threads and in this case let's also see users and comments so if I go to comments here we have this should be or once it gets loaded of course oops threads blah blah blah maybe I need to refresh it maybe I broke or Atlas so if I go to comments now I see it so first comment user and you can see the user is an object ID and the parent here is particularly null right now what if I wanted comments but I also wanted the user object instead of the ID in this case we have something in mongodb that we call populate and that actually allows us to just populate the user instead of the user ID in this case so I also get the name of the user automatically in one single query so if I wanted to do that I would actually go to my comment service where I'm trying to say hey return the created comment or find all so when I do exec just before that I can say populate and then I can give this an array and inside the array I can say user now if I go back to my URL here and refresh magic you can now see that the user is not an ID but the entire object of the user which means that if I try to go into my database and actually change the user's name so if I change to I don't know motion IRS new in this case and if I update the document my comments query or URL would automatically get the information so all I all I sort of did was binding the user ID with each comment that is being created so we know which user created that and I don't need to maintain the name or add everything so anything happens to the actual user object I get it automatically by doing a populate let's actually fix this back I'm gonna change this to motion as update this right there and then I should be able to get the new information so now we have an API that creates comments has validation and also returns comments as well one more thing that I want to do is when we are creating comment you will notice that the response that we get back does not have this populated so I'm gonna show you this again so now we're gonna create a second comment by motion so if when I do this you're gonna see that here the response that I get from the server actually does not contain the user object it contains the user ID so when we get information it populates the user when we create a comment it does not populate the user in response we want to be able to do that and not only user but also the parent as well if there's a parent of course so in this case what I want to do is if I go to the comment service I want to be able to do something like this so I'm going to go to comment service and here let's first make sure that if there's a parent that's also populated we're going to look at that in a minute but when I'm creating just instead of returning the created comment just like that what we can do is to do something like this so we can do it then and then we have some information there so what we have is here a doc and then in here we can do return doc dot populate and then here I can do the same thing I can say user and parent please send them back and publish them now if I create something new it's actually going to return the populated information so if I go here into the comments HTTP and if I say third comment and then if I create this look at this the response sends back the user instead of the user ID as it was doing before so my create call sends back the populated information my get call also sends back the populated information with each comment so if each comment has the user as you can see now what I'm actually going to do is I'm going to create a comment that is nested comment so this third comment will have a nested comment so we could say something like hey let's try to find the ID of the third comment so this is the third comment that we have right so what I can do now is I can I can copy the ID of the third comment and I'm going to create a new comment that says I am a nested I am a nested comment inside the third comment okay now what I want to do is to be able to provide the parent ID here and then I should be able to do this okay so now if I provide the parent ID it should take that parent ID and then assign it but I also want to change the user so I don't want more sin to reply to the third comment I want SN to do it so I'm going to go to users send the request and copy the ID of SN in this case and in comments I'm gonna replace the user ID so once I do this and now if I try to create a comment that is a nested comment let's see what happens boom you can see that the text is there the user is there and the parent is there as well so I can see I'm a nested comment inside third comment has a parent that is the third comment n has the rest of the stuff as well so now I actually get the information that I want so if I do a send request or get request you see all the comments with the third comment but also the nested comment having the parent and user both all right so if I go to my browser and refresh you can see that we have the comments right here and there's the first comment we have the second comment and then we have the third comment and then we have the nested comment containing the parent and what not so this is the response that we actually want to send back to the front end or the rest client in this particular case now the only thing that's remaining is that I want to be able to get nested comments by parent ID and I can't do that at the moment I only have one endpoint that returns all the comments now what I want to do in my application is if you look at this UI if I have let's say 50 top level comments and I have I don't know 200 nested comments I don't want to load all the comments when my app starts it would be much smarter to just load the top level comments and when we expand this or when we want to reply to this comment only then we fetch the nested comments so that means there are two sort of calls that I want to do one sort of call would be get all top level comments which I should be able to do by do going to the comments in this case the other one is get all nested comments by parent ID and that should be the route comments also but it would have a query that would say it's parent ID and then you can provide the parent ID blah blah blah right there which means the same endpoint but allowing us to pass parent ID and that should just return the nested comments so we're going to implement that right now and to do that what I'm gonna do actually is I'm gonna go to my comment service and in here instead of the find all I'm actually going to create two different functions I'm going to call this get all comments or get top level comments comments and in here I would just do exactly what find all does and then we can remove find all later on so that means I'm just going to do a populate but the difference would be if I want the top level comments I know that all the top level comments are essentially having parent ID null so if I get those you can see the parent ID is null here or the parent is null here the parent is null here only the last one has the parent ID in this case so what I can do now is I can go to comment service and say hey get all the particular comments which have parents set to null and that would essentially return the top level comments similarly I could also have another function that would say get nested comments or get comments by parent ID and that means I need to provide the parent ID as the string here and then I should be able to say parent ID and this should return all the comments just by that parent ID so once I have this what I can do is inside my controller where it right now uses the find all method in the find all route which is just slash I can change this so I can also use something called query from this GS decorators and I'm gonna call this query patterns like this so query params and then what I can do is I'm gonna look into how these query patterns look like or I can just take them or I think it's any so we can use that so I can say something like if query params dot parent ID if it has a parent ID then what I want to do is I want to do return this dot comment service Dot and then I can do something like get comments by parent ID and I can pause query params.parent ID parent ID so that should actually make everything work flawlessly I also need to import query from the Cs common of course so now I have that what would happen if I don't have a parent ID then just get top level comments which means that I don't have to pass parent ID null in this case so now I have this let's actually make it work so if I try this now on the browser or even in the rest let's see what happens so now that I've changed the default route if I go to comments.hdp this should only return top level comments if I hit send request you can see that I have got one that has parallel second that has parallel third that has parent null and I don't have anything else so now I'm not getting 4 I'm just getting 3 which are top level now if I want a particular uh comment that is or nested comments by parent ID I know that the third comment has nested comments so I'm gonna copy the ID from here then I can go right here and then say hey I'm gonna add another route that's going to be get and then let's also say HTTP localhost 3000 slash comments parent ID equals and then we pass this and then what we want to do at the end is have the separator so now if I try to send this particular request let's see what happens send request and you see that I've got an array that has just one item so I only had one nested comment that's why I get that nested comment I'm a nested comment inside the third com the third comment if I change anything with the ID let's say I add X to it which means now this is or not X to it but in here so if I change the parent ID to something that doesn't exist maybe X here and then if I hit send request you can see that it says internal server error so it doesn't find anything now if I have something then that works if I had something that doesn't work then it crashes maybe it would be a good idea to not have it crashing so what you would want to do is you can go here and say hey I have a comments service and also let's have a look at the code as well so it says object ID failed for Value blah blah blah parent for model comment so in this case you can look at your comment right here and then if had if it has a parent ID then try to have a try catch right here and you can say Obviously try returning it if there's a catch then you can throw some errors right and in sjs has some specific ways of saying what went wrong so let's try that actually so let's try to find the error so http errors I think and here we have built-in HTTP exceptions exactly so in here what we can do is we can say Throw new and then we can see something bad happen blah blah so I'm going to copy this one and in my code I'm going to catch this and then do this so bad request exception from here so I can say something bad happen the cause is new error description some error so let's save this so in this case we can also say e Dot message so taking that particular error whatever happened and then taking that message all right it seems like with X as well or with this information I can't get the error to catch so if I go to the service and I looked at a stack or flow of course so if I go to combat service it doesn't catch it automatically if something happens from here so what would really happen is I would have to create my own filter and then use it so I could go ahead here and say something like validation dot filter.ts this is the code that's going to go there so I'm also going to run this by lint so everything looks good so you can just have a look at the code and see what is happening right here so we have a validation error which is essentially error.balization error so we are using this Mongoose error that we are catching and when that happens we just return a 400 and then we have some error so you can just have a look at this and try to copy it as well I'm going to also create a gist and put that in the description so you can also work with that now that we have this uh validation error created I'm gonna actually have to use this inside my main TS so here validation pipe we should also have app dot use Global uh filters and here you can say new validation error filter and here you can import it from filter and I think this should be fine so now if I have this running let's try the HTTP again and see if now it caches it so if I send a request does a server crash or what happens so in this case we have the object ID that still says internal server error that's a bit annoying to be honest Maybe if I look at the validation filter we are using that inside Global filters uh-huh I think this should be able to catch from an SGS or the validation error no this should be the error I think and the exception itself look at that because I'm too skeptical now so if I go to comments try this again now I get it right so the problem there was so even some modification from a stack Overflow looks like so what happened was in the validation filter we should not only be looking into validation errors but actually just the error itself so in here we have validation error that points to Mongoose and then we can catch all the errors from itself so look at this when the error happens it Returns the status code it also sends back what is the problem so cast object ID failed for Value so it senses the error which is supposed to be handled properly and that's good so now if I have a valid ID it should work fine if I don't have a valid ID it should still not crash my server so that's good so I'm gonna quickly fix this again so if I do this send request I get the data when the object ID is there if I have the parent ID being empty completely if I send this you can see that then it returns everything completely and if I don't have parent ID I have parent idx send the request then it returns everything as well so if the parent ID is a particular object ID only then I get the right data which is just one item in the array and it works so our back end is actually done I don't think there's anything that I'm missing to be honest we could have like find one by ID and whatnot but I think our application basically uses these endpoints get all the comments or get all the top level comments get nested comments create a particular comment and create a particular user and then just the ability of creating a particular comment by a user in this case so we are done with our backend the next thing would be front end but obviously this this took a lot of time and it was totally worth it because now we have an API that works so let's get to the front end part all right it's actually time to work on the front end so now that we have our apis ready what I'm going to do is I'm going to start a new project with angular and then we are going to take it from there so let's open the vs code and inside here I can go to a terminal and then I can do CD dot dot and in here now I can say something like NG new so you can check if you have angular installed globally and you can then do NG version to make sure that you have the latest version of angular available and it shows you what version that is if you don't have the latest version then you can essentially install it by running npm install Dash G angular tli and I'm going to show you that in a bit so here you can see I have the 16.0 next so this is not the latest version so what you can do here you can do npm install Dash G and in here you can say angular CLI so this installs the latest version in our system and then we should be able to use NG command we could have done the same with an scli as well so I'm just showing you two different things to essentially do the same thing so if I did not have this installed globally I would run a different command so I would rather run npx and then angular CLI and then I would say new and then I can give it a name so for example my app and then this would essentially run it from npx and not the globally installed package so this is one way of just creating an app without having to install angular CLI globally but this would obviously ask us hey do we want to have for example angular routing so you can see the command runs right scss blah blah blah it was dry run so nothing happened but since I have the angular CLI package installed in my machine now I can just run NG so instead of running npx angular CLI I can now run NG new my app and that essentially does exactly the same so it would ask us questions and then yeah you can see the same thing essentially so what I want to do is to run NG new and then I'm going to name the application threads app and then I will use routing and then I'll also use Standalone so previously angular used to have NG modules but it was a bit of learning curve but what we want to have is as simple components as possible so we're gonna go with Standalone so I want routing in my application I want Standalone components then I can also Define the style so I'm going to say style scss and this should be good maybe it will ask us more question but we'll see so threats app is going to be the name so now you can see that this generates the application you can see here that the Styles use scss so we are going to use SAS then we have the app component and you don't see any app module file so that's why that's because we are using the Standalone apis now and our components or creating components would be like super easy and this is going to be very fun because there are no modules hopefully so now I'm just going to wait for the packages to be installed and then we are going to spin up the app or we're just going to open that in vs code awesome so now that everything is installed I'm gonna open this into vs code so I can say threads app and this should spin up on new window for us here we go and now we can work within this so you can open Terminal from here terminal new terminal and in here we are going to serve the application so let's quickly do that all right we can do npm or Run start or npm start essentially or we can just run NG serve that also does the same thing so I'm going to just run nbm start because the packages are already has a start script which runs NGA serve so we should be doing the same thing all right so once the app runs we are going to be able to see the app but before we see it in the browser let's have a look at the source folder The Source folder has the index HTML which is the starting point of when your browser loads the application this is what it sees then you have the top level component called app or we call it the root component or the app component we basically call it app component and then this approot essentially comes from the app component itself so if I search this you can see that it is defined or it's used in index HTML but it's defined in app component yes so going back to the folder structure if I go inside the app this is all the code so this is the starting code you can say and here we have the app components template so this is all the HTML that we'll see in a bit all the styles that could go here the test file and then we have the component itself where we can Define the variables and then the methods and then also how this component would be used so you can see the app Root is the selector here so if I have the selector approved I can essentially go here and then use it as many times as uh I want and then we have the Standalone set to True some uh some imports right here and that's pretty much it we also have a config we don't need to go into here now we'll go there when the time comes and then we have the route so the application right now does not have any routes but we have the initial configuration set just with an empty array having said that let's actually have a look at the app so I'm going to go to localhost 42 000. or actually do this so this is how the app looks when we serve this you can see threads app is running so all of this is actually coming from the angular uh or from the app component what's going to be great is if you can install the angular extension for Chrome so you can say angular Dev tools Chrome and then you can find this extension super handy super nice you can see 200 000 users plus so essentially if I go and inspect this I should be able to see the angular tab here and that makes it much easier for us to work with the with the app so you can see all right I have the app Root component I have a div here I have the router Outlet blah blah blah it's really easy to work with all of this but let's go back to what we should actually build so essentially this is what we want to have so we want to have a list of comments and then we should have a write comment component and then we have more things as well so what we're going to start with is having a page that we can work with so what I'm going to do is that I'm going to actually remove whatever you see right here with a page so let's go back to the code so in here I want to create a particular page so what I'm going to do is I'm going to open a new terminal and in here I can quickly say hey NG generate component ngdc and then I'm going to call this page home and that's pretty much it I do want to make sure that this is a standalone component so we are not working with any modules and then I can just run it and this will create a home folder inside here with the configuration so here you can see now we have got a home folder so I what I love about angular is that we have the ability to create the files as we have in sjs so I have a CLI that can generate files for us and I don't have to do it myself so now that we have this home component here if you go to the typescript file you can see the selector is app home which means if I use this as HTML tag anywhere it should essentially render the template home works at that place so if I go to app component what I'm going to do actually is I'm going to remove everything apart from the router Outlet so here we have the router Outlet as you can see so I'm going to actually remove everything apart from it so let's go ahead and remove everything completely from here now what I'm gonna do is I'm gonna be configuring this home component to be used so if I look at this component and if this is supposed to be a page or a route in our application all we need to do is to go to app routes TS and in here we can define a route object or route configuration object so here we have or a route essentially so here we Define the path what should be the path so we want this to trigger when someone goes to our application and on the root of it so we'll just keep this one empty and then here we can say hey you need to load component and we are using this because this is a standalone component so you can say load component is going to be a function that Imports a file so you import the file home slash home component and in here you can say dot then so once we get this file we want the home component if I can just type it right home component class and this essentially makes No it should be m.horn component and we should not import anything so here we are saying hey angular you have to load this component lazily which means only if I go to this page then I should be able to load this component and if I go to let's say an about page then this file will never be loaded which means that the user will get things faster and no unnecessary bundles so now that I have this mentioned here that means that if I go to my app now it should essentially work so here you can see that the app already says homeworks so if I refresh you can see that this is working flawlessly right here what I want to do is also to use Tailwind CSS in my front end so what I'm going to do is I'm going to go to Tailwind CSS guides angular and in here this is what I need to do so I'm going to install this package do npx Tailwind CSS in it set this one set the Styles and I should be good so I'm going to quickly copy this command in the terminal I'm gonna go and see all right we are using package log Json so I can run npm install Dash detail when CSS post CSS Auto prefixer in the thread step so this is obviously the front-end app that we are talking about and then I'm gonna copy the next command which is npx Tailwind CSS in it so then I can do this and this will generate a Tailwind config.js for us which is a configuration file so this is Tailwind config.js here we need to tell the Tailwind CSS where would we use the CSS or where would we use the Tailwind Styles so when you define Tailwind CSS or when you use statement CSS you essentially use them in your HTML as classes so that means any HTML inside this app folder or inside this Source should actually have Tailwind CSS applied to it so that's the next step and here I can say inside the source folder any HTML or typescript file please use Tailwind CSS on top of it so I'm going to go here and then in here I can go to the Tailwind config and here I'm just going to replace the content I'm also going to add the scss file here because I want to be able to apply Styles in my SAS files as well so not just TS but HTML as CSS and TS altogether now that we have this the next step is to copy these into the Styles scss file so I'm gonna go to my Styles CSS file right here and then here I can just paste this super cool now that we have this I can just restart my angular server and I think it should be good and we should be able to use table and CSS out of the box and we're gonna see that working uh as well so in here it says no utility classes uh detected in your source files because we are not using a Tailwind at at the moment or not using any classes so if I go to home and inside here if I change this to uh or if I just add a class here let's say text 5xl then this should become 5xl so if I go to my app now you can see that this increased itself to 5xl so if I go here inspect this and here you can see the text 5 XL has the style font size 3 REM so if I remove this class you can see that this also don't apply so my tailbone CSS is working perfectly fine now what I also want you to do is to install the Tailwind CSS uh extension so Tailwind CSS you're gonna see that it is by Tailwind labs and what this extension allows us to do is exactly what I did so if I want to add a background I can do background red and you see all of this so this intellisense or this color or the names of these classes are coming from the extension so I can now easily see all right this is how the color looks cool I'm going to use this one and then text white and then I'm gonna go here and then you see that the styles are working perfectly fine so that's how we configured our tailor in CSS now that we have this let's actually start styling or start creating some templates so in here I'm gonna change the app component a bit so for example here what I want to do is I want to have inside my app component I want to have let's say a div inside which we would have a class we'll call it Min H screen so we want it to be at least the height of the screen and then what we want to also have is flags and flex column so we are going to have two things we are going to have a header and then inside that we are also going to have the main so inside main we are going to then have the router Outlet so I'm doing this because if we end up having more pages all of them would have this header so if I go here I can now say nav and inside nav we can have uh just the logo or something or it's not in nav but inside header we could have a logo uh file or blooper so inside header we can have a logo so we can have a span here and the span essentially says threads in this case and now I can save this if I go to my app you can see the threads right here so what I do want to do is to go to my home component and remove these classes for now I'm going to change that later on but in my app component I want this header to look uh like something so I'm going to say class I'm going to say the height of this is going to be 12 which means it's going to be about 48 pixels then I want this to be BG indigo 700 the text is going to be white so if I look at this now this is how it looks now I want these to be aligned properly let's also add some list items so I don't know maybe we can say home here and then inside we can also have another Ally uh that could have an about so here you can see the home link right here and then also the other should be about but you don't see it at the moment we'll fix that so this should essentially be flex and now you see threads home and about so what I want to do is to have this threads on the left and this home and about here on the right side so what I can do here is I can say justify between and now you see them apart I want some padding inside from left and right so I'm going to say p x is going to be three so you see there's some space or some padding right here and right here as well which is equal from both sides and now that I have this I can go ahead and install the nav or the UL essentially so I can say class this is gonna be a flex with a gap of four so this is a flex of Gap 4 so if I inspect this you should be able to see when I Mouse over UL you see that this purple area that shows here you can see that it that's the Gap that we've got and then this is flex now you can also play around with this as well like I really like this whole part where blooper I really like this feature in Chrome that can allow us to do crazy stuff like this one this one flex and space around so not really helping at the moment but I could change the direction to column as you can see to row row reverse um I can also see if the justify content can be centered in this case or flex and this was Flex end this is a space between this is a space around this is Center so I could actually play around with that quite a lot this is Center as well no stretch Center oh this looks weird so I'm gonna just refresh it but I really like the tool so now that we have the the UL set what I want to do is inside this just uh inside this header Flags I also want to do items Center so everything is in the center and that's pretty much it that's essentially what I want so now that we have the header there you can see the home works right here you can also notice that there's a difference between the spacing right here and right here because this header has a class of PX3 but there's no class on Main so I'm gonna actually do that I'm gonna say class PX3 and now you see that both of them have the same uh spacing from left and also actually from the right so this is if you see both of them both of them have the same spacing all right now that we have this we are also going to give a py of four so we have some padding from Top in this case now we can add more components more images so I can quickly go here and say NG generate component about and then in here I can say Standalone so it this will generate the about page for us and now what I can do is in the routes in here I can say hey I'm gonna have another route we'll call it about and then load component and here we have a function that Imports the file of about about component dot then module and here we can say and when when I say module it's actually the file itself being a module so it's either the es6 modules or common JS modules but here it's es6 so I can say m dot about component which means now I have my about component set on the about route now if I go here and if I try to go to about manually you see that about works here so we have both of them working like this okay we also want these links to work so what I'm going to do is inside the app component HTML with these allies I'm gonna also use the router link so this is supposed to be slash and this is supposed to be about and this component already uses router outlet but the links may not work because if I go to click here you can see nothing really happens that's because we are only using router outlet from angular routers instead of that if we go and use router module and use it right here as well then we should be able to have the links as well so if I go to home you can see it goes to home if I go to about they work now so using router module instead of router Outlet now I also want to style these L links so I'm going to say class and we can say this should be cursor or actually this should have an A inside which should have a router link to be honest if you want uh actual semantic uh HTML so in here we have the LI which should have an A and then this should be routing about and I have the about inside this so this is how it should look Li a and then the router links so I don't have to style it manually now you could go ahead and say hey if I Mouse over on this it shows us a particular style so maybe so right now this looks um how do we do it so we can do hover text underline or hover underline and if I do this you can see right this maybe this is okay maybe this is not okay I don't really know let's also do hover underline offset 2 so the underline is maybe a bit down um poor yeah this one is much better so I'm gonna copy these Styles and then I'll go here so this is how it works and then also you can obviously style if the particular route is the one we are at you can underline it automatically so in this particular case you might want to say hey if this is router link active then you could have your particular class so you could say class Dot um how do we go about it maybe underline so underline is going to be applied when we have the router link active or actually we could do it like this so router link active underline so if I do this you can see automatically it's underlined okay but you could also say underline and underline offset 4 together and I think that should work just fine yeah so you can see that now the active One automatically is highlighted if I go to about it doesn't work because I have not done it for for the about one so it only works on the slash so routerlink active and then I can also put it on this one and let's see so home about what does this highlight this guy that's a bit that's a bit interesting so if it's active it applies it to do what does this apply to the page that oh but is it because hmm because this is ah okay because this is root so it's gonna do that yeah then we have a problem we can't really have something on the root so then I would have to go here and say ah this is gonna be home and then there should be a route on the default one and this should have a path of empty string redirect to home and then the path match should be pulled so now if I go to home and about on about it it does for both of them but not there I'm a bit confused I think the reason for that is if I go to app component I think that's because of that yeah so if I go to home now you can see that it highlights on home if I go to about now it highlights or about yay problem solves yeah fixing things in production or in the video itself cool now that we have this working let's actually talk about the component itself so what we're gonna do is we're gonna go quickly to the comment component so let's quickly go here say git or not get but ngg component we're going to have a components folder inside that we are going to call it comment and can we name this better no let's go with comment in this case this is going to be a standalone component so we can have uh this used in our components as well and now that we have this component created we can start using this comment component inside the home so if I go to home HTML I can go here and start saying if you go to the comment component file the typescript file you can see this is the selector so since this is not a page but rather a component that is supposed to be used inside home I can go here and say hey in this particular case I'm going to have a section which will contain all the components and I should have an app component you can see that this becomes red that is because our home component is a standalone component the comment component is also a standalone component so they don't know each other and we have to sort of establish a relationship between them or let them know each other and the way you do that is that the component that I have to use should be imported in the parent component where I am using it so the home component has to import the comment component like this once you do so you can see the HTML is happy we are all happy and if I go here you can see comment works so if I go to the angular extension you can see that I have the app Root which has some allies up top but then I have app Home inside which we have the comment so here you can see the home component has the common component cool I can have this comment multiple times and now we have multiple comments cool and this is what we sort of Envision happening at the end so multiple common components now that we have this we could start from scratch with our sort of UI but ideally we should have a component that has the username it has the description or the text itself and then three different buttons right so I could also write it from scratch but I could also just copy paste stuff from the internet so I can go to I think it's called hyper UI Tailwind something something uh I think it's this one and I remember having some cool cards from here so maybe card Block cards uh product cards collection cards let's have a look product cards and I think this was the one so I do remember there was a cool card something like this maybe I don't know you can see that it's a it's really nice overall so I could just copy the Tailwind CSS from here there was also one more card that if I can find it I'll I'll show you that hey this is the one product card um no let's see hmm I think it is this one let's see card card card product card cards this is the one so yeah we have this one and I think the one I really wanted to work with was this guy so we can either go with this one that has a title some tags and then you can have some buttons from there and then you can make it work so maybe let's go with that so I'm gonna copy this one so if I go to view this is how it looks like overall so I'm gonna copy this and then I'm gonna go into comments component just paste it and now if I go to my app you can see we have a bunch of cards that you can see right there so you can actually work with your user image if you want it but I'm gonna remove a bunch of things and you can you can look into the Tailwind CSS part itself but I'm gonna focus more on the business logic here I'm gonna also talk about the Styles here and there but not primarily focusing on that okay so now that we have the common component here let's actually remove the things that we don't really want so in here we want the username we don't want the Buy in this case so let's find that so by John Doe we don't really want it cool and now if I see it again you can see that it's gone I don't want the image itself as well so I'm gonna actually remove that as well no image then we have some text I'm gonna still keep it then you have this um I don't want this maximum bit to force the edge so I'm going to actually expand it just like this then we have the date that this was published how much time it takes so instead of having that date stuff I'm gonna actually have the actions or the buttons so what I'm going to do is this DL that we have right now we are going to have a bunch of buttons so this Flex should essentially be also items end or no maybe justify end justify end so these are towards the end so I I want the buttons to be right here and then I can have those buttons right right here so in this case I want three different buttons so let's have a button that says Reply we also should have another button that says um likes or 12 likes something and then we have another button that says uh expand or you can also say collapse in in other cases so in here this is how it should look we're going to style the buttons of course but let's take some inspiration from here so these are the buttons now I can go to the UI kit again and see the buttons itself where are the buttons where the buttons application UI marketing buttons so these are sort of the buttons that I could go with let's have a look at the buttons itself so do we have any other oh but there we go so these buttons the action buttons this one we got the icon buttons as well but what are the icons how do you use the icons are those svgs yes those are SVG icons okay maybe not those um let's see download oh I like these ones but I I like these ones more for some reason find out more nope nope nope maybe go with this one this one so view we have the download button let's actually copy this guy let's change this and say this is reply and then let's have a look at the UI so in here now you can see the reply button we have a bunch of likes and then we have expand or collapse so what I'm going to do is keep the reply right here let's actually use the one that was a bit wide uh uh and not this one few nope I'm gonna remove that this guy this guy so the second one so I'm gonna keep that one let's replace this and this is the reply button yeah I think I like this one what I don't like is it's a bit too thick so to say so py could be 1.5 and PX should be four yeah I think this is much nicer maybe even py1 yeah I think this is much nicer awesome so now that we have this button here we can talk about the expand and collapse you can use something called hero icons and you have Chevrons that you can use so you have some svgs that you can work with so we're going to keep it simple so what we are going to do is we're gonna go into copying the Chevron SVG so Chevron down SVG and then what I'm gonna do is inside the button I'm just gonna copy and paste this SVG so now here if I go here you can see the button and the SVG right here okay now this is supposed to be the the expanding or expanding SVG and we also want the collapsible as well but we can be smart and we can sort of just style it or translate and rotate it when it's expanded or collapsed we're gonna have a look at that in just a bit so now that we have the reply button and this one let's talk about expand and collapse so we're going to go and say hey is expanded it's going to be a Boolean that's going to be false then you can have toggle expanded and then that's going to be a function so what we do here is that we say this dot is expanded equals not this dot is expanded so whatever it is we flip it and then we can use this variable to actually dictate if this should show this or the other way around so in here what I'm going to do is I'm going to use a class and inside here I'm going to also use class Dot and then we can have a rotation so I think it's called rotate uh 180 so we want the rotate 180 to be applied when when is expanded right so this one so I'm going to remove the default rotate class and now you can see that in this one it right now already says that this is or blooper so here if I go here you can see that this shows normally but what I want to do is to be also able to click this button to toggle it so toggle expanded and now if I try to click this you can see that it sort of flips itself and the reason that this is happening is do we have uh because of this top level anchor tag so we could instead of having that we can say article instead of an anchor tag and remove the href so we don't want that and now you can see if I click this looks nice right but I want this to be animated as well so I can go here and say hey this button is gonna have a trans No it should have a duration of 200 so it automatically applies the CSS transition so now you see it just moves cool super nice right now that we have this let's also talk about uh the reply and the other thing as well but I think overall this looks good as a component I also want some Shadow on this on all of this and I also want this to not take the entire bit in this case so I think I'm gonna go with something like if I go to home I'm going to say this section is going to have a class max width um should be MD and this sort of limits it to be this big but then I also want this to have MX Auto so this is centered automatically so now you can see that now for each comment I want each comment to have a bit of Shadow so here we can say shadow MD or maybe Shadow LG let's see how this looks so this does have a shadow it's not as prominent but you can see it is still so maybe go with MD so this is how it looks now and I also want some gap between them I can also have some border as well so border I don't know and Border slate um oh we already have a border what is the border right there is it transparent it's gray hundred no maybe we go with slate and then we go with this so this has some border now this is too much maybe we go with gray but then we go with 200 so we have some sort of Border yeah and then I also want to go to home component I have some gap between those so what I'm gonna do is in here I'm gonna also say this is flex Flex column and then I also want a gap of 4 which means 16 pixel and now you can see we have some gap between those as well this looks really nice now so now that we have the reply and the likes and the expand and collapse let's talk about how can we have this create component as well so so far I think what I also want to do is to be able to expand this component so for now let's say we have in this component we do have something that we can show here so maybe we can say something like I don't know NG container so this one shows or we can have something simple here so maybe we can say something like a section and here we can say nested components maybe so nested components or nested comments and in here we can say this shows nested comments right just for the sake of placeholder so here I could say text Excel and in here this now this is being shown everywhere I don't want this section to be outside the article so I want this to be inside the article so it shows something like this so this shows nested comment but this should only be shown if I am either replying or expanded so this should be is expanded for sure so now if I expand this you can see this coming or going okay so this is perfect now I also want this to also have a variable called is replying and is replying would be false by default we'll also have a toggle for is replying and I'm going to show you what is the difference between those two so toggle replying and in here we say is replying to is replying now when we have replying we want to show oh why did that happen because it's a link let me quickly fix this anchor tag right here so this should be a button and there shouldn't be no href cool so now if I click reply you you see nothing happens but if I go to my extension you should be able to see this so in here x expanded is false if I click this nothing happens because we don't have any click Handler if I click expanded you can see this changes or the x is expanded changes right there so I want this button to be clickable and then when we click it it toggles the replying so now if I try to click reply if I'm on the first comment you can see that that changes as well so I want the content or the comments to be shown when if is is expanded and when it's replying so what I'm going to actually do is that if I click the reply button then I'm also going to see that if I have changed that is replying to true then I also expand it as well true as well okay so if I go here I'm gonna say hey I can expand collapse but if I click reply it also expands if I click reply again what would happen it would still turn off something but the expanded Still Remains there so this is what I want because what I want to have is in the comment here and this is just me being really nitpicky about the user experience because in here I could have a section that says is replying and here I can say this is where you reply right so the ux that I'm looking for right now is if I expand it it just showed shows nested comments but if I hit reply it not only shows where you reply but also nested comments as well okay because that is what usually happens on Facebook as well if I toggle this back then you still keep seeing the nested comments because you have already loaded them so the only way to get rid of this is to go here now if it's not expanded it's it expands if I click or if I have reply open then I can not be or Looper if I click this reply to go away then you can see this still keeps expanded but if this is expanded and I open reply then obviously the reply is there as well but even in this case I can sort of collapse this but the form would still be shown where I can reply which is actually this form okay so I hope this makes sense now that we have got the logic or the ux here what we would want to do is we want to be able to have the right common component so we're gonna have a look at that just now so what I'm going to do is I'm gonna actually go and say hey I have uh an ngg component in the components folder and this should be right common component now you could call it write comment create comment it's up to you so maybe we could say comment form Maybe component I think that's rather a good one so you can have a form that uh that adds a comment and then you have in Standalone so this will create the comment form and I want this comment form to be here that says where you reply so I want to go to components comment form and in here this is the selector app comment form so I can go here and say hey app comment form this is going to be red because the comment component does not know about the comment form so I'm going to go to comment component typescript and in the Imports I'm going to import the comment form component and now typescript is Happy HTML is happy and now I can go to my app I can go to threads app click reply and here you see comment form Works yay so I'm actually using the actual comment form which makes sense and I also have this here as well so all the components now has the same thing now what I want to do is I want to be able to also render the nested comments so in apart from this one I also would just do something like a comment so let's say we have further nested comments like this and this makes it so so much interesting so if I open reply you not only see the comment form but you also see now the nested comments right and all of the nested comments that you see if I click reply you see more nested comments so it's sort of a common section if you will cool now that we have this let's actually remove this these texts so nested comments no we don't want that we can also see the comment form so if so if I now go to my app reply you can see comment form works and then I have the nested comments which are a bit inside there right so now that we have this let's actually implement the comment form so what does that have if I go to my super cool drawing you can see that it's just a component that has a button and it takes input so we go to the comment form component and we start styling it so we first start with the form it has to be a form of course then we would have to have um you can have a label or you can just use a text area to be honest so I can go with a text area I'm going to keep the columns to I don't know I don't want the columns I I want three rows and I also want a so if I just render this let's see what happens so the app right now if I reply you can see this is the text box but you see that this is not expanding to its full width so we're going to fix that so in our code we actually go here and say hey class is going to be worth full we're gonna have an outline or we can yeah we could have an outline or actually a border border slate um 900 so if I reply now you can see this right here now I'm gonna add more stuff to it so also making make it a bit rounded and have a placeholder right here so we go ahead and say hey in this particular case we should have I'm gonna have an ID right here just yet so in here we can say hey apart from this we have rounded MD we should also have a placeholder that says right something and I think that should be it let's see so now if I click reply you can see write something the text size should be a bit smaller so I'm gonna go here and look at this so this is the form and the styles are probably coming from if I go to the comment component here we had the comments but here we have text Excel so I'm going to actually remove this and have the app comment form directly now I can go here and see this has write something it should have a bit of padding so let's also add that so I'm going to go to my comment form component and inside here we should have a padding so we can have something like AP X of 3 py 1.5 and I think that should be good so now if I reply you can see write something I should be able to write something and then I have the nested list right here now what I also want to do is in my in my comment component where we have this when we show the replies I want this comment form to have some sort of margin so in this case I would have I would not enable nested comments but rather say comment form or I don't even need that class to be honest so I'm going to say m y equals or my4 which means that now if I open this you see this nice little gap between these two because now we have a margin bottom of uh 16 pixels so super cool now that we have the margin we have the nested Commerce I can open that and that also has nested right something and you go on and on from there I don't know how crazy this can be I think I would say super crazy and you see all the nested things that happening Ah that's what we wanted so we talked about replies we talked about expanding and collapsing and showing the data in that particular case now what we want to do is to be able to write something but we'll get to the functionality later on so we want things to be really simple we already have an API so if I go here you see the comments we already have them by default we get an array which has the text which has the user okay and that also has then the Ida blah blah blah but what we are more interested in right now is showing the username here and then to be able to show the text that's the first thing that we want so what we're gonna do is we're gonna start working on the API and it's at least try to render the comments uh on the home page so on the home page I have hard coded three comments I want real comments right here so what I'm going to do is I'm gonna go and say NG generate service I'm going to have a Services folder and then I'm gonna have a comment service right there so let's actually have that real quick and inside that I want to use the HTTP um calls so I'm going to go there and inside here I want to be able to use HTTP for that I need to go to my main TS and not main TS but I think it was config app config and here you can see that for this application since this is a standalone application I'm using this config with provide router what I want is something like provide HTTP client but you have to import it the first time so in here I'm gonna say angular common HTTP and inside here we have something called provide HTTP client so I'm going to use this but I'm also going to use this with fetch with fetch means that it's going to use fetch out of the box for the HTTP calls the fetch which is now built in into the modern browser so it's much better to use this one now so then I can go here and say HTTP equals inject and then I can now do HTTP not HTTP or it should be HTTP client oh not module but HTTP client service now I can create a function called get comments and if you remember from our API we had two different apis one where we don't use the parent ID but it's the same URL localhost 3000 comments and the other one is localhost 3000 comments where we provide a parent ID and then it just Returns the nested comments in this case so what I want to do here is to be able to make the HTTP call against those routes so what you can do is you can manage your environment variables either via EnV variables or something like that but what I'm going to do is I'm actually going to quickly um so if I go to the angularjson uh replace no we don't have that configuration here okay so let's do it the simpler way in this case so what I want to do in here is to have for example an environment dot TS file and here I can Define const environment equals and in here I'm gonna say API base URL which is HTTP localhost 3000 so this is the endpoints beginning right so I'm going to actually use this environment dot uh base URL in a lot of places so export cons environment and now what I can do here is I can say hey this dot HTTP dot get and in here I'm gonna create a template literal and in here we can say environment dot API based URL slash and here we have comments now if I have provided parent ID to this function which is supposed to be a string then what would happen is and this is by default empty so if that's the case then what I would do is I would construct the URL first something like uh let Ur if the parent ID has been provided then I would just add to this so URL plus equals question mark parent ID equals and we can also make this a template editorial to be honest and here we say the the variable parent ID and when that happens then I can just use this URL and that's pretty much it now I want to know what a comment looks like so I need to have some interfaces that I can work with so I'm going to create a new folder and you can use interfaces or types it's up to you and here I'm gonna say comment dot interface dot TS and then I can say interface or export interface comment and in here we Define so the text has to be a string then we also have um the parent which can be either comment or null then we have user we are going to define the user cell but it's either going to B so let's also Define user so user dot interface dot TS a user export interface user should have an ID which is the generated ID of type string and just the name that is string so this is the user right now I can say this is going to be I the off type user and then what we want to have is the text is string the parent is there as well and then the ID of the comment so the ID is going to be string now then more properties of course if you look at this right here so it has a user it has a patent created that updated that we don't really care about those so I think these are enough now that we have defined this I can actually say that if I make this call I'm gonna expect it to return a comment array so the comment make sure to import it from the right interface file and then what I can do is I can say return this dot http.get comment URL that's pretty much it now this means that this get comments returns an observable of type comments which makes sense so far now that I have this what I want to do is when I have my home component being rendered I want to call this get comments to get the information and then render it so what I can do here is I can say implements and here I can say on init and here we can say NG on in it so in here I want to import the comment service so I can say comment service equals inject comment service and then here I can say now to get the comments so let's create a function get comments which essentially should do this it would say this dot comment service dot get comments then I'm not passing any parent ID because this is top level comments then I do a subscribe and then inside here we can either just say Observer next so in this case I could say hey go ahead and try to Bing the comments we know what get comments returns is an array of comments so if I Mouse over this this is an array of comments and then I need to assign this to something that I can use to render so in this angular application since we have version 16 I'm going to use something called signals which is something new in angular but it's super nice and super handy so you can import signal from angular core and that means that I can now have something reactive in my component so I'm going to say comments is gonna be off type signal and the signal of type comment array and then I initialize this with nmd array now when I get the comments from the back end let's call this comms for the sake of this change or let's call this command is fine now we can do this dot comments dot set and then I can assign the comments that I've gotten from my backend so in this case what does this cry argumentative competition yeah oh okay so this this is of type signal uh [Music] is it because I have imported the comment from some weird place wait a second get comments it is imported from the interface yes that makes sense but in my home component if I import it from somewhere oh because I have not imported comment so this should be comment from the interface you need to make sure that you're importing this because I think there's something default comment this is the mdn comment so we don't want that we want it from the interface that should be happy so now that we have this code here let's call this when the component initiates so we get the comments we assign it to the comments uh signal so this dot get comments this should this function should trigger this this should get the comments from the back end and then set it on the UI and now all we need to do is to use the signal which means here instead of rendering these three hard-coded comments I can actually go and see so if I go to threads app this is how it looks if I go to network you see that I make the call I don't get any response it's all red and that is because we are not allowed from our front end to make this call to the back end because the back end doesn't allow cross origin resource sharing or access so why is that because my front end is on 4200 local is 4200 and my backend is on localhost 3000. this actually sort of replicates something being on google.com versus facebook.com because those are two different domains in this case it's different ports so how do we solve this we solve this by going to our backend and if you go to the main TS file and here you can say something like app dot enable course and that's pretty much it this takes a configuration object which is options and in here you can actually provide which front end or which URLs can access this application so we can explicitly say localhost 4200 can access this backend when you deploy this to production you're obviously going to say these domains can access my backend apis otherwise it's public everyone can use it so for the sake of Simplicity I'm just going to put it there as this so let's save this let's go to a browser and if I refresh now you can see it works got 200 yay and now if I see the data I get three different comments that I can render so these are three at the moment if I go and remove one of them now I see two inside of this two I'm gonna now render based on the array so I'm going to say ng4 let comment of comments now this comments is a signal so I can't just put it like this I would have to use it with these parentheses so we can get the value of the comments now if I refresh you see we got three comments awesome now what we want to do is to be able to actually have the data of of these comments reflecting here so what you can do is we are going to pass the comment this guy as an attribute or input so I can say comment equals to comment which means that this common component now has to have an input so I'm going to go to components comment the typescript file and in here I'm going to add a new input I'm going to call it comment this is going to be of type comment from the interface file and that should be it an import input from angular core so now we're receiving a comment as an input if you look at the home component we pass each comment to the common component now which means that if I refresh this go to my angular app and make sure to save all the files then I can actually see that app home has a bunch of comments and each comment now has the relevant common data right here so this one is the first comment this one is the second comment and this one is the third comment so all of the actual data is passed to each component the only thing is we are not rendering the data so now we can go to our comment component and inside here you see we already have the comment here so we can essentially go here and replace the top level text which is this guy with the username so in here I can say something like uh comment dot user Dot and in here we can say name there you go you got more sin mohsin mohsin all over the place every comment is by motion now what I also want to do is to show the text so here I'm gonna say comment oops comment dot text and here you see first comment second comment third comment yay we should also be able to have likes I don't know if we added likes or not no we did not but we can obviously modify and have likes in our database as well we'll keep it like that for now you can implement the likes feature yourself now that we have this let's talk about how do we render nested comments Okay so whenever we are going to expand this component we should be rendering or we should be getting the nested components and then using them so how do we do that we will first remove the hard-coded comments which are this one I'm just gonna comment them out so many comment words in my video and now if I click reply or expand collapse you can see that the first component just shows the form nothing else at the moment so whenever we are expanding then we should definitely see what nested comments we have and then import them and then render them that's the idea so whenever this is expanded that's when we should essentially do it now I could use a regular variable here or use signal so I can automatically do something when this is expanded or when this changes so instead of regular inputs what I'm going to do is I'm going to make them signals so I can go ahead and here I can say signal and this is supposed to come from the signal or the angle core package so this is going to be a signal or false this is also going to be a signal of false which changes a lot of thing because now we can't really do this we have to change the code a bit so instead of assigning it like this we now would have to do something like this so This dot replying Dot and here we have to say set the reverse of this dot replying notice that when I get the value I use parenthesis because that's how signal work if you want to set a value in the signal you use the set method if you want to get a value you use the getter function okay just like this now I can check the value just like this if the is replying is now true I want to set the expanded true true okay now toggle expanded what should this do the same thing as this guy so I'm going to quickly copy this is expanded is expanded so I toggle the expanded I toggle the replying okay now I can save this but the HTML would have to change as well because in here wherever I'm using is expanded I need to use the getter function and same goes with this replying now if I save this and run it let's see what happens so if I go here click reply you can see that now we toggle it so everything is working if I expand or collapse that also should work so if I expand collapse that should work but it doesn't at the moment or at least doesn't show the UI so if I go here you can see that is expand is a bound signal that you can see right here and we have the value that we can also return in this case so if I go here is it possible for me to look at the extension or or the value of it maybe not but we will look at it in a bit I know that when it's expanded I need to show the UI being toggled so I can actually do it like this as well for example if I now expand I see the comments collapse so this thing toggle is working but this this icon doesn't rotate and that is because in here we have is expanded without calling the getter method now if I do this you can see now it works with the animations as well so everything there is working now what this allows us to do the the signal thingy is I can use an effect looking at the expanded whenever this expands to true whether if I toggle replying or whether I toggle expand it it doesn't matter whenever this expanded changes I get the new comments so I can say something like get nested or we can say nested comments effect equals you use an effect from angular core create a function and then you just check if this dot is expanded if it's true then you want to do this dot get nested comments you could either do that or you could directly use the comment service so here I can say hey I have for example here the comment service equals inject comment service nope like this comment service and in here I can now say this dot comment service dot get comments and now I have to pass the parent ID because I'm getting nested comments so I can say comment dot parent Dot ID and this is going to be this dot comment now the thing is we would or not the comment.parent ID that's wrong it should be comment.id so I'm passing the current comments ID as the parent ID so it can fetch all the nested comments based on this as a parent and now I can do something like subscribe you get the comment or comments back and once you get those then you should have unnested comments array as well so I'm going to create a nested comments array so nested comments equals we have a signal in the signal we would have the comment type from the interface so comment array and in here we have the default empty array in this case when we have this then we can do this oops nope what this dot nested comments dot set comments and now when we do this now I should have nested comments which means that now I can go to my UI and then instead of running all of this I can use nng4 so here I would say hey ng4 let oops let comment of nested comments it's a signal so using the getter method and then I need to pass each comment inside that so in this case and and this makes it a bit difficult because this is called command and then this is also called comment maybe call this nested comment all nested comments or end comment I'm not really sure maybe nested comment is better and here I can now say hey I'm gonna pass the nested comment as the comment input to the nested comments I see comment so many times all right so now Moment of Truth Network calls if I expand it yay you can see that we sent a call which is super nice so if I look at this you can see that there was an empty array returned so there are no comments to actually show in this case and we know that there's only one comment in the database that has nested comments so the first one doesn't have this the second one doesn't have this the third comment has a nested comment because here you can see it by parent ID and this nested comment was done by not motion but SN okay every other comment that we saw was done by motion so most in most in motion now if I expand this one you see the nested comment hey and here we also send an API call but we get an empty array in this case but the second one that will be sent where we send the parent ID of this guy then we got the actual response of this particular comment I'm a nested comment inside the third comment so now our API actually works really nice so we are able to at least render all those components or all the comments and also having the possibility of even replying and then getting it so as you know if I expand this I send the API call but even if I reply this is expended because of our code so even then it sends the API call so if I reply you can see that we send the API call in this particular case alrighty Google cool so I I think now we have a good idea of all right how this this thing is working let me also check one thing really quick so if I click reply reply da da the signal doesn't trigger any other API call so let's try this if I click reply this sends a network call if I hide it again nothing happens if I click reply again nothing happened because this is already expanded but if I close this now it's not expanded anymore and if I expand it again you see that this works so whenever the expanded is going to be true from false then it's going to retrigger it and that makes it much easier for us to work cool so I love it so far so good now let's talk about actually creating a particular comment for that we also need the user because if there's no user you can't really have um you can't really have a comment so how do we work with the user for the application for the Simplicity right now what we'll do is that when the app opens we're going to check if we have a user for this application for example if I'm using this application I'm gonna check hey am I already a user in the database in the database if that's not the case then we are just going to create the user on the Fly we are just gonna give them a random username at the moment you can Implement your own login you can also Implement a form that takes a username and whatnot I'm just gonna make it quick and I'll see hey if if this browser or if this local storage in in my app understands me as a user from the database then I'm good otherwise I'm just going to to create a user so for that we need a couple of things first of all we are going to create or I'm going to close all of this first let's talk about creating the user and then we are going to go to creating the comment so ngg service and we are going to say or ngts services and then we are going to call a user service so let's create that really quick so now that I have the user service right here let's talk about creating a user in this case so what I want to do is in this case I want to be able to say HTTP equals inject HTTP client and in here what I want to do is I want to say create user and what it requires basically is just the name because that's what we need the rest of the things are just just created by mongodb which is the ID so this should be this Dot http Dot post and in here we will create the URL so this is supposed to be environment dot API base URL slash users and the in the body all we need to send is essentially the name and that's pretty much it so once we do so we should have the API call working and I'm gonna return this what this API returns is essentially a user so let's import the user interface so we are sure that this has a right type so observable of user is what you're looking for now that we have this user type imported from interfaces and this API created let's now look at if the user needs to be created or not now when would you want to do it a good time to do this would be in the app component so I'm going to go to the app component and I'm going to say hey I have um I can do it on Constructor so Constructor so we only do this once per application so in this case I'm gonna say hey inject no not inject but user service equals inject user service and then what I'm going to do is I'm gonna check if there's a user if there's no user then I'm gonna just say or I'd create the user so let's have a couple of things first of all once we get the user I want to be able to save that user in local storage so I'm going to say save user to storage is supposed to be a function that would take a user of type user and then it would save this to local storage what is going to be the key where we will save this let's add that so local storage key is going to be threads user so in this case I'm going to say hey this Dot no local storage dot set item and then we'll say this dot local storage key and then json.stringify the user so we save the user to storage we can also say get user from Storage so get user from Storage this doesn't require a parameter and what we should have here is const user equals and in here we can say local storage dot get item head item no get item and then just the local storage key so this should give us a user now we either have this in the storage or not so we can return if the user from Storage exists then we do json.parse and then we parse the user otherwise we just return back null so it's either going to be null or a user now that we have these functions let's actually make them work so in this case what I'm going to do is I'm going to say if this dot user service dot get user from Storage so in here I'm gonna say hey const user equals this so this is gonna be either null or a user so get user from Storage should either return as user or null which means that if I hover over it it's either user or null so I'm going to save this and in here if I Mouse over the user it's either user or no so I'm going to say hey if this is null or if there's no user then we do this dot userservice.create user and we pass the name to this user right and we can use a random name to generate right here so what I'm going to do here is just for the sake of fun I would say that the random name that I want to generate would be something like this cons random name equals is going to be something like uh user underscore and we can have something between random number I mean to be honest I could just give this an ID but let's make this fun and we'll see something between math dot seal math dot random multiply by five thousand or four thousand and plus thousand so we are saying hey pick a number between thousand and four thousand and just add it right here and that's pretty much it so now that we have this we're gonna use this random name and pass it just like this and then we have a subscribe we get the user back and then we also do a console.log we can say user created and show the user on the log as well okay so this is the case if I save this I believe it's just gonna do it so let's quickly go to the network Tab and then I'm gonna just say um and once we have this user created then I also want to save it to the local storage so here I'm gonna say this dot userservice dot save user2 storage and the user goes to the storage so let's save this and try this out and I'm gonna also go to the app component let's see if I can go there and I would have to put a breakpoint there save cool so now it's going to go into Constructor and now let's debug as well so in this case it comes here it tries to get the things from Storage it tries to look at local storage get item this dot local storage key so local storage doesn't have thread user at the moment so it's gonna say null then you just return it then if there's no user it goes here it generates a random number this is 3717 right now then it uh creates the random name which is user3717 now what it's going to do is it's going to go to the service and do all the magic so it goes to the service it does a post request with the name that we have just said sends the call gets back the result and if I Mouse over the user there you go we got a user 3717 that is now created so we log this this means that we have now this in the database and now I can save this to storage so I take this user put that inside and now I just set the item with Json stringify so if I go to the application right now to local storage here you don't see the threads user at the moment and this is from my other application so I'm going to clear this at the moment but now if I mouse or if I step over it you can see that now we got threads user which has the user in there which means if I refresh the application now this will not be created again so it's gonna try to get the user from storage and will get it which means this code will not be executed anymore unless the user goes to the local storage and deletes uh itself okay so now you see we are not creating the user multiple times just one time now that we have the user created let's actually look into creating a comment so we're going to work with the common form of course to make everything work so the first thing that we want to do is since this is a form I also want to have sort of a button so we can submit or we can say all right submit the reply or something so if we go to the diagram you're going to see that this component had the create text here at the top level and then it had send inside when it's nested so what we also want to do is inside the home component right now we don't really show the create comment at the top which we really want so what I'm going to do is I'm going to go to the home component inside here and just above this section we're going to have another section that essentially should have the create form or the common form in this case so I should have something like uh app comment but I don't see the comment form so I need to import it inside the component TS so here just like the comment component I need to import the comment form component and now I should be able to have intellisense here so app comment form exactly now that we have this let's save this let's save the home TS file as well and now if I go to the threads app inside here now you see write something up top and then you have all the comments and I can also reply here as well now what can really change in this case is the placeholder and the reply button in this case it was create or send right so what I want to do is to have this as something Dynamic so dynamic means we need them as inputs so this form component has two inputs so the first input what happened to my keyboard oh here so input should be the placeholder and this should be write something by default and then the second one would be the button text and that would be create by default so the one that we're going to use for Global one we are keeping them as default values if someone wants to change that they can now I'm going to use the placeholder right here by using the attribute binding placeholder and just using this placeholder and then we also have to have a button so what I'm going to do is I'm going to copy the button from the component the comment component similar to what we had for reply so I think it was this one so let's copy this one I'm gonna go to form just below this we have now the button and here we're gonna remove the click Handler but say the type is going to be of submit okay cool now that we have this we can call this create or just the button text in this case so button text all right now that we have this let's save this and try this out so now if I go to threads application here you can see the create right here but I want this to actually be here instead of on the left side so let's go and fix that so I'm going to actually pick this or let's apply to the form so class is going to be Flex gonna be Flex column so if I make this Flex you know what happens right this is what happens so I'm gonna make this Flex column now it looks like this and now we'll say items end and there you go now we also want to have some gap between those so we're going to say Gap 4 now we have some Gap and now we want to wrap all of this into some sort of card that we can also use to write something so blooper and now we can wraps this into a card or even if we are not doing that then having some space because you see this is being cut right here right so this this whole thing should have some sort of padding and if I go here then is it the same because if I go to the last one which has some comments would there be the same issue here no because we have enough spacing here so what we can do with this one is have the space right here so I'm going to go to the home component where we have this app component form and inside here we can have a margin bottom of four now you can see that this one doesn't conflict with this UI and this looks nice to me now for the replys what I want to do is not have create here but you could have something like send whatever it was so if I go here what does it send yeah it's sent or you could say reply either of those so let's change this when we are using with in the components or with the comment component so if I go to my code here inside the comment component where we are using the form here I will change the placeholder so first the placeholder would be something like write your reply and then the button text is going to be send now if I go to my app you can see that here it says create if I hit reply it says write your reply instead of write something and then this says send so the text essentially changed and this can go on and on and on so now if I go inside oh this is where no this works actually it's fine so if I try to go to most in IRS and the last comment expanded you can see that we have this maybe we should say uh I'm not really sure if we should say something like no comments or something because right now there's no way for us to actually identify if a particular comment has child comments we could add that in our code because whenever there's going to be a new comment created a nested comment created we could essentially update the count of comments on the parent one I think it becomes a bit more complex if we want to do that I'm not sure if I want to do it at the moment to be honest maybe I'll I will so we'll see how this goes but anyways let's talk about creating a comment so now if I want to create a comment I would just have to type something hit create and it should work if I do it right now you can see that the page refreshes because there's the default behavior of the form so I need to go to my component the comment form component and in here I have to have on the HTML the type submit which is good now I can use the submit event to say form submit and then pass the event ident okay now I need to create this function right here which will receive an event which would be of type form or submit event and then I can use event dot prevent default you can call this event or event it's either fine and now that I have this I want the information out of the placeholder right so this is essentially the uh the fork I also want the form to be empty when this happens so I can just do event.target.reset because the event.target is a form so I can say const form equals as HTML form element and then here I can say form dot reset so now if I do this go here save this as well I can type something hit create and you can see that the form resets itself cool really nice now what I also want when the form submits is to be able to get the value out of the form so if I wanted to take out the value I could take it out by doing something like this so I could say something like um values I think if I do form dot elements Dot and then I can say named item and then I can get the name so for this I would want to have the name right here from this text area so the name of this one would be comment text let's say and that means that I can actually get the value right here so I get the value or I get the comment text I reset the form and I console.log the comment text okay now that I have this let's try this out so something create and if I look at the console you see the common text is right there oh the thing is the element itself or this actually Returns the element or the comment or the text area element so this should be text area element and here we should have common text const command text equals text area element dot value because this text area element itself is supposed to be a html text area element so here I can get the value out of it and then I can just console log it so now if I run this blah blah blah blah create and I get exactly the text out of it and then I reset the form now that I have this value what I want this the form component to be able to emit this outside so the parent can get it so in this case I want an output and here what I want to Output is essentially just the text so I'm going to say output is going to be uh um I don't know form submitted here because this is called form submit so I'm a bit skeptical of what to name that um um yeah let's call this form submitted for now so I'm gonna say new event emitter make sure to import that from angular core and then in here you can then specify the type so what I'm going to do I'm going to be actually passing an object of having name String or not a name but um text is a string so now that I have this console logged I can say this Dot form submitted dot Emit and in here I can say text would be comment text so now that I have this event being bubbled up or submitted or emitted from the comment form any place where this common form is being used I can use this event so I can go to home component and in here I can use this event form submitted which should call a function so we should call create comment event and this event is essentially the string that we get so I'm going to create this function right here call create comment with receives the object containing the text so I can say hey we do have um the form values and here we have a text string in this case so this is what we receive so now I can say hey or I could actually just have said um yeah we could also have said just text in this case as well taking that out but let's keep it this way this is more readable so now here I can say text equals form values and then what I need to do is to do this dot comment service dot create comment which doesn't exist at the moment so we are going to have to do that so if you remember the back end for creating a comment if I go to create common dto it requires the text and the user ID those are the two things you can optionally provide a parent ID but these two must be there so let's actually fix that in the front end so in the front end in my comment service I should have a create comment function which should receive a couple of things it should receive the parent ID the user ID and the text so here you can actually expect this uh to be provided so you could say something like a type that says create comment dto maybe and in this case you can say there should be a parent ID that should be either a string and this is optional so this could be there this may not be there then we also should have the text of type string and the user ID of type string and now that I have this create common dto I can say hey I'm going to have the patterns being sent here or we can say something like comment create common DDO and this should return this dot uh HTTP dot post here we would have environment dot API based URL slash comments so we are posting against it and then here what we want to do is to be able to take that those values so we would have the text comment dot text we should have I mean I could just use comment directly here I think that should still work um I would have to pass the comment like this and I think that should work and then in return what we get back is essentially the created comment so I'm gonna just say when we post this data we get back a comment which is just created okay so now that we have this let's actually look into the home component calling those so what it really requires here is an object which contains the text okay we got this already it needs a user ID we don't have it at the moment I don't think so we have it and then finally it also needs the parent ID so in this particular case the parent ID is going to be null because it's the home component so they're all top level components so the parent ID shouldn't be passed but the user ID is important so how do we get the user ID in this case I would have to inject not inject the user service by doing inject user service and then what I can do is I can get the user right here so const user equals this Dot userservice.getuser from storage and this essentially gets us the user of type user or null so we say if not user then return so don't do anything if we have the user then we're gonna say user dot ID and that is the user ID that we want to pass once we pass this then it should create the comment returned the right comment back and we should be able to then render the comment how do we render the comment once we get the new comment back we could do something like hey subscribe wait for the new comment or created comment to come back once it's there then what we want to do is to add that to our comments signal so we already render a bunch of comments if we create a new one let's just bring the latest one to the top so we are going to do something like this dot comments don't set and in here what we want to do is to push or to have this one the created comment at the top and then every comment that we already have this dot comments after it so look at this again we are using the getter to Cal to get the value so now that we have this let's try creating a comment and see how it works so I'm going to go to sources and inside here I'm going to put some breakpoints so let's go to the home component and in here let's go to the create common component right here so now if I say something like um this is my top level comment and if I hit create you see that we eventually get here you can essentially track how we get how we got here so from the comment form component when we submit this form submit method gets called then we emit the command text after we have gotten it from the text area element and then we essentially get this event triggered from submitted that calls create comment and then in here we get the text finally so it's the whole journey right here so if I take out the text this is my top level comment then I'm getting the user so I get the user from Storage I have the user I have the username as well and then finally I also have the user ID and then what I do here is if I go forward I can go to comment service create comment and then I go there I have the post with the comment details so text and user ID both are there I should be able to make the call and then I should be able to get the data back so if I make the call everything works nicely and if I see the comment we have the status 201 which means it was created if I go here you see that this is my top level comment has been created with the parent being null and the user being user 3717 yay the backend was ready we just implemented the front end now I can take that created comment and push that into the UI so if I just hit the play button there you go user317 we already have the comment now you'll see a problem when we refresh this would actually go all the way to the bottom because the data that we get from the back end when we request the comments is sorted based on the created ad but towards the wrong side of it so to say so it doesn't so it's not in ascending order or it should be in the descending order based on the latest being the first one and then the oldest being the last one how can we fix that we can go to our back end and then inside the comment service where we try to return the top level comments after the find or even after the populate you could actually do sort and in here you can either do ID or you could do created at and here you can do -1 and I think this should actually work so let's try this out now so now if I refresh this you can see that the latest comment now is this one this is the top level common so yes create it at minus one means in descending order so the latest one is going to be the first one now let's also do this for the other ones so get comments about parent ID should also be sorted based on the same criteria so you populate you sort it and then I think we should be good because the create doesn't need anything else cool now that we have this implemented let's actually check this out so I'm going to go and refresh this user317 let's also say this is also my comment and then if I add it I should go through all the process but then it should be added right here so this is also my comment if I refresh this still stays there still the same consistent sequence really cool now what I want to do is to reply to this third comment so if I go here and try to create it it probably wouldn't work or it most likely wouldn't work because we have no logic that handles what happens if you try to reply to someone because what we handled so far is in our home component where is it this is the backend code this is the front-end code so in my home component when the form sort of emits this event then I create the comment so this is what I want in my nested replies as well so what I'm going to do is close all of this I'm going to go to my comment component which uses the form right here and now here I need to handle this form submitted event so when this common form is submitted I need to create a comment so I'm going to create this function right here this should do exactly what we are doing in the home components I'm going to copy paste a bunch of stuff from there so I'm going to copy this I'm going to copy this and paste this right here and then I'm gonna also inject the user service so user service equals inject user service this guy and then if I look at the comments right here you notice that I can get the text from the from values similar to how I do it in the user form I still get the user I still use the comment service.create comment but here apart from user ID I also add the parent ID which should be this dot comment dot ID so we add it right here okay now that we add it right here when we get the nested reply created then we should be adding this to the nested comment signal because there's no comments array here or not no comment signal here so nested comments nested comments easy peasy lemon squeezy and now I can go to the UI and see it working so now if I go here I want to reply to the third comment which already has a reply so I was I'm gonna sitcom inside does it comment but now here I can reply to this third comment from motion IR so I can say something like but my comment is just much better and now if I just create it let's see what happens send and there you go now you can see that apart from this nested comment already I see my comment is just much better the form was reset it super nice and now if I refresh this I can check this again so most in IRS has both of these comments and now I can go and do a nested comment in here and this can go on and on and on but I have now the ability to create nested comments I have the ability to create a top level comment as well so everything just works magically let's go to the code and see if you're missing anything so we are now able to handle both the nested comments so we can add and create uh top level comments we can have nested comments as well we also discussed about the let's actually have a look so one of the things that I also just recalled right now is since we are using ng4 in multiple places when you're working with performance of course what you need to do is to use a track by in this case so you should be able to say track no track and I think what's this no but that was The Proposal in the RFC so I would have to have a track buy function and I would have to then give it uh that as well so in this case I would have to have hey common Track by comment track bye which should receive a comment of type comment and then you just return the comment dot ID and then I can use this right here as this and I think every should everything should be good so this is the home component go here yes so now if I refresh the track by count by 24 what wait a second is my angular knowledge Rusty angular Track by go to the angular Docs this guy Track by function yes give me an example yes yeah so you have the user track right here index and ah okay so it should be indexing the user so okay but then what Track by Common track buy um coming Track by go here go here this should be the index and then this should be the comment yes I know it's I think it should have five number but why do you cry is it because what why uh coming from component is it because so this looks at the home component I go to the home component common track by track by function [Music] hmm it's confused right now let's have a look at the track by and there's also about 115 am so that's probably why track by function um um strike by it's not tracked by function that's why make it fix uh huh cool so not so Rusty after all cool so now that we have the track by working we should also have the same thing and the uh in the comments component as well so in here I'm gonna go and do this guy um we have here so there we should have Track by common track buy and then we create that component right here common Track by should receive a comment of type comment return comment oops return comment dot ID and this should be index of type number but we don't use it or maybe to underscore this should be good comment ID um I think we are good and In Here Also let's save this so if I go to see the replies I see the comments and I can also say here but sometimes uh I don't know why my code works let's send it cool I think that completely makes sense it also reflects a lot of uh in-depth knowledge about myself cool but sometimes I don't know why my code works all right so now our application looks actually nice so what I want you to do is to go to github.com snirs and if you found this video useful try following me right there press the Thumbs Up Button share this video with others and let them know if this was nice or not and let me know in the comments what was the best part that you found about this video and if you liked it at all alright so I hope you liked the video if you did press the thumbs up button and also I wanted to share something with you if you know and understand Urdu or Hindi or if you know someone who understand it me and my team has worked on this whole new course in which we are working with web development basic so we start from ground up from the point where you don't even have the softwares installed in your machine to get started in web development we talked about HTML we talk about JavaScript we talk about something Advanced related or some things which are a bit more advanced in JavaScript and CSS 3 as well and then we end with just a project or a bunch of activities and projects that actually help you understand the concept but also to sort of build your portfolio that you can show off to others so if you want to join all you need to do is to go to codewithson.dev to the courses and you can enroll yourself you have more than 300 people already following this course and sort of making their web Journey possible so if you're interested make sure to join this or share with others with that said as always happy coding and I'm gonna see you in the next video
Info
Channel: Code with Ahsan
Views: 7,163
Rating: undefined out of 5
Keywords: angular, nestjs, nest js, full stack typescript, typescript full stack
Id: cAj6gzAMNfA
Channel Id: undefined
Length: 166min 43sec (10003 seconds)
Published: Mon Jul 24 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.