Edin Osmic
Edin Osmic

Reputation: 446

Nuxtjs document is not defined

I am trying to create reviews page in nuxtjs, and i am using vue-star-rating library to achieve this but i am facing this error even i tried a few solution that seemed to work for many people. Here is my script:

<template>
  <!--MAIN-->
  <main>
    <!--REVIEW ADDRESS-->
    <div class="reviewPage mt-3">
      <div class="container-fluid c-section">
        <div class="row">
          <div class="col-sm-2"></div>
          <div class="col-sm-8">
            <div class="a-spacing-top-medium">
              <h1 class="a-spacing-base">
                <b>Create Review</b>
              </h1>
              <div class="row">
                <!-- Product Photo -->
                <div class="col-md-2 col-sm-3 col-3">
                  <img :src="product.image" style="width: 80px" />
                </div>
                <!-- Product Title -->
                <div class="col-md-10 col-sm-9 col-9 m-auto">
                  <h4>
                    <b>{{ product.title }}</b>
                  </h4>
                </div>
              </div>
              <div class="a-spacing-top-medium"></div>
              <hr />
              <h2 class="a-spacing-base">Overall Rating</h2>
              <div class="a-row">
                <!-- Rating -->
                <!-- Get rid of document is not defined error -->
                <client-only>
                  <star-rating v-model="rating"></star-rating>
                </client-only>
              </div>
              <div class="a-row a-spacing-top-large">
                <h2>Add photo or video</h2>
                <p style="font-size: 14px; font-weight: 700;">
                  Shoppers find images and videos more helpful than text alone.
                </p>
              </div>
              <div class="a-row a-spacing-top-medium">
                <!-- Choose a Photo -->
                <label class="choosefile-button">
                  <i class="fal fa-plus"></i>
                  <input
                    type="file"
                    ref="file"
                    v-on:change="handleImageUpload"
                  />
                </label>
                <p>{{ imageName }}</p>
              </div>
              <div class="a-spacing-top-large"></div>
              <hr />
              <!-- Headline -->
              <div class="headline a-spacing-large">
                <h2 class="a-spacing-base">Add a headline</h2>
                <input
                  type="text"
                  class="a-input-text"
                  style="width: 70%;"
                  placeholder="What's most important to know?"
                  v-model="headline"
                />
              </div>
              <!-- Body -->
              <div class="a-spacing-base">
                <h2 class="a-spacing-base">Write your review</h2>
                <textarea
                  placeholder="What do you like or dislike? What did you see this product for?"
                  style="height:6em; width: 100%;"
                  v-model="body"
                ></textarea>
              </div>
            </div>
            <br />
            <br />
            <hr />
            <div class="a-spacing-top-medium">
              <p style="font-size: 14px; font-weight: 700;">
                This is how you'll appear to other customers:
              </p>
              <div class="media a-spacing-top-large">
                <div class="media-left">
                  <img
                    src="/img/avatar.png"
                    class="img-fluid"
                    style="width: 50px;"
                  />
                </div>
                <div class="media-body pl-3 pt-2">
                  <input
                    type="text"
                    class="a-input-text"
                    style="width: 100%;"
                    :value="$auth.$state.user.name"
                  />
                </div>
              </div>
            </div>
            <div class="a-row a-spacing-top-medium">
              <span class="a-color-tertiary"
                >Don't worry, you can always change this on your profile</span
              >
            </div>
            <div class="a-row text-right a-spacing-top-large">
              <span class="a-button-register">
                <span class="a-button-inner">
                  <span class="a-button-text" v-on:click="submitForm"
                    >Submit</span
                  >
                </span>
              </span>
            </div>
          </div>
          <div class="col-sm-2"></div>
        </div>
        <div class="a-spacing-large pb-5"></div>
        <hr />
      </div>
    </div>
    <!--/REVIEW ADDRESS-->
  </main>
  <!--/MAIN-->
</template>

<script>
import StarRating from "vue-star-rating";
export default {
  components: {
    StarRating
  },
  async asyncData({ $axios, params }) {
    try {
      const response = await $axios.$get(`/api/products/${params.id}`);
      console.log(response);
      return { product: response.product };
    } catch (error) {
      console.log(error);
    }
  },
  data() {
    return {
      headline: "",
      body: "",
      rating: 0,
      image: "",
      imageName: "",
      product: "",
      user: ""
    };
  },
  methods: {
    handleImageUpload(e) {
      this.image = this.$refs.file.files[0];
      console.log(this.image);
      this.imageName = e.target.files[0].name;
    },
    async submitForm() {
      try {
        // Initialize the form data
        let formData = new FormData();
        // Add the form data we need to submit
        formData.append("headline", this.headline);
        formData.append("body", this.body);
        formData.append("image", this.image, this.imageName);
        formData.append("rating", this.rating);
        formData.append("product", this.product);
        formData.append("user", this.user);
        //   Make the request to the POST http://localhost:8080/api/reviews/product/:id URL
        await this.$axios.$post(
          `/api/reviews/product/${this.$route.params.id}`,
          formData
        );
        // Redirect to home page
        this.$router.push(`/products/${this.$route.params.id}`);
      } catch (error) {
        console.log(error);
      }
    }
  }
};
</script>

I installed this vue-star-rating library and as you can see i am importing it as StarRating, and in template i am wrapping it with 'client-only' tag as suggested, previously i used no-ssr but it doesnt work either as someone said client-only is suggested for newest nuxt version. So i even tried to create folder in plugins with this content

import Vue from 'vue'
import StarRating from 'vue-star-rating'
Vue.use(StarRating)

and i also added this line to nuxt.config.js as suggested

{ src: '~/plugins/vue-star-rating.js', mode: 'client' }

but still error is persisting

Here is my review.js script:

const router = require('express').Router()
const Review = require('../models/review')
const Product = require('../models/product')
const verifyToken = require('../middlewares/verify-token')
const upload = require('../middlewares/upload-photo')

router.post('/reviews/:productID', [verifyToken, upload.single('photo')], async(req, res) => {
    try{
        const review = new Review()

        review.headline = req.body.headline
        review.body = req.body.body
        review.rating = req.body.rating
        review.photo = req.file.location
        review.productID = req.params.productID
        review.user = req.decoded._id

        await Product.update({ $push: review._id })

        const savedReview = await review.save()

        if(savedReview) {
            res.json({
                success: true,
                message: 'Succesfully added review'
            })
        }
    } catch(err) {
        res.status(500).json({
            success: true,
            message: err.message
        })
    }
})

router.get('/reviews/:productID,', async(req, res) => {
    try{
        const productReviews = await Review.find({
            productID: req.params.productID
        }).populate('user').exec()

        res.json({
            success: true,
            reviews: productReviews
        })
    } catch(err) {
        res.status(500).json({
            success: false,
            message: err.message
        })
    }
})
module.exports = router

Upvotes: 0

Views: 2870

Answers (1)

kissu
kissu

Reputation: 46596

A wrote a similar answer here but it was lacking some parts, so I feel like I can answer here too.

Your library is probably in the same state as vue2-editor, meaning that you need to load & import it only on the client. For that, you could use something like this

export default {
  components: {
    [process.browser && 'star-rating']: () => import('vue-star-rating'),
  }
}

Upvotes: 1

Related Questions