Как сделать так, чтобы дочерние компоненты имели доступ к методу родительских компонентов в VueJS

#javascript #html #vue.js #vuejs2

#javascript #HTML #vue.js #vuejs2

Вопрос:

У меня есть кнопка, которая выполняет проверку формы, которая находится в моем родительском компоненте. Эта кнопка в случае успеха отображает дочерний компонент, который вызывает API. Проблема в том, что когда я меняю ввод, отображение меняется одновременно. Я пытаюсь использовать функцию animeFind в родительском компоненте, чтобы мой дочерний компонент просто заботился о пользовательском интерфейсе и знал, какие данные отображать (т.Е. Иногда response.data[«title»] или response.data[«image_url»].

Родительский компонент

 <template>
  <section class="hero">
    <div class="parent-1">
      <h1 class="title is-1">Compare two animes! :)</h1>
    </div>

    <div class="columns">
      <div class="column">
        <b-field class="label" label="Anime 1">
          <b-input value="Enter the first anime!" v-model="anime1"></b-input>
        </b-field>
      </div>
      <div class="column">
        <b-field class="label" label="Anime 2">
          <b-input value="Enter the second anime!" v-model="anime2"></b-input>
        </b-field>
      </div>
    </div>
    <div class="button-spacing">
      <b-button class="button" type="is-primary" @click="checkComplete"
        >Compare!</b-button
      >
    </div>

    <Info :anime1="anime1" :anime2="anime2" v-if="success"></Info>
  </section>
</template>

<script>
import Vue from "vue";
import Buefy from "buefy";
import "buefy/dist/buefy.css";
import Info from "./Info.vue";
Vue.use(Buefy);

export default {
  components: {
    Info,
  },
  data() {
    return {
      anime1: "",
      anime2: "",
      success: false,
    };
  },

  methods: {
    checkComplete() {
      if (this.anime1.length > 0 amp;amp; this.anime2.length > 0) {
        this.success = true;
        return this.$buefy.toast.open({
          message: "Yay, just a moment now!",
          type: "is-success",
          position: "is-bottom",
          duration: 3000,
        });
      }
      this.success = false;
      return this.$buefy.toast.open({
        duration: 3000,
        message: `Please fill out both fields`,
        position: "is-bottom",
        type: "is-danger",
      });
    },
  },
};
</script>
  

Дочерний компонент

 <template>
  <section>
    <img :src="url1" alt="./assets/notFound.png" />
    <img :src="url2" alt="./assets/notFound.png" />
  </section>
</template>

<script>
import axios from "axios";

export default {
  props: {
    anime1: String,
    anime2: String,
  },
  data() {
    return {
      url1: "",
      url2: "",
      error: "",
    };
  },
  methods: {
    animeFind(anime, data) {
      axios
        .get(`https://api.jikan.moe/v3/search/anime?q=${anime}`)
        .then((response) => {
          const id = response.data["results"][0]["mal_id"];
          axios
            .get(`https://api.jikan.moe/v3/anime/${id}`)
            .then((response) => (this[data] = response.data["image_url"]));
        })
        .catch((error) => {
          this.error = error; // take care of this later
        });
    },
  },

  watch: {
    anime1: {
      immediate: true,
      // eslint-disable-next-line no-unused-vars
      handler(newVal, oldVal) {
        this.animeFind(newVal, "url1");
      },
    },
    anime2: {
      immediate: true,
      // eslint-disable-next-line no-unused-vars
      handler(newVal, oldVal) {
        this.animeFind(newVal, "url2");
      },
    },
  },
};
</script>
  

Комментарии:

1. Чтобы было понятно. Вы хотите перейти animeFind к родительскому компоненту и checkComplete функции when returns true ; вам нужно вызвать animeFind и отправить запрос с помощью axios, ЕСЛИ axios выдает успешный ответ, вам нужно передать response.data дочерней функции и выполнить рендеринг. Я прав?

2. Это правильно

Ответ №1:

Примечание: я закомментировал запрос axios и всегда возвращал URL-адрес изображения, чтобы этот пример работал здесь.

 Vue.component('compare-anim', {
  template: '#tmpl-compare-anim',
  data() {
    return {
      anime1: "",
      anime2: "",
      url1: "",
      url2: ""
    };
  },
  methods: {
    checkComplete() {
    
      if (this.anime1.length > 0 amp;amp; this.anime2.length > 0) {
        this.animeFind(this.anime1, 'url1');
        this.animeFind(this.anime2, 'url2');
      }
    },
    
    animeFind(anime, urlName) {
      var responseImg = this.makeRequest(anime);

      if (responseImg !== null) {
        this[urlName] = responseImg;
      }
    },
    
    makeRequest(anime) {
    
      //Not gonna make axios request; returning sample image url
      //in order to test this implementation
      return "https://homepages.cae.wisc.edu/~ece533/images/monarch.png";
      /**
      axios
      .get(`https://api.jikan.moe/v3/search/anime?q=${anime}`)
      .then((response) => {
        const id = response.data["results"][0]["mal_id"];
        axios
          .get(`https://api.jikan.moe/v3/anime/${id}`)
          .then((response) => ( 
            return response.data["image_url"]) 
          );
      })
      .catch((error) => {
        this.error = error; // take care of this later
        return null;
      });
      **/
    }
  }
});


Vue.component('render-anim', {
  template: '#tmpl-render-anim',
  props : {
    url1 : {
      default: function () {
        return "";
      }
    },
    url2 : {
      default: function () {
        return "";
      }
    }
  },
  methods: {

  }
});

new Vue({
  el: '#app',
});  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <compare-anim></compare-anim>
</div>


<template id="tmpl-compare-anim">
  <section class="hero">
    <h1 class="title is-1">Compare two animes! :)</h1>
    <input placeholder="Enter the first anime!" v-model="anime1" /><br/><br/>
    <input placeholder="Enter the second anime!" v-model="anime2" /><br/><br/>
    <button class="button" type="is-primary" @click="checkComplete">Compare!</button>
    
    <render-anim :url1="url1" :url2="url2"></render-anim>
  </section>
</template>

<template id="tmpl-render-anim">
  <section>
    <img :src="url1" v-if="url1" alt="./assets/notFound.png" width="100" />
    <img :src="url2" v-if="url1" alt="./assets/notFound.png" width="100" />
  </section>
</template>  

Комментарии:

1. Что делает идентификатор шаблона, гарантирует ли он, что переменные, объявленные в компоненте vue, доступны с любыми тегами, которые есть в шаблоне?

2. Нет. идентификатор шаблона предназначен для определения, какой шаблон принадлежит какому компоненту. вам не нужно добавлять их в свой код. это не имеет никакого отношения к каким-либо переменным компонентов

3. Есть ли способ модулировать этот код, потому что, если я начну добавлять больше компонентов vue, он станет действительно большим, как у меня будут render-anim и compare-anim в разных файлах? Или вы бы не рекомендовали мне делать это

4. вы можете добавлять их в разные файлы. это не влияет на реализацию. Я добавил это в тот же файл, потому что у меня не может быть здесь нескольких файлов, и я хотел создать фрагмент кода, пригодный для выполнения. Дайте мне знать, если у вас возникнут какие-либо проблемы

5. Я получаю сообщение об ошибке, в котором говорится TypeError: Cannot read property 'functional' of undefined