I have been working on a news app, using Vue 3 and the News API.
I am currently working on a search functionality.
In App.vue
I have:
<template>
<TopBar @search="doSearch" />
<div class="container">
<HomeView searchString="searchTerm" v-if="!searchTerm.length" />
<SearchResultsView searchString="searchTerm" v-if="searchTerm.length" />
</div>
<AppFooter />
</template>
<script>
import TopBar from '@/components/TopBar.vue';
import AppFooter from '@/components/AppFooter.vue';
import HomeView from '@/views/HomeView.vue';
import SearchResultsView from '@/views/SearchResultsView.vue';
export default {
name: 'App',
components: {
TopBar,
AppFooter,
HomeView,
SearchResultsView
},
data: () => ({
searchTerm: ''
}),
methods: {
doSearch: function(searchTerm) {
this.searchTerm = searchTerm;
console.log(this.searchTerm);
}
}
}
</script>
I emit the search
event form the TopBar.vue
component, where the search form is:
<template>
<nav class="navbar py-1 sticky-top navbar-expand-md">
<div class="container-fluid">
<form ref="searchForm" class="search_form w-100 mx-auto mt-2 mt-md-0">
<div class="input-group">
<input
@change="handleChange"
v-model="searchTerm"
class="form-control search-box"
type="text"
placeholder="Search..."
/>
<div class="input-group-append">
<button class="btn" type="button">
<font-awesome-icon :icon="['fas', 'search']" />
</button>
</div>
</div>
</form>
</div>
</nav>
</template>
<script>
export default {
name: "TopBar",
methods: {
handleChange(event){
this.$emit('search', event.target.value)
}
}
};
</script>
The search string is correctly "captured" by the root App.vue
component. I try to pass it to the ArticleList.vue
component, so that it becomes a part of the endpoint used by the component:
<template>
<div v-if="articles.length" class="row">
<div
v-for="article in articles"
:key="article._id"
class="col-xs-12 col-sm-6 col-lg-4 col-xl-3"
>
<ArticleCard :article="article" />
</div>
</div>
<p v-else class="text-center">
No articles to display
</p>
</template>
<script>
import ArticleCard from './ArticleCard.vue';
export default {
name: "NewsList",
components: {ArticleCard},
props: {
whatToShow: {
type: String,
required: true,
},
searchString: {
type: String,
required: true,
default: ''
}
},
data: () => ({
language: 'en',
page_size: 24,
current_page: 1,
articles: [],
}),
mounted() {
this.getArticles();
},
methods: {
getArticles() {
let endpoint = `${process.env.VUE_APP_API_URL}/${this.$props.whatToShow}?q=${this.$props.searchString}&language=${this.language}&page_size=${this.page_size}&page=${this.current_page}&apiKey=${process.env.VUE_APP_API_KEY}`;
console.log(endpoint);
this.$axios
.get(endpoint)
.then((response) => {
this.articles = response.data.articles;
console.log(this.articles);
})
.catch((err) => console.log(err));
},
}
};
The problem
The searchString
prop in the endpoint
variable above does not update upon doing a search (with "money" for example) and console.log(endpoint) outputs
https://newsapi.org/v2/everything?q=&language=en&page_size=24&page=1&apiKey=myappykey123secret
instead of
https://newsapi.org/v2/top-headlines?q=money&language=en&page_size=24&page=1&apiKey=myappykey123secret
Questions
- What am I doing wrong?
- What is the most reliable way to fix this issue?
3
Answers
Yes you are correct,
searchString
updated inApp.vue
but you are not watching this change in ArticleList component.The request method getArticles only trigger on mounted. You have to watch
searchString
prop and triggergetArticles()
method againThe code has too many errors:
bind
prop inApp.vue
TopBar.vue
you haveref='searchForm'
andv-model='searchTerm' but the refs
searchFormand
searchTerm` are not defined at all.TopBar.vue
you didn’t declare foremits
so custom eventsearch
will never be caught bydoSearch
functionArticleList.vue
you don’t even listen for changes so only when this component is mounted does it send a request toendpoint
and it doesn’t react at allWhere do you try to send the results to NewsList component from ArticleList.vue? You did not post where do you do the binding.