Nested Comments in Angular - With Replies and Editing

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
in this video we will implement comments inside angular as a separate module and actually the comment feature is really difficult as you can see here we have a list of comments and we can make replies there and actually every single reply can also be replied this is why it is that difficult also we have in line editing here and much more and this is exactly why i want to show you how to implement it inside angular correctly [Music] so here i already generated fast and angular application and as you can see it is completely empty inside source folder i have just an app and here is a module app component and nothing inside except of each one tag and the only thing that is external here is this style css actually here i already pasted all styles that we need for the project this is why you can just take the styles from the link in the description box below so you should not retype them and we can focus on angular so how we will implement this feature and actually we need to create an additional module for the whole comments why is that because actually we can reuse comments on every single page we can write comments for the post we can write comments maybe for the profile which actually means it must be completely shareable and configurable this is why what i want to do here inside source app i want to create new folder and just name it comments and we will have inside our module which will export our root component comments also i want to make our example as realistic as possible and for this we can use a tool which is called json server and if you don't know json server allows us to create a fake api in a second as you can see here we just installed globally this json server package and then we must create db json inside our root folder and now inside we can paste some data inside and in our case we will have only comments and now we just need to start it inside console with json server watch db.json and actually i already created here inside root db json where i have a comment property and this is an array with predefined comments now i can jump inside console and as you can see here i have a worker which is called json server watch dbjson and when we are starting it inside localhost 3000 slash comments we can get our default comments and additionally all this post put page and delete requests are working there out of the box this is why if i'm jumping to the browser and i'm typing localhost 3000 comments as you can see we're getting a list of our comments which actually means we have already an api and our angular application can work with this api so now let's start to implement our module as you can see here i already have inside source app our new folder comments that we just created and the first thing here is of course our module so let's create here our file comments.module.ts and inside we must export our new class and we're naming it comments module and this is not enough on the top we must provide a decorator in g module where inside we will provide some configuration later so our module is ready and we must register it inside our app module this is why let's open app module ts and here at the end of our impulse i must write comments module that we just created and obviously we must import it here on the top our next step is to create our root component for our comments this is why let's jump inside comments folder and here let's create components so the root component that we will have is called also comments because actually we have a list and we can split it in three different components for example first we will have a component for the list of our comments secondly we will create a component for single comment and the last what we need is comment form this is a form that we will use first of all to create a comment and secondly in the reply section so let's add here our new folder which is called comments and inside we must create comments.component.ts and also comments.component.html now inside we can simply write comments so at least we see that something is rendered and inside our ts file we want to export our new class and it is comments component and we must register it on the top this is why here is the creator component and we must provide inside several things first of all here we must write our selector for example let's write here comments and we also must provide here template url which we just created and it is comments.component.html so we successfully created our first component but it is not enough we didn't register it inside our comments module so here inside ngmodule we must write declarations and register our component here so we have our comments component as you can see here but it is also not enough because this is the component that we want to expose to use outside which actually means we have a page and we want to inject there our commons component and this is the only component that is allowed to be injected outside this is why here we must provide an experts and inside experts we can say what components we want to allow to use outside and in our case it will be just a single component comments component and now we can use this comments component outside for example inside appcomponent.html so here after each one tag we can write just comments and it will render our component let's check this out i am jumping inside browser and as you can see commons component is successfully rendered the next thing that we need to do is provide current user id inside our comment section why is that actually in the typical production application we have some current user when we are logged in and typically only logged in user can create new comments and update his own comments inside our current application we obviously won't create authentication this is why what i want to do i want just from outside provide here a property and it can be for example current user id equals one and we don't have current user id at all inside our application but our commons component should not know anything about current user id and we always will provide it from outside which actually means it is completely production solution because this current user id we will get in any case outside of this component and not inside now to make this code working we must open our comments component and create an input inside so here inside our class we want to register the creator input and we must name it current user id and it is string because we are getting it from outside but here is the problem as you can see typescript screams that property current user id has no initializer and it is not definitely assigned in the constructor which actually means yes this input is undefined by default and yes typescript doesn't like it the typical solution here will be to write that it can be string or undefined for example and in this case we don't get an error the main problem is here that we will use inputs in all our components and in every single time we must check for undefined which actually means we have like 10 different methods and inside we will always provide for example an input current user id but in every single case we must check if we have current user id or not and actually this is super tedious and yes in the real project i would do that but i don't want to bother with it if i can avoid this this is why here we will use the solution which is called a bank and as you can see here after our current user id variable inside typescript we can just put an exclamation mark and actually this is called bad practice why is that because here we're telling typescript yes we're sure that current user id will be provided it will never be undefined and actually you can't be sure about it because later some person can use your comments module and not provide current user id here and then it will break so if you don't want to bother with undefined and if you're really sure that you will provide everything everywhere then you can put bank and it is okay but if you want to be 100 sure every single time then you must check for undefined in every single case the next thing that i want to do is create an interface for our comment because actually we are talking about angular and typescript and typically we want to create an interface for every single entity this is why here inside our comments directory i want to create new folder which is called types and inside we can create a new interface comment.interface.ts now inside we must export our new interface and we're calling it comment interface so what properties we have inside our comment and for this i want to open our dbjson because this is a format that we are getting from the backend which actually means here we have an id and it is a string then we have our body and it is also string we also get a user name and it is also string also user id it is our string we have a parent id and as you can see here we have parent id null and it means that this is a root comment because it doesn't have any parent and here we have a comment where parent id is 1 which actually means this is the child comment because here is its parent and our parent id can be either string or now if it is a root comment and after this we have our created ad and it is always a string the next thing that we want to create is our service because we want to fetch our comments from our api this is why here i want to create new folder services where we will put all services which are related to our comments module and here we can create just comments service and this is a really nice name first of all because you should not think what exactly service will do this is like a default service for our module and we will put some stuff here and if we have too much stuff here then we will split it in several services so here first of all we must create our new class and it will be our comments service and we also should not forget to add here our decorator and it is injectable so inside this service i want to put all our methods that will work with our api and the first method here will be get comments and this is what we want to fetch so what we will get here back we will get an observable because we are talking about http and http requests are returning for us observable this is why here we are getting our observable and inside our observable we are getting a list of our comments this is why here is comment interface array now inside this method we want to use http client this is why we must inject it inside our constructor so here we must write constructor we have private http client and we want to use here our http client import now inside our get comment we can simply return our this dot http client and here is our get and by getting our comments from http localhost 3000 slash comments but as you can see we are getting an error because actually by default http get is returning for us observable of object because http get can't know what data we're getting back but we know what data we're getting back this is where we can specify it here inside our get and we can say that here we're getting our comment interface array so here we define it that this http get will return for us array of our comments so our method is ready but we didn't register our service this is why we must jump to our comments module and here we must define our providers and inside providers this is an array we must provide our comments service and it is not all you also need to check if you have inside app module and http client module because if you didn't import it here then your http client won't work so now we're ready to fetch some data inside our component this is why let's jump inside our components comments comments component and here first of all we must create a constructor because we want to inject our service inside and here we must write private comments service and here is our comment service class that we just created now we also must create an ngo in it and here i like to write implements on unit so we must implement this method and here we can provide engine in it and it is obviously void and we want to fetch our comments here this is why here we can write this comment service get comments and this will return for us a promise this is why here we must try to subscribe and we are getting a list of our comments so let's console log here our comments and check if it's working at all if i will jump to browser and reload the page you can see inside console that we are getting our list of comments and actually this is a real api yes these data align inside db.json but our json server is serving them through api this is by inside network you can see when i'm reloading the page that we have here a url comments this is 200 and this is our localhost 3000 slash comments and here inside response we are getting a list of comments now let's render our list of comments without creating additional components and actually for this we must save this comment somewhere locally this is why here we must create for example comments property and it will be comment interface array and by default we can assign here it to empty array now here instead of console.log we must assign inside comments our comments that we fetched and now we can render them inside our markup so let's jump to our markup we don't need comments word here and here is our markup so we have here first of all class comments and we must close this div now inside we have h3 with class comments title and here let's close our h3 and inside we have comments after h3 we will have div with class comment form title and here we will have write comment text and after this we must render our comment form but we don't have it yet this is why i will simply write here comment form as a placeholder and we will implement it later after comment form we will have our div with class comments container let's close this div and inside we will render a list of comments and actually later here we will render a common component within g for loop but for now we can simply run the additive with this ng4 so here we want to loop through our comments and get locally a comment this is by let comment of comments and we already created comments property and now inside this div i want simply to render comment dot body let's check if it's working i'm reloading the page we see some markup but actually we have an error cannot bind to in g4 off and if you are getting such error it means that we didn't inject common module inside our comments module this is why here we must import common module let's check once again i'm reloading the page and we don't have any errors and then g4 loop is there and we see our comments that we fetched from api now it's time to create our comment component as you can see here we just rendered a single div but we obviously want to render here our single common component this is why let's jump inside our components and create new folder which is called comment now inside we must create comment dot component dot ts and also comment dot component dot html and now we must register our comment component here but i want here to just copy paste the declaration from our comments component in this case we don't need to type it again so first of all here we want to rename our comments to comment component we don't need implements for now and we need to import this comment on the top also a selector here will be comment and template url is common.component.html and now inside we want to get as an input the whole comment as an interface this is why here we can write input so we're getting here our input which is a comment and here we must put a bank sign because we want to say that we will for sure provide it and here we can define that our comment is of type comment interface now i want just to render something inside our comment.html for example comment.body and this is our message we also must register this component inside our module so we're jumping inside our comments module and here inside declarations we must provide our comment component now we can use it inside our in g4 loop for this i want to jump inside comments comments.html and here we don't need div now but we need a comment this is our new component and now inside we don't need to render our body we must provide it here as an option we're providing inside our comment let's check if it's working i'm reloading the page and as you can see we successfully rendered our component comment but obviously our markup is broken and we must fix it now this is why let's jump inside our comment comments.html here we can remove our command body and we must write at least some basic markup for example here we have our div with class comment now inside we want to render an image and actually i put the image of the user inside our public folder so as you can see here source assets and here i have user icon png and you can take it from the description box below with the source code together so here we can simply write slash assets slash user dash icon dot png and we just close here our div and obviously it's not a div this is an image tag and we must close it now but i also forget to wrap this image with additional div so here will be div class comment image container here we're closing our div and after our image tag we must close it also now we must create our right path so here we will have diff class comment right part let's close our div and now inside this right part we must render our comment so div class comment content and inside we want to render first of all our author name so here will be div class comment author we're closing this div and inside will be our comment dot author after this div we must create one more div and here we will have our div class comment text and let's close this div and inside render our comment.body so we're done now let's check if it's working i'm reloading the page and we're getting property author does not exist on type comment interface let's check this out i will open comment interface and as you can see here we don't have author at all and actually we have here a username and this is exactly what we want to render so let's change this to comment.username i'm reloading the page and we don't have any error and here is our comments and actually markup is looking correct here but we're missing our image and actually i already see the problem i wrote here image class it is obviously not class but image source let's reload the page as you can see now our comments are looking much better now i want to continue with comment form because this will help us to create new comments this is why let's create a new folder here comment form and we need at least two files inside first of all it will be comment form dot component.ts and comment form dot component.html and i also want to copy paste what we have with site comment because all this component creation will be almost the same so here we don't need our input and here we will have our comment form the selector is comment form and template url is also comment form and we don't need interface on the top and we don't need an input so we successfully created our comment form now let's render something inside at least let's write down comment form and we must register this component inside our comments module so here inside declarations we have command form component now let's think a little bit about our comment form what properties do we need inside first of all we must provide initial text because we will use this form also for editing which means we want to provide the initial text from the comment secondly we also need an attribute for cancel button because we have a cancel button in for example reply or edit but we don't have it in create comment and we also need a submit label because for example when we are creating comment we must create a label add comment and when we're talking about replying maybe reply and when we're talking about update and update this is why we must pass here several inputs first of all here we will have our input with text submit label and again i will put here band because we must provide it from outside secondly we will have here input which is a boolean and it is has cancel button and by default it will be false and i also want to write here that this is a boolean and the last one here will be our initial text and we must provide it in update which means it is not mandatory and we don't need it for add this is why i define it here by default as an empty string now we must create our form and actually it is super easy to do because angular has this feature which is called reactive forms this is why here i want to write a constructor and inject here reactive form and exactly fb which is a form builder now we want also to define here ngon in it this is why here is implements on init and here we must create a method and zeon in it where we will create our form so here we can write this form equals and we have just a single property here this is why here we have this fb group and we are providing inside all our values and we just have a title because our comment form has just a single property but here we want to define also some validations this is why here we can provide this initial text this is our default value and it is empty string or a value that we provided inside and secondly we have a validator this is why here we write invalidators dot required now we're getting an error that we don't have this form this is by here i want to create a form and this is a form group and we must put here bank also because by default it will be undefined and actually we don't want to bother with undefined it is completely fine for us to just create it on ngo need now let's write markup for our form so i want to jump inside comments form and here write form and this is form group this is exactly what we created in our case we named it form and here we have also ng submit method that we will create in a second and this is on submit function now let's close our form here and inside we must first of all provided exterior this is exactly our title so here we have text area with class command form text area and after this we must bind it to our specific input so here we are right in form control name equals title this is exactly the binding to our form group and here we are closing our exterior field and after this we will have several buttons first of all cancel and secondly submit so first of all let's create here submit button this is why here we will have button with class command form button and after this we have type submit and here we also must provide a disabled and we actually want to disable this button if our form is invalid and by default it is invalid because our title is empty and this is actually amazing logic because here we don't need to write any rules we simply check that our form is invalid then this button is disabled so now let's close this button and render our submit text inside so here we will have our submit label and just like we defined here we must provide it from outside this is mandatory after this i want to create cancel button this is why here we will have also button but here we must write in gif because we don't have this button always and here we have this hash cancel button that we must provide from the outside and here we have type button and also class and in our case it is comment form button and also command form cancel button now let's close this button and just write a text cancel inside and our markup is fully ready now we must use this form inside our comments at least and we will use it later also for replies and for editing so here is our place for comment form and we must create here add comment form so here i want to use command form component that we just created and provide inside submit label because it is mandatory and this is just text for example right and this is our add form and for now i won't provide anything inside but later we must provide a submit handling site so here i will close our comment form and let's check if it's working i'm reloading the page as you can see we're getting an error can't bind to form group since it's not a known property of form and this is actually happening because we must inject reactive forms inside our module so let's jump to our comments module and here inside imports we must write reactive forms module and this is actually not the only problem as you can see here we're getting a problem that comment form is not an available component and actually i already know the problem here is our command form and i must write it here with dash and not with camel case and as you can see now we have another error on submit is not defined inside command form so we must jump inside our comment form and create here on submit method and actually here we're not getting any arguments because we have available this form so we can console.log here on submit and here this dot form dot value and actually we still get an error but now here is the problem that ng submit is actually an event and not an attribute so this is why the brackets here are wrong they should be round here let's save this and check again as you can see now everything is green and it was built successfully so we can jump inside browser and here we can see our new command form so we have here our text area and here right button is disabled because actually our form is not valid now i am providing something inside and directly here our button is enabled we can hit here enter and as you can see in console we have our on submit with title inside which means we successfully implemented our basic comment form what we want to do now we need to implement submitting of our form as you can see here we just wrote in on submit our console log and this is not enough we want to send this data back to our comments component and for this we must create here an output this is why here i am defining an output and it will be our handle submit and here we must assign here new event emitter and this is what we are using to emit data most importantly here is not to forget auto import not from stream but from angular core so here we have new event emitter and we want to provide the type so what we want to submit in our case we simply have a string this is why we just want to emit a single string this is why here i am setting string as an argument so our output is there now we must call it inside on submit so here we can write this dot handle submit and just to remind you here we must write here emit and not just call directly handle submit it won't work and here we must provide inside this form value and we know that we have a title and we are interested only in title so console.log we can remove and this is how we are providing a value i would say to our comments component now let's jump inside our comments component so as you can see here we have our form this is our comment form we submit title and now here we must provide an output this is handle submit and actually here i don't want to create a function handle submit because here we want actually to say what this form is doing and in our case we can write here for example add comment and we can provide a value and in our case it is dollar event and it will be exactly our string that we are providing from inside but actually this is not exactly what we want yes this is a comment and this is our event and we can use it in our case now but later we also want to implement replies which means we will use this add comment method to add replies to some parent comment which actually means it is not enough to just pass a string inside that comment we must know to which comment we want to add this comment it will be either a root comment or it will be some parent this is why here i don't want to provide just dollar event i want to provide here an object and let's write here for example text this is our event that we provided from our comment form and here additionally i want to provide a parent id so what is parent id in our case it will be null because here this form creates for us a root comment and root comment always has a parent id now this is what we're providing here but with such notation now we can use this form and this add comment function to also add comments to some specific comment by providing here inside parent id so now let's create this add comment function so here after nguyen init we want to create a comment and we are getting here an object with text and parent id and here we want of course to write correct typing so we know that text is always a string and parent id can be either null or a string and this function will return for us void which means nothing and let's just write here console.log add comment and let's check what we're getting here is our text and our parent id let's reload the page we don't have any errors let's write some comment and hit right as you can see here we see our add comment console log and here is our text and null which actually means we successfully arrived inside this function now here we want to save our comment inside our database and for this we must update our service this is why let's jump inside our services command service and here we must create new function for example create comment and inside we will pass our text this is our string and parent id this string or now in this case we can create any comment inside database and here back we want to return observable and inside we are passing our created comment which means comment interface which actually means create comment is an asynchronous function exactly like get comments here we are getting back observable of array of comments interface and here inside create comment we get an observable of a single comment now here we must create this comment this is why we want to call this http client and here it will be a post request and here we must provide insight what we will get back in our case it is comment interface because this post request will create a comment and get it back and inside here first of all we must provide the url and we have here http localhost 3000 slash comments and this is a post request so we don't need to provide any id here and after this as a second parameter we must provide all our data and just to remind you here on the right i will open our db json and as you can see this is our database so actually here we have not text but body this is why here i want to write first of all body equals text and secondly parent id equals parent id this is what we are getting from the argument and actually this code is enough in the real database because we are providing from the client just body that we want to create and parent id but actually here we're using a fake api which actually means it won't create for us all other properties like you can see here they are predefined like created ad user id user name and so on it will just set an id and put inside what we passed there because it is mocked it doesn't have any logic inside which actually means here i want to add all these properties that typically won't be said by the client they will be generated on your production by your backend but in our case our mock server can't do it this is why here i will write comment should not be set here in this case you know that typically we don't set this properties and now inside i want to write first of all created add and here we can write new date and here 2 is a string after this we need user id 1 and here will be our username john and actually in the real application when you are creating a comment then you are logged in and then your backend knows with what customer you are logged in in this case backhand knows your user id username and it can set created ad so our create comment function is fully ready let's use it now this is where we must jump back inside our comments and here is our add comment so what we want to do here we want to call this comment service and now here we have a method create comment where inside we must provide a text and parent id and we can subscribe to the result which actually means we are waiting for created comment this is why here as an option i will write created comment and as you can see here typescript gets the correct comment interface which actually means we should not type it here it is there by default now the question is what we are doing with this created comment actually it was created on the backend but we must update our comments array this array here and that there our created comment and actually we can easily do it just by spreading all our comments to the comments property again this is why here i want to assign an empty array i am spreading here these comments and at the end i want to pass inside created comment that we just got in this case we will update our comments and then glue will re-render our array let's check if it's working i'm jumping to browser i'm reloading the page here i'm writing a comment i'm hitting right and as you can see here on the bottom i see my new comment so it was successfully created and we can actually check this inside network because here we can see our comments request and it was paused on our slash comments and here is our payload that we send here as you can see parent id now here is our body and here is our response so our method is working correctly and then google successfully rendered our array now let's implement some actions for our comment this is why i want to jump back inside our comment component and here after our comment text we will first of all have a comment form later when we will implement editing so here we can write command form editing maybe and after this we will have all our comment actions so here we can create div with class comment actions let's close this div and inside we will have three different buttons edit reply and delete so first of all here will be a reply button this is why here i want to write div ng if and here i will write can reply and we will implement this logic in a second the main idea is that we can't reply on the comment if we are not logged in so here we must provide also a class comment action and we also will have a click event but a little bit later so inside i will simply write reply text and close our div now i want to do exactly the same with edit and delete this is why i will copy paste this div three times and here we want to write can added and then can delete and here inside will be first of all added and after this delete and actually after this div we will have later comment form replying so i will just paste here a placeholder and after this we also need to render our replies when we will have them so now we need to implement these three properties can reply can edit and can delete so let's jump inside our component and first of all create them first of all we have can reply and it is a boolean and by default everything will be false in this case we are on the safe side we don't show anything so here is scan edit this is also a boolean false and we also have can delete and this is also boolean and by default it will be false now we must assign all these properties inside ngin init this is why here i want to write implements on init and i need to create here ng on init function now inside first of all let's talk about can reply and actually we can reply when we have a current user id and actually inside this component we don't have any current user id so we must pass it from outside this is why here we must write that we need an input which is called current user id and this is a string and here we must put a bank to avoid initializer problem now let's open on the right our comments component and here inside our html we must provide inside every single comment this string so here let's write current user id equals current user id and just to remind you inside comments component we already have current user id from the input which means it will work correctly and here we're passed inside current user id now inside ngon init we must assign this can reply property and actually here we simply check if we have current user id this is by here i will just wrap inside boolean this dot current user id so either it is null or undefined so we will get here false in other case we will get here true let's check this out i am reloading the page and as you can see here we have a reply button under every single comment which actually means our logic is working correctly now we need to implement can edit and can delete and they will be really similar because the logic is the same you must be an owner of the comment and you can't change your comment if it is older than 5 minutes this is why here first of all i want to define what is 5 minutes here we can write 5 minutes and inside i will assign 300 000 it is exactly 5 minutes in milliseconds now we can check if the time is passed this is why here i want to write a property time passed and here we must compare current date with our current date minus 5 minutes so here we can write new date and here we must convert it to milliseconds this is why get milliseconds minus and here will be new date of this dot comment dot created add and this is when this comment was created and just to remind you this is a utc screen and we are converting it to date and here we also must write get milliseconds to compare it correctly and we are checking here if it is bigger than 5 minutes so what we are doing here we are first of all taking our current new date this is our local time here is the date when our comment was created and we're checking that the difference between these two dates is bigger than 5 minutes in this case time is passed and we can't change this comment anymore and now we can create this scan edit and can delete properties so first of all this can edit and first of all we must check that we are an owner of this specific comment this is by here this current user id equals this comment dot user id and it is not all we also need to check that 5 minutes didn't pass this is why here and not time passed in this case we can edit our comment and with delete it is exactly the same current user id equals comment user id and time is not passed and it is also not all we also must check that we don't have any replies of this specific comment the main idea is that if you have here a comment and we have replies inside you can't remove a comment it doesn't make any sense because you have the inside replies the main problem is that we don't have any replies here and we must get them as an argument so here we can write that we have an input and the name is replies and we know that this is comment interface array because replies is simply an array of comments now here we must check that and this dot replies dot length equals zero in this case we know that we have zero replies and we can successfully remove a comment let's check this out i'm reloading the page and we're getting property replies has no initializer so here i will put an exclamation mark let's check again we don't have an error but here is of course our arrow cannot read properties of undefined reading length which actually means here inside our engine in it we are checking this replies dot lens of undefined it and obviously we are getting an error this is why here we must go inside our comments and here inside every single comment we must provide replies the question is how we can get replies we must create a function where we inside provide an id and we find all children of this id so here let's create a function get replies and inside we must provide an id of the comment so comment dot id now let's create this function so i need to jump inside our component yes and here let's create get replies and we know that here we are getting our comment id which is a string and back we will get an array of our comments so this is comment interface array and inside this component we have a full access to the list of all our comments this is why here we can simply return these dot comments and we must filter it by parent id so here we can write filter and inside our filter we get an access to the comment and we can check command dot parent id equals our comment id that we provided in this case this code will return an array of the children of this comment but we can do even better we can sort our replies this is why here we can directly write dot sort and here we're getting a and b which is previous and next comment in the row and here we can just write new date and we're providing inside a dot created add dot get milliseconds and minus new date and here we're providing b dot created at dot get milliseconds in this case we will sort our replies in the correct order directly inside get replies function let's check this out we don't have any errors let's reload this page we also don't have errors here and as you can see now we see reply button everywhere but we don't see here edit and delete but as you can see in the last comment we see the edit button because actually five minutes didn't pass and we can edit this comment let's check this out i will create one more comment and as you can see here this comment is on the bottom here we have reply and edit but actually delete is not there this is why we must check what is the problem let's open our comment component let's check here the markup so ngif can delete this is correct and i already can see the problem here i named can delete can edit this is the problem so we just reassigned it here we must create this can delete let's check this out now we have delete button and it is working correctly now let's think about active comment and actually we can have a reply form and edit form and we can have only one active comment with either a platform or edit form and if we are hitting reply on another comment then we must close our previous reply comment and open a new one this is why it makes a lot of sense to have a single property like active comment for example where we store all needed information this is why what i want to do i want to jump inside our comments and here create a new property so we have here our array of comments we can create here active comment and it will be of type comment interface or now and by default it will be now because by default we don't have any active comment but actually this is wrong approach why is that because actually here we're storing comment interface and nothing else which actually means we can't know what this comment is exactly doing is it an editing state or is it in replying state here we just know what comment we activated this is why i want to create here a new type so for now we have just a single comment interface and it is not enough for us i want to create inside types something like active comment interface and inside we will store not only our comment but also its type this is why here we can name it active comment interface and what we want to store inside first of all the id of our comment this is our unique identifier what comment is activated and secondly a type and actually here we can just save like a string maybe replying or editing but actually this is not the best approach because we are using typescript and in typescript we have enumerables this is why here we can create for example active comment type so here i am naming it enum.ts and inside let's create our enum so we want here to export an enum with name active comment type and this is just an object with two fields first of all replying and here we will store a string replying and second string will be editing and actually this is a nice approach because in this case we're storing all our possible states inside types and we can use it like a value and like a type this is why here on the left we don't need two strings but here we can just write active comment type and actually it will be even better if we will write here postfix enum in this case we're naming it exactly the same if it is interface then we're writing inside interface if it is enum we're adding here enum this is how our active comment interface is looking like we have an id and a type and now we understand what comment is activated and in what state where are we replying or are we in editing state so now we can jump back inside our commands component and here we must write not comment interface but actually active comment interface and inside we have just an id and type and by default it is null this is completely fine but now we must change it inside our comment this is why here inside comment we have these buttons with reply and edit and we must change our active comment when we're clicking here this is why here i want to set a click event and here we want to call set active comment we don't have it yet and here dot emit why amid because it will be our output and here inside we must pass two things first of all the id and here we have access to command.id and secondly we have a type and our type can be replying or editing and in this case we can directly write here replying but again this is not the best approach because we have an enum here but we can't import an enum directly here inside html this is why here what we can do we can create an enum property we can write here for example active command type equals active common type enum in this case as you can see i have an import here on the top and now this active command type is available for me inside my html so here i can directly write active command type dot replying and now we have exactly the same story for our editing this is why i will copy paste this click and put it inside edit as you can see it looks exactly the same but here we don't want replying we want dot editing and it will work exactly the same because we simply change our active comment this is why now we must create this set active comment output so let's jump inside our command component and define here our output and our name will be set active component and here we're creating new event emitter and we must import it from angular core and inside we're passing our possible type and in our case it will be either active comment interface or now and here we obviously need round brackets and you might ask here why did we provide as a type now because actually in our definition here we don't have now we just set an active comment and actually we're providing here null because later we will have a cancel button so we open edit form and then we're hitting cancel in this case we want to set our active comment back to now this is why null here is completely possible and as you can see we didn't create any additional functions we simply use this set active command inside our html directly with emit now what we must do we must jump inside our comments comment html and here we must define this output so we have here set active comment and actually here we can directly create set activecomment method where inside we're providing our event now let's create this set activecomment function inside comments so here on the bottom we can simply define that we're getting here our active comment and it is either active comment interface or now and this function will return void what we want to do inside we just want to change our these dot active comment with active comment that we got from our child this is it with this logic we already can build our reply form so let's jump back inside our comment and here is a placeholder for our replies this is why here after div we can create comment form let's close this form here and inside we must provide some properties and first of all i want to put here ng if is replying and actually what is this reply and this is an additional function that we will create in a second which will check exactly active comment and reply and state in this case we know okay we're replying now to this comment and we must render this comment form also after ingief we must provide here a submit label in this case for example reply and we also need a handle submit that were already defined inside and actually what we want to call here we want to call add comment and just to remind you previously we had a comment just in parent inside comments but we also want to provide this information to the top this is why here at comment will be an output this is why here we're writing it with a mid and we are providing inside an object just like we did previously first of all we must provide here our text from dollar event this is what we are getting from our comment form and secondly parent id and in this case we are providing here reply id so what we are missing inside our component first of all is replying function and secondly this reply id so let's try to do this now first of all i want to create this is replying function and actually this is a function because this value can be changed later this is why we can't write it directly inside ngon init we must call it inside a function now this function must also return a boolean in this case we know if we must run the form or not so what we want to check inside is an active comment and actually here is the problem we don't get active comment from our parent and we must do it this is why here i will write that we are getting as an input our active comment and actually it can be of type active comment interface or now and now we must pass this active comment inside comments html so here we will have an additional property active comment and we have the section comment inside our this property after this we can use this active comment inside is replying function so here first of all i want to check if we don't have this active comment then they will directly return false in this case we are for sure not replying which actually means this comment is not active after this we must check for the type and the id so here what i want to check is that this activecomment.a equals this dot comment dot id and this is our current comment and we also must check a type so this activecomment.type equals this dot activecommon type just to remind you this is our enum dot replying as you can see here i didn't use any strings this is all coming from enum and this is extremely important so once again first of all we're checking for our active comment if it is not active we are directly returning false if we have an active comment then we are comparing first of all the id and secondly if the type is replying so we know that we must render a reply form and the last thing that we must implement here is reply id just to remind you here i wrote add comment and this is our output we must also create it and here first of all we're providing text and secondly parent id equals reply id so what is reply id and actually this is a tricky question because here we want to render all our comments nested which actually means we will have a lot of levels of our comments and this is extremely difficult to support and this is not super fast this is why typically all applications that you will find in the internet have only two or three levels of nested comments in this case it is much easier to support and you don't have performance problems this is why what we want to do here we want to assign a comment when we're replying on the second level to the parent of the second level which actually means you will never have more than two levels you either have a root comment or you have a list of children and you can't really reply on the child of the second child and so on these comments will always be grouped in the list of the root comment and this is exactly how it works for example on youtube so first of all here we must create this property let's create here reply id and actually it can be string or now because this is simply an id and by default here we will assign now and this is what i want to assign inside so here we will have this reply id equals and we're checking if we have this parent id if yes then we want to return this parent id if no then we want to return this comment id what does it ever mean first of all we don't have parent id here for now but we must provide it the main point is that here inside our common html we must render all our children of this specific comment which actually means we will have a parent and actually inside our root comment our parent is obviously now and actually we will render comment component recursively and here we first of all check if we have a parent id and we have parent id only if it is a children and if we have a parent id then we set it as a parent id in other case we are using this comment id why is that because in this case we're a root comment and we want to attach a child comment directly here to this comment where we are inside but if we are in child command so we have a parent id then we want to use a parent id because we want to avoid this nesting so what we must do now here we must get a parent id from the outside this is why here let's create an input and here we are getting our parent id and it will be either string or null and by default we can assign it to now and last but not least we must create this add comment function with a mid that we just wrote here and this will be our output this is where here we can write that we have an output with the name add comment and this is new event emitter where we have a type of the object and here we have first of all text string and secondly parent id which is either string or noun and don't forget please hear round brackets because you will get an error so what we must do now we must write this add comment function in our parent but actually we don't need to pass parent id because inside our root level it will be always now this is why let's jump back inside our comments comment html and here we must create a new function add comment and here what we want to call is directly add comment with the dollar event because actually we are passing inside our data in the correct format and just to remind you here we have our add comment where inside we are passing text and parent id which means it will work out of the box so now let's check if all this stuff is working together first of all we don't have any errors here inside web server i will reload the page and here let's hit on the reply and as you can see now our comment form is rendered which actually means here inside our comment as you can see in html we have this logic with this replying and it is working which actually means when we hit on the reply we set it in our parent our active comment with the id and replying type now here we are rendering our comment form this is this form with submit label reply which is really awesome because it works out of the box also we're checking here for the text and our button is disabled now we're hitting reply form doesn't disappear but here inside console you can see that we are getting add comment here is our text and this is our parent id this is exactly what we wanted and now we can check our network because actually we had a request and as you can see this is our post request to create a comment and this is our correct payload and here we have parent id 1 which actually means we directly created a comment inside our api this is why we can open our db json and find down your comment here on the bottom with parent id1 and as you can see we successfully implemented our reply form and active comment what is more important when we're hitting reply on another comment then the form inside first comment is being closed and here we are getting a new form the only problem that i saw afterward hit and reply we don't hide this form but this is really easy to fix we can jump directly inside our comments and here when we have add comment at the end after creating a comment we can just change this dot active comment back to now because no comment should be active let's check this out i'm hitting reply i'm writing something here i'm hitting enter and we don't see our form which actually means we implemented everything successfully now we must render a list of our replies and actually it is not that difficult to do because we prepared almost everything so let's jump inside comment and as you can see here we have already our list of replies which actually means we simply need to render them here after our comment form so what i want to write here is div with class replies and here we want to write ng if condition because we don't need to render this list if we don't have any replies so what i want to check here is replies length if it is bigger than 0 then we are rendering this div now inside our div we must render a list of the common component this is why here we can simply write comment and here we have ng for loop let comment and actually it's not a comment but it is a reply so let reply of replies and replies is an array and here let's close our comment which actually means we're rendering here all our comments recursively but the main point is that we must pass every single input and every single output inside this component in other case it won't work this is why here first of all we must provide a comment and this is our reply secondly we must provide the function set activecomment we already did that and here is our setactivecomment dot emit and here dollarevent so this is how we typically provide it from outside but of course here we must have round brackets because this is an output so we just call here set activecomment function which is essentially the same function and we're providing here event that we got from our reply now we also must provide inside an active comment because we need it inside our comment and we have access to it inside our parent comment we also need to add here our add comment function and it will be at common.mid and here we have dollar event and after this we must provide a parent id and just to remind you we didn't provide it inside our route because their parent id was null but it is not now here inside this list it is the reference to our comment comment.id the last one here will be the list of replies and actually our reply can never have any replies because we have just two levels this is why here we simply provide an empty array this is completely fine and the last but not least is of course current user id we also need it and here we have access to current user id inside our current component let's check if it's working i'm just reloading the page and as you remember we created quite a lot of replies to our first comment and we can see all these replies here they are directly available for us but here the interesting question is regarding this reply button but actually if i will open a reply form to reply to the reply and write here for example this is the reply for reply and hit here reply button then here we directly created this reply not as a child of this comment but as a child of its parent in this case we always have just two levels and not deep nesting and this is exactly why we wrote this logic that you can see here with reply property so here we wrote when we have a parent id then we assign inside parent id so this comment and another case we just make it a child so we successfully rendered all our replies now we must implement editing form and it will be super similar to our replies form first of all i want to create additional function here not is replying but is editing and the idea will be exactly the same so i will copy paste this function completely and just rename it so let's name it is editing and inside first of all we're checking for active comment if we don't have an active comment then we directly return false and here we're comparing exactly the same first of all the id and secondly active command type dot editing now we must use our command form inside html so let's jump here on the top and here is our placeholder for comment form editing so here i want to inject our comment form and let's close our comment form and first of all inside we want to check within gif for is editing function because actually if we're not editing we don't need to render form at all after this we also must provide a submit label inside and this is for example update word we also need here has cancel button and just to remind you we must create a cancel output also so we can close our form we didn't do it yet and after this has cancel button we want to provide also initial text because this is an edit form and here we can provide our text dot body and last but not least is of course handle submit function and here we must create an output for example update comment and it will be exactly the same here we must write an emit and here we must provide an object with first of all text and this is our dollar event because it is coming from the component and secondly comment id because actually here we must know what comment we want to update on the backend and here will be our comment.id so the idea is exactly the same with handle submit just like we did with create but here instead of parent id we're providing inside actually not comment but comment id now let's register this update comment function this is why here actually we can jump to the top and copy paste for example add comment because it will be really similar so here we have our output but not add comment but update comment and this is new event emitter with text and comment id and actually in this case our command id can't be null it is always a string because this is our unique identifier what exactly we need to update now we must register this output inside our comments component this is why here let's add update comment and we need to create here a new update comment function where inside we will provide an event now let's jump inside our comments component tiers and here after our add comment we can write update comment and here we're getting our text and we also are getting here comment id and actually text is string for us and comment id is also stream now inside we want to call our service and update our record inside the database so we must also create a new function inside our service but for now i just want to write the code how we will use this service so we have our command service and here we can create update comment function and we can provide insight first of all our comment id and secondly text and this function will return for us an updated comment so here we can directly write subscribe and we have access here to our updated comment what we want to do after that is update our list of comments because we need to change all properties inside this specific comment and for this we can use a map function which fits really this case so we can write here these comments equal and we're just looking through these comments with map and we have access to every single comment now inside first of all i can check if our comment id equals comment id on the top then we must update it this is why here i will return updated comment in other case i want simply to return comment without any update in this case we're simply reassigning every single comment inside this comment property again but we're changing the comment with this id with our updated comment that we got from the backend and obviously you are getting here an error because we didn't create update comment function yet and actually after all this stuff we also need to hide our active comment this is why here i can write this active comment equals now now let's create our update comment method inside service this is why let's jump back inside our services comments and here we want to create update comment method that is getting first of all an id this is a string and secondly text which is also string and back we will get exactly the same like on the top this is observable with comment interface so what we want to do inside exactly the same stuff like we did inside create this is why i will copy paste completely this http client but in our case it won't be paused it will be patched because we just want to change some properties and here we must specify what we are getting back it is our comment and here we must also change the url because here we have also an id so here we have slash comment slash and here we have an id of the comment that we want to change after this we are providing just body with text because this is just a single property that we updated we don't need to provide any other properties let's check if it's working but don't have any errors here let's jump inside browser and here let's click an edit button and as you can see we are getting directly here our comment form but the problem is that we didn't hide our text here and actually it must be hidden this is why let's jump back inside our comment component html and here on the top we must trend this comment text only when we are not editing so here ng if not is editing and we're good to go let's check this out here i'm hitting now edit we don't see this text we just see our comment form with stacked inside now for example here i will change this form and hit update and as you can see nothing happened and actually this is a really nice case to show you that we need also to pass correct outputs to our children the main problem is that if here i will create the root comment for example qqq as you can see this comment is here somewhere on the bottom and now here i will hit edit and change it here and hit update it is working it was updated and actually it was also updated on the backend but it didn't work with our child because we didn't provide this update comment amid inside our child so here on the bottom as you can see we have add comment for example and we also must provide here update comment function in other case it won't work because this function just doesn't exist so here we're writing just exactly the same update command emit let's check this out here we have a child i'm hitting edit i'm just changing it i'm hitting update and it is working we successfully updated this record and we can just find it inside our dbjson and as you can see it was updated inside our database but the next problem that we have we didn't implement our cancel button which actually means here is our form to update a record and we're hitting here cancel and nothing is happening this is where we must jump inside our comment form component and here is our cancel button and as you can see here we don't have any click actually we must provide here a click event for example handle cancel dot emit and we don't need to provide anything here it will be just an empty call and we must create now inside our output so i will copy paste our handle submit and change it to handle cancel and we're providing inside void which means nothing now we can work with handle cancel outside inside our command this is why let's open our comment and we just need to change our comment form here because hash cancel button is only here we must add after handle submit handle cancel and what we want to do here we just want to change active comment back to now and this is exactly that case this is why here we can simply call set active command emit and we're just providing null insight this is it let's check this out here i'm reloading the page i'm hitting enter and i'm hitting cancel and as you can see we don't see our edit form anymore and the last feature that we must implement is deleting of the comment this is why first of all i want to jump inside our service so let's create deleting of the comment and actually it is really easy because we simply delete a comment by id and we don't need to do anything else this is why here we have our delete comment function and here we're getting id as an argument and back we're getting just observable with empty object so we're not getting a comment back this is just an empty object now here what i want to write is return this http client and here is delete method and what we want to pass inside actually we want to do exactly the same like in update this is the full url that we're using to remove the record so here we have slash comments slash and our id and we don't need to do anything here or provide anything because by default it will return an object so our service is ready now we must create this function as an output inside our comment so actually here inside our comment inside html we have delete button so what we want to do here we want to add a new click event and here we will call our delete comment and this will be for us just an output this is right here dot emit and we must provide what id we want to remove in our case comment dot id and this is it but we should not forget to also pass this delete comment inside our child in other case we can't remove our children this is why here we're providing delete comment method and here is delete comment emit and inside we're providing dollar event now we must register this output here on the top so here we can write that we have an output of the name delete comment and we simply assign here a new event emitter and we are passing inside just an id which actually means this is a string now we must register this output on our parent this is why we are jumping inside comments inside html and here let's add our delete comment and we will call delete comment function where we're providing our stream now let's jump inside comment yes and create here delete method so actually inside we just get a single comment id which is actually a string and we're getting back void now we want to call here our this comment service to remove the comment and here is our delete comment method where inside we're providing our comment id and now here we want to subscribe to the result we don't care what we're getting back and we just want to remove this comment from the list of our comments this is why we can use filter here we can write these comments equals and here we're just filtering all our comments we're getting access to every single comment and we want to check that comment id does not equal the comment id that we want to remove in this case we're saving all our comments that we have except of this specific comment let's check if it's working i'm reloading the page and as you can see here we have a delete button i'm hitting here delete and this comment is gone and as you can see here was our request inside network so this is the delete request to slash comments and this is the id of our specific comment which actually means we successfully implemented nested comments inside angular and the code was really dry and reusable and you can use it in almost every project but as you can see it is really a complex feature it is not easy to implement it is difficult to render a nested list of comments and you need to be aware of limitations that you have with recursively rendered components and actually if you want to see how i build exactly the same project with react don't forget to check this video also you
Info
Channel: Monsterlessons Academy
Views: 11,173
Rating: undefined out of 5
Keywords: monsterlessons academy, angular building comments application, angular building comments, comments in angular, angular comments system, angular comment section, angular comments section, angular comment reply component, angular comment form, angular comment library, angular comment reply
Id: _DACuv_xYCs
Channel Id: undefined
Length: 78min 1sec (4681 seconds)
Published: Thu Mar 03 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.