-2

I'm a beginner to Vue.js and wanted to do a project that used an API. This project was originally done by TraversyMedia using React but It seemed really simple enough to convert to Vuejs. My issue is I'm not sure how to search the API for specific actors. The search bar detects input but I have an issue querying the API.*

<template lang="">
    <div>
        <!-- Breaking Bad -->
        <Header></Header>
        <Search v-on:wordChange="onWordChange"></Search>
        <ActorList v-bind:characters="actorInfo"></ActorList>
        <!-- characters is the name of the property we want to show up in the child component -->
        <!-- {{ actorInfo.length }} -->
        <Actor></Actor>
    </div>
</template>
<script>
    import Header from "./components/Header";
    import Search from "./components/Search";
    import ActorList from "./components/ActorList";
    import Actor from "./components/Actor";
    import axios from "axios";

    export default {
        name: "App",
        data: function() {
            return { actorInfo: [] };
        },
        components: {
            Header,
            Search,
            ActorList,
            Actor
        },
        mounted() {
            axios
                .get("https://www.breakingbadapi.com/api/characters?name", {})
                .then((response) => {
                    this.actorInfo = response.data;
                    // this.datas = response.data;
                    // console.log(response.data);
                })
                .catch(function(error) {
                    console.log(error);
                });
            //This is how to get all data from the api endpoint with axios
        },
        methods: {
            onWordChange: function(searchTerm) {
                console.log(searchTerm);
            }
        }
    };
</script>
<style>
    * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }

    body {
        font-family: "Montserrat";
        font-size: 15px;
        color: white;
        background: rgb(0, 0, 0);
        background: linear-gradient(
            90deg,
            rgba(0, 0, 0, 1) 0%,
            rgba(2, 44, 23, 1) 33%,
            rgba(2, 44, 23, 1) 66%,
            rgba(0, 0, 0, 1) 100%
        );
    }
</style>

TajC
  • 35
  • 6
  • `mounted` doesn't return anything. `mounted` is never used. – evolutionxbox Dec 31 '20 at 16:42
  • I see, what should be used instead? Should I use Method instead? – TajC Dec 31 '20 at 16:45
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – evolutionxbox Dec 31 '20 at 16:47
  • Not exactly what I'm looking for. Bare in mind, I can display the contents of the API. I have images and data showing. I'm just not sure how to search for a specific actor and display that info – TajC Dec 31 '20 at 16:54

1 Answers1

0

The axios request you put in mounted only gets fired when the component is mounted to the DOM. That is only going to happen once which is why nothing is happening when you type.

I have two options I recommend:

Option 1: Query the API once on page load and search all records locally on the client-side.

Benefits of this approach is you're not querying the API on every keystroke, so very fast searching and also you have control over how the records are matched against your query. Downside to this approach is if new data is made available in the API, you need to refresh the page in order to see and query it. This looks like what you are trying to do from what I see.

If so, you could use a computed property to show the filtered results from a query while using this.actorInfo to contain the entire list of searchable records:

<template>
  <div>
    ...
    <Search v-on:wordChange="onWordChange"></Search>
    <ActorList v-bind:characters="filteredResults"></ActorList>
    ...
  </div>
</template>

<script>
  ...

  export default {
    ...
    data () {
      return {
        actorInfo: []
      }
    },
    mounted () {
      axios.get('https://www.breakingbadapi.com/api/characters?name')
        .then(response => {
          this.actorInfo = response.data
        })
    },
    computed: {
      filteredResults () {
        return this.actorInfo.filter(actor => {
          // Replace this return statement with your own custom "search" matching algorithm if you want
          return actor.toLowerCase().startsWith(this.searchTerm.toLowerCase())
        })
      }
    },
    methods: {
      onWordChange (searchTerm) {
        this.searchTerm = searchTerm
      }
    }
    ...
  }
</script>

Option 2: Query the API on every keystroke.

Benefit of this approach is new information that is made available by the API can be seen immediately. Downside is that you'll have some (probably) noticeable latency between each keystroke and you potentially risk getting rate limited (the server might block your requests temporarily because you're sending too many), and you don't really have control over how records are matched against your query.

To do this, you could create another method called search that takes a search term and then performs the HTTP request. To show all records on page load, you perform the search with no specified value in mounted and that should return everything (only for this specific API, other APIs could behave differently so just be mindful of that for the future). After that, every keystroke with send an HTTP request with the value of the search input which should return the filtered results for you.

<template>
  <div>
    ...
    <Search v-on:wordChange="onWordChange"></Search>
    <ActorList v-bind:characters="actorInfo"></ActorList>
    ...
  </div>
</template>

<script>
  ...

  export default {
    ...
    mounted () {
      this.search('') // I confirmed that this will load all records on page load
    },
    methods: {
      onWordChange (searchTerm) {
        this.search(searchTerm)
      },
      search (searchTerm) {
        axios.get('https://www.breakingbadapi.com/api/characters?name=${searchTerm}')
          .then(response => {
            this.actorInfo = response.data
          })
      }
    }
    ...
  }
</script>
Graham S.
  • 1,480
  • 1
  • 20
  • 28
  • Like you explained, `mounted` will be executed when the DOM is mounted. So why not use `created()` instead? – wittgenstein Jan 02 '21 at 12:47
  • `created` is fired before `mounted`, so yes, you could use `created` instead and would make more sense to use for fetching "page load" data. In this case, I stuck with `mounted` since the OP used `mounted` . But yes, OP, prefer `created` over `mounted` when fetching data. – Graham S. Jan 04 '21 at 21:23