How to render contents based on the params received from vue-router?

Refresh

4 weeks ago

Views

5 time

0

I am using Vue 3.6.3 to build a film list page. The home page shows a list of films, only includes the brief information of each film, and the user can click the title of the header of each film to enter a page where there is detail information about the film. I use the vue-router to help me with the transition.

The root component is App.vue, actually it does nothing but showing its child components.

the important codes in App.vue:

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

The router is mounted on App.vue, I created a routers.js file to define the routes, and then make a router in main.js.

codes from routers.js:

import App from './App.vue'
import FilmList from './components/FilmList.vue'
import DetailPage from './components/DetailPage.vue'

const routers = [{
    path: '/',
    redirect: '/filmlist',
    component: App,
    children: [{
            path: '/filmlist',
            name: 'filmlist',
            component: FilmList
        },
        {
            path: '/detail/:id',
            name: 'detail',
            component: DetailPage
        }
    ]
}]
export default routers

codes from main.js:

import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import routes from './routers'

Vue.use(VueRouter)
Vue.config.productionTip = false

const router = new VueRouter({
    mode: 'history',
    routes: routes
})

new Vue({
    router: router,
    render: h => h(App),
}).$mount('#app')

The App.vue component has two children, FilmList and the DetailPage. At first, FilmList will be shown, after the user click one of them, it will jump to a detail page of a certain movie based on the id param in the router. After receiving the id param, the DetailPage.vue ought to select one of the movies from the film list and display its detailed contents.

codes from the FilmList.vue:

<template>
  <div id="filmlist">
    <film-brief
      v-for="(film, index) in filmList[pageIndex-1]"
      v-bind:key="film._id"
      v-bind:index="index + groupCount*(pageIndex-1)"
      v-bind:film="film"
    ></film-brief>
    <div class="buttons">
      <jump-button
        class="button"
        v-for="index in buttonIndexList"
        :key="index.id"
        v-bind:index="index"
        v-on:jump-page="pageIndex = index.id"
      ></jump-button>
    </div>
  </div>
</template>

<script>
import FilmBrief from "../components/FilmBrief.vue";
import films from "../assets/films.json";
import JumpButton from "../components/JumpButton.vue";

const GroupCount = 10;

export default {
  name: "FilmList",
  data: function() {
    return {
      films,
      pageIndex: 1,
      pageCount: Math.ceil(films.length / GroupCount),
      groupCount: GroupCount
    };
  },
  components: {
    FilmBrief,
    JumpButton
  },

......

codes from DetailPage.vue:

<template>
  <div class="detail-page">
    <div class="poster">
      <img :src="this.imgSrc">
    </div>
    <div class="details">
      <ul>
        <li id="title" v-html="title"></li>
        <li id="director">{{directors}}</li>
        <li id="writers">{{writers}}</li>
        <li id="casts">{{casts}}</li>
        <li id="genres">{{genres}}</li>
        <li id="duration">{{duration}}</li>
        <li id="pubdate">{{pubdate}}</li>
        <li id="summary"></li>
      </ul>
    </div>
  </div>
</template>

<script>
import errorImg from "../assets/imgErr.jpg";
import films from "../assets/films.json";

export default {
  name: "DetailPage",
  props: [],
  data: function() {
    return {
      id_: this.$route.params.id,
      film: films[id_],
      imgSrc: "",
      imgErrSrc: errorImg,
      duration: "片长:" + this.film.duration + "分钟",
      summary: "简介:" + this.film.summary
    };
  },
  methods: {
    loadImg(resolve, reject) {
      let originImgSrc = this.film.poster;
      let img = new Image();
      img.src = originImgSrc;

      img.onload = function() {
        resolve({
          src: img.src
        });
      };

      img.onerror = function(e) {
        reject(e);
      };
    },
    getList(name, list) {
      let result = "";
      result += name;
      for (let i = 0; i < list.length; i++) {
        result += list[i].name;
        if (i !== list.length - 1) {
          result += " / ";
        }
      }
      return result;
    }
  },
  created: function() {
    this.loadImg(
      response => {
        this.imgSrc = response.src;
      },
      reject => {
        console.log("图片加载失败");
        this.imgSrc = this.imgErrSrc;
      }
    );
  },
  computed: {
    title: function() {
      let originalTitle = "";
      originalTitle += this.film.title;
      let index = originalTitle.indexOf(" ");
      let input = originalTitle[index - 1];
      let isPunc =
        (input >= "A" && input <= "Z") || (input >= "a" && input <= "z");
      if (!isPunc) {
        return (
          originalTitle.slice(0, index) +
          "<br>" +
          originalTitle.slice(index + 1)
        );
      } else {
        return originalTitle;
      }
    },
    directors: function() {
      return this.getList("导演:", this.film.directors);
    },
    writers: function() {
      return this.getList("编剧:", this.film.writers);
    },
    casts: function() {
      return this.getList("主演:", this.film.casts);
    },
    genres: function() {
      let genres = this.film.genres;
      let str = "";
      for (let i = 0; i < genres.length; i++) {
        str += genres[i];
        if (i !== genres.length - 1) {
          str += " / ";
        }
      }
      return str;
    },
    pubdate: function() {
      let dates = this.film.pubdate;
      if (dates[0] === "") {
        return "上映时间未知";
      } else {
        return "上映时间:" + dates[0];
      }
    }
  }
};
</script>

After clicking, the route shown in the browser is OK, like "http://localhost:8080/detail/11", however, the page shows nothing, and the error in the console is something like:

[Vue warn]: Error in data(): "ReferenceError: id_ is not defined"

found in

---> <DetailPage> at src/components/DetailPage.vue
       <App> at src/App.vue... (1 recursive calls)
ReferenceError: id_ is not defined
    at VueComponent.data (DetailPage.vue?b507:31)
    at getData (vue.runtime.esm.js?2b0e:4742)
    at initData (vue.runtime.esm.js?2b0e:4699)
    at initState (vue.runtime.esm.js?2b0e:4636)
    at VueComponent.Vue._init (vue.runtime.esm.js?2b0e:5000)
    at new VueComponent (vue.runtime.esm.js?2b0e:5148)
    at createComponentInstanceForVnode (vue.runtime.esm.js?2b0e:3283)
    at init (vue.runtime.esm.js?2b0e:3114)
    at merged (vue.runtime.esm.js?2b0e:3301)
    at createComponent (vue.runtime.esm.js?2b0e:5972)
[Vue warn]: Error in created hook: "TypeError: Cannot read property 'poster' of undefined"

found in

---> <DetailPage> at src/components/DetailPage.vue
       <App> at src/App.vue... (1 recursive calls)
         <Root>

and many other warnings like that.

Later I assume that maybe it's because the id param should be loaded before other attributes get access to it, so I add it to the created in DetailPage.vue like:

`created: function() {
    this.film = films[this.$route.params.id];
    this.loadImg(
      response => {
        this.imgSrc = response.src;
      },
      reject => {
        console.log("图片加载失败");
        this.imgSrc = this.imgErrSrc;
      }
    );
  },

Now some information can be loaded, but some other cannot, like the poster and the duration. The warnings include:

vue.runtime.esm.js?2b0e:619 [Vue warn]: Error in data(): "TypeError: Cannot read property 'duration' of undefined"

found in

---> <DetailPage> at src/components/DetailPage.vue
       <App> at src/App.vue... (1 recursive calls)
         <Root>
TypeError: Cannot read property 'duration' of undefined
    at VueComponent.data (DetailPage.vue?b507:34)
    at getData (vue.runtime.esm.js?2b0e:4742)
    at initData (vue.runtime.esm.js?2b0e:4699)
    at initState (vue.runtime.esm.js?2b0e:4636)
    at VueComponent.Vue._init (vue.runtime.esm.js?2b0e:5000)
    at new VueComponent (vue.runtime.esm.js?2b0e:5148)
    at createComponentInstanceForVnode (vue.runtime.esm.js?2b0e:3283)
    at init (vue.runtime.esm.js?2b0e:3114)
    at merged (vue.runtime.esm.js?2b0e:3301)
    at createComponent (vue.runtime.esm.js?2b0e:5972)

So, why is that and how can I get all the information displayed?

Apologize if my poor English troubles you, and thanks in advance!

0 answers