Implement Quick Blog Comments in VueJS
Tags: javascript, vue, appwrite, webdev
Published: 2022-04-12 09:00:15

Blogs share information via written pieces of content. Blog comments can drive engagement or solicit feedback depending on the content domain.

This post teaches how to add a comment section to blog posts. We will be able to add, view, edit, and delete each comment. Also important to note is that we don’t need a custom backend server to implement this feature.

For the complete source code, check out this [GitHub repository](https://github.com/Terieyenike/appwrite-blog-comments-with-vue).

This project is completed and written in [Vue.js](https://vuejs.org/). Vue is an open-source JavaScript front-end framework for building user interfaces and single-page applications with HTML, CSS, and JavaScript.

View the final product and see the finished look

![blog comments](https://res.cloudinary.com/terieyenike/video/upload/v1648483659/blog-comments_-_28_March_2022_qgrwmy.gif)

## Prerequisites

To complete the tutorial, you need the following:

- Basic JavaScript knowledge
- Install Docker Desktop on your local machine. Run this command `docker -v` to confirm if you have it installed; otherwise, install it through the docker [products](https://www.docker.com/products/docker-desktop/) page
- Knowledge of Vue.js
- A locally running Appwrite instance. Follow this [documentation](https://appwrite.io/docs/installation/?utm_source=hackmamba&utm_medium=blog&utm_campaign=vue-comments) to install it.

[Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=blog&utm_campaign=vue-comments) is an open-source backend-as-a-service platform that allows developers to build modern backend APIs faster. Appwrite abstracts the complexities involved with building and managing complex backend services. We don’t require a custom server for this project, and we focus on building the interface.

## Getting Started with Vue

We use the Vue CLI terminal command to scaffold a new project having Vue 3, Babel, and ESLint.

    vue create blog-comments

After installation, we change the directory and start a local development server on `localhost:8080` using:

```
cd blog-comments && yarn serve

# or

cd blog-comments && npm run serve
```

## Creating a blog page

We import the `src/components/Blog.vue` component in the `App.vue` file in the project folder.

components/Blog.vue
[https://gist.github.com/Chuloo/68921c6b9729ca2da7ee03fab1dab60c](https://gist.github.com/Chuloo/68921c6b9729ca2da7ee03fab1dab60c)

The code above gives us a page containing a text body, post title, and publisher name, which occurs with the stored blog data as state variables in vue.

We also added scoped styles using the `<style>` tags for the component.

Next, we import the Blog component to the root component `App.vue`.

```js
<template>
  <div class="container">
    <Blog />
  </div>
</template>

<script>
import Blog from '@/components/Blog.vue';

export default {
  name: 'App',
  components: {
    Blog,
  }
</script>

<style>
  [component styles go in here]
</style>
```

Here’s the complete code snippet of the home page.

[https://gist.github.com/Chuloo/c6d23d0b7503228b553df180067c2e43](https://gist.github.com/Chuloo/c6d23d0b7503228b553df180067c2e43)

The blog comments page looks like this now:

![home page text](https://paper-attachments.dropbox.com/s_D7CBE305335ABA15AC2578F5A659EF2A343B08496D251D5A83784758220481C8_1648498428562_image.png)

## Creating the comment input forms

Next, we create input fields with which readers can enter comments.

In the components folder, we create a new file, `AddComment.vue`, with the following content:

```js
<template>
  <div class="form">
    <form>
      <div class="form__group">
        <label>Leave a comment</label>
        <textarea
          v-model="newComment"
          rows="10"
          required
          cols="50"
          placeholder="type in your comment"
        />
        <button>Submit</button>
      </div>
    </form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      newComment: '',
    };
  },
};
</script>

<style scoped>
.form {
  margin-top: 1.5em;
}

label {
  display: block;
  margin-bottom: 1em;
  font-weight: 700;
  font-family: Padauk, sans-serif;
}

textarea {
  width: 100%;
  margin-top: 0.5em;
}

button {
  border: unset;
  background: #79b791;
  color: #230c0f;
  font-weight: 700;
  padding: 1em 2.5em;
  margin-top: 1em;
}
</style>
```

The code above has a data property `newComment` set to an empty string. This property is bound to the `<textarea>` field using the v-model directive.

With this in place, let's import the `AddComment` component into the `App.vue` component with the following code:

```js
<template>
  <div class="container">
    <Blog />
    <!-- add this -->
    <add-comment />
  </div>
</template>

<script>
// other import component
import AddComment from '@/components/AddComment.vue'; // add this
export default {
  name: 'App',
  components: {
    // blog component
    AddComment // add this
  }
</script>
<style>
/* styles for the page */
</style>
```

The result of the blog should look like this:

![text input area blog](https://paper-attachments.dropbox.com/s_D7CBE305335ABA15AC2578F5A659EF2A343B08496D251D5A83784758220481C8_1648510552616_image.png)

Next, we will list all comments created under a post. We will also include options to update or delete a comment.

We create a file, `Comment.vue`, in the components folder with the following content:

```js
<template>
  <div class="comment">
    <div class="comment__flex">
      <p>I found this article helpful</p>
      <div class="comment__flex-btn">
        <button class="update">Update</button>
        <button class="del">Delete</button>
      </div>
    </div>
  </div>
</template>

<style>
[Styles go in here]
</style>
```

You can see the complete component with styles in [this gist](https://gist.github.com/Chuloo/0edc8d42d8c69221b6cac39eafa93204).

[https://gist.github.com/Chuloo/0edc8d42d8c69221b6cac39eafa93204](https://gist.github.com/Chuloo/0edc8d42d8c69221b6cac39eafa93204)

Finally, let's include the `Comment` component in the `App.vue` file.

```js
<template>
  <div class="container">
    <Blog />
    <!-- add this -->
    <div class="comment-space">
      <p>Comment (1)</p>
    </div>
    <div>
      <comment class="message" />
    </div>
    <add-comment />
  </div>
</template>

<script>
// other import component
import Comment from '@/components/Comment';

export default {
  name: 'App',
  components: {
    // other components
    Comment, // add this
  },
};
</script>
```

All the data on the page is static, and the page looks like the image below.

![blog](https://paper-attachments.dropbox.com/s_D7CBE305335ABA15AC2578F5A659EF2A343B08496D251D5A83784758220481C8_1648512532977_image.png)

Next, we’ll add interactivity to the page.

## Appwrite project setup

To use [Appwrite](https://appwrite.io?utm_source=hackmamba&utm_medium=blog&utm_campaign=vue-comments) in this Vue application, we install the Appwrite client-side SDK with the command.

    yarn add appwrite

    # or

    npm install appwrite

Next, let's set up the app by creating a new Appwrite project.

## Create a new Appwrite Project

We click the Create Project button to start a new project on our Appwrite web console (either local or hosted).

After that, we obtain the `Project ID` and `API Endpoint` in the project's settings tab.

In the project’s root directory, we create a utils.js file to define a new Appwrite instance and other helpful application variables.

```js
import { Appwrite } from 'appwrite';
// Init your Web SDK
const appwrite = new Appwrite();
appwrite
  .setEndpoint('http://EndpointURL.example') // Replace this with your endpoint
  .setProject('ProjectID'); // Replace this with your ProjectID

appwrite.account.createAnonymousSession().then(
  (response) => {
    console.log(response);
  },
  (error) => {
    console.log(error);
  }
);

export const db = appwrite.database;
export const COLLECTION_ID = 'COLLECTION ID'; // Replace with your Collection ID
```

To bypass some security requirements, we created an anonymous session on Appwrite.

## Creating the database collection

Appwrite provides a functional database with which we will store our blog comments.

To create a collection in our Appwrite console panel, we navigate to the Database tab, click the _Add Collection_ button, and provide a collection name. We copy the `CollectionID` as we require it to interact with the collection using the client-side SDK.

At the `Collection Level` within the `settings` tab, we set the Read and Write access to have the value of `role:all`.

![blog](https://paper-attachments.dropbox.com/s_D7CBE305335ABA15AC2578F5A659EF2A343B08496D251D5A83784758220481C8_1648518488594_image.png)

Update the data in `utils.js` to include the Appwrite credentials obtained.

Finally, in the attributes tab, let's create the properties for our documents. For this application, we store the comment and date of each comment.

![blog comment](https://paper-attachments.dropbox.com/s_D7CBE305335ABA15AC2578F5A659EF2A343B08496D251D5A83784758220481C8_1648518852144_image.png)

## Fetch all comments

We require a function to fetch all comments when the app loads. We do this in the `script` portion of `App.vue` with:

```js
<script>
import { COLLECTION_ID, db } from '@/utils';

export default {
  name: 'App',
  components: {
    // all components
  },
  created() {
    this.fetchComments();
  },
  data() {
    return {
      comments: [],
    };
  },
  methods: {
    fetchComments() {
      let promise = db.listDocuments(COLLECTION_ID);
      promise.then(
        (res) => {
          console.log(res);
          this.comments = res.documents;
        },
        (err) => {
          console.log(err);
        }
      );
    },
  },
};
</script>
```

We created the comments array in the `data()` function to store comments we retrieve using the `listDocuments` API.

In the `created()` lifecycle method, run the `fetchComments()` function when the App component is created.

We update the HTML in the `<template>` section of the `App.vue` component with the following.

```js
<template>
  <div class="container">
    <Blog />
    <div class="comment-space">
      <p>
        {{
          comments.length > 1
            ? `Comments (${comments.length})`
            : comments.length == 1
            ? `Comment (${comments.length})`
            : ''
        }}
      </p>
    </div>
    <div v-for="data in comments" :key="data.comment">
      <!-- comment component goes here -->
    </div>
    <add-comment :fetchComments="fetchComments" />
  </div>
</template>
```

The `<p>` tag contains a ternary operator that shows the number of comments. Using the `v-for` directive, we loop through and render each comment.

To reuse the function to fetch all comments after creating a new comment, we bind the `:fetchComments` prop to the fetchComments method we defined earlier.

## Creating a blog comment

We move to the `AddComment.vue` file to handle a comment’s addition to the database.

```js
<template>
  <div class="form">
    <form @submit.prevent="createNewComment">
      <div class="form__group">
        <label>Leave a comment</label>
        <textarea
          v-model="newComment"
          rows="10"
          required
          cols="50"
          placeholder="type in your comment"
        />
        <button>Submit</button>
      </div>
    </form>
  </div>
</template>

<script>
import { COLLECTION_ID, db } from '@/utils';

export default {
  props: ['fetchComments'],
  // data ()
  methods: {
    createNewComment() {
      if (this.newComment === '') {
        return;
      }
      let promise = db.createDocument(COLLECTION_ID, 'unique()', {
        comment: this.newComment,
        date: new Date(),
      });
      promise.then(
        () => {
          this.fetchComments();
          this.newComment = '';
        },
        (err) => {
          console.log(err);
        }
      );
    },
  },
};
</script>
```

In the createNewComment method, we use Appwrite’s createDocument API to write a new comment to the database. An error message is logged if the write operation fails. We fetch an updated list of all comments after adding a new comment.

The Appwrite web console will display one document representing a comment in the image below:

![blog](https://paper-attachments.dropbox.com/s_D7CBE305335ABA15AC2578F5A659EF2A343B08496D251D5A83784758220481C8_1648544339183_image.png)

## Updating the Comment list Component

In the App.vue component, we update the `comment` component’s props to include the comment data and the `fetchComments` method.

```js
<template>
  <div class="container">
    <-!-- Blog component --> <-!-- Comment count -->
    <div v-for="data in comments" :key="data.comment">
      <-!-- add this -->
      <comment class="message" :data="data" v-on:refreshData="fetchComments" />
    </div>
    <-!-- add-comment component -->
  </div>
</template>

<script>
// import component
import Comment from '@/components/Comment';

export default {
  components: {
    // other components
    Comment,
  },
};
</script>
```

`fetchComments` runs once the `refreshData` event is fired.

Let's update the `Comment.vue` component to handle comment updates and deletion. We will also include a component to edit a comment. First, we add the update comment function in the script portion with:

```js
<script>
import { db } from '@/utils';
export default {
  props: ['data'],
  data() {
    return {
      open: false,
      displayedComment: '',
    };
  },
  methods: {
    updateComment() {
      this.open = !this.open;
    },
    updateCommentMethod() {
      let promise = db.updateDocument(this.data.$collection, this.data.$id, {
        comment: this.displayedComment,
      });
      this.open = false;
      promise.then(
        () => {
          this.$emit('refreshData');
        },
        (err) => {
          console.log(err);
        }
      );
    },
  },
};
</script>
```

We added a state variable to manage the visibility of a comment’s action buttons and another variable to hold the new text. Appwrite’s `updateDocument` API uses the collection ID and document ID passed as props to update the comment. Once the comment is updated, we emit the `refreshData` event to fetch all comments.

We update the template portion to utilize the methods and variables created.

```js
<template>
  <div class="comment">
    <div class="comment__flex">
      <p>{{ data.comment }}</p>
      <div class="comment__flex-btn">
        <button class="update" @click="updateComment">Update</button>
        <button class="del" @click="deleteComment">Delete</button>
      </div>
    </div>
    <div v-if="this.open" class="open">
      <form @submit.prevent="updateCommentMethod">
        <div class="form-group">
          <textarea
            cols="50"
            rows="10"
            id
            required
            :placeholder="data.comment"
            v-model="displayedComment"
          />
          <button class="update">Update</button>
        </div>
      </form>
    </div>
  </div>
</template>
```

Lastly, we add a method to delete a comment using Appwrite’s `deleteDocument` API.

```js
<script>
export default {
  methods: {
    deleteComment() {
      let promise = db.deleteDocument(this.data.$collection, this.data.$id);
      promise.then(
        () => {
          this.$emit('refreshData');
        },
        (err) => {
          console.log('error occured', err);
        }
      );
    },
  },
};
</script>
```

The image below is the final look of the web page.

![blog](https://paper-attachments.dropbox.com/s_D7CBE305335ABA15AC2578F5A659EF2A343B08496D251D5A83784758220481C8_1648552941268_image.png)

## Conclusion

This post is an in-depth guide on using [Appwrite](https://appwrite.io/?utm_source=hackmamba&utm_medium=blog&utm_campaign=vue-comments) to create, display, edit, and delete blog comments. As seen, this feature doesn’t require a custom backend server. Try to add more blog posts and create unique comments for each.

## Learn More

- [Appwrite CRUD operations](https://appwrite.io/docs/client/database?utm_source=hackmamba&utm_medium=blog&utm_campaign=vue-comments)
- [Getting started with Appwrite](https://appwrite.io/docs/getting-started-for-web?utm_source=hackmamba&utm_medium=blog&utm_campaign=vue-comments)
- [Appwrite SDKs](https://appwrite.io/docs/sdks?utm_source=hackmamba&utm_medium=blog&utm_campaign=vue-comments)