Our Comments component will hold the list of all the comments, and its view will create a Comment component for each comment in that list. Each Comment component will use an Editor component to provide in-place editing of its content. These editors work autonomously using their own controls, and they emit an event if the content is changed or altered in any way. To take care of this, we need to re-emit this event with the name commentEdited from the Comment component. Now we only need to catch this event within our Comments component in order to update the list of comments with the changes. This is illustrated in the following part of the code:

onCommentEdited(comment, content) {
  const comments = this.comments.slice();
  if (content.length === 0) {
    comments.splice(comments.indexOf(comment), 1);
  } else {
    comments.splice(comments.indexOf(comment), 1, {
      user: comment.user,
      time: comment.time,
      content
    });
  }
  this.commentsUpdated.next(comments);
}

This method will be called for each individual Comment component that is repeated using the NgFor directive. From the view, we pass a reference to the comment object concerned, as well as the edited content we would receive from the Comment component event.

The comment object will only be used to determine the position of the updated comment within the comment list. If the new comment content is empty, we will remove the comment from the list. Otherwise, we will just create a copy of the previous comment object, change the content with the new edited content, and replace the old comment object in the list with the copy.

Finally, since we wanted to communicate the change in the comment list, we emitted an event using the commentUpdated output property.

With this, we have completed our commenting system, and now it’s time to make use of it. We already have an empty tab prepared for our project comments, and this is going to be the spot where we will add commenting capabilities using our commenting system.

First, let’s amend our Project component template, project/project.html, to include the commenting system:

...
<ngc-tabs>
  <ngc-tab name="Tasks">...</ngc-tab>
  <ngc-tab name="Comments">
    <ngc-comments [comments]="comments"
                  (commentsUpdated)="updateComments($event)">
    </ngc-comments>
  </ngc-tab>
  <ngc-tab name="Activities"></ngc-tab>
</ngc-tabs>

This is as easy as it gets. Since we are paying attention to a clean component architecture, the inclusion of our commenting system really works like a breeze. The only thing we now need to ensure is that we provide a property on our project with a list of comments. We also need a way to react to changes if comments are updated within our Comments component. For this purpose, we will create an updateComments method.

Let’s look at the component class changes within the project/project.js file:

export class Project {
  ...
  @Input() comments;
  ...
    updateComments(comments) {
      this.projectUpdated.next({
        comments
      });
  }
}

Since we are already dealing with project updates in a general way and our Project component is emitting directly to our App component, where projects data will be persisted, the only thing we need to implement is an additional input property, as well as a method to handle comment updates

 

Recap

Within this Tutorial , we have successfully created a full-fledged commenting system that can be placed in various areas of our application to enable commenting. Users can interact with in-place editors to edit the content in comments, which gives them a great user experience.

While writing the code for our commenting system, we learned about the following topics:

  1. Implementing a simple pipe using the @Pipe annotation and the Moment.js library to provide relative time formatting
  2. Using the OnChanges life cycle hook to prevent unwanted or invalid values within input properties
  3. Using @ViewChild to obtain a reference to the components within the component sub-tree in order to establish direct communication
  4. Reusing the Editor component as an input field replacement and as an autonomous in-place editor within the Comment component