What is the best way to create a reactive prop within this Vue 3 application?

I've been developing a news application using Vue 3 along with the News API.

My current focus is on implementing a search feature.

Within my App.vue file, 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 trigger the search event from the TopBar.vue component where the search form resides:

<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 entered search query successfully reaches the root App.vue component. My attempt is to pass it to the ArticleList.vue component so that it can be included in the component's endpoint:

<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));
    },
  }
};

Screenhot

https://i.stack.imgur.com/LWWAs.png

The issue

The searchString prop within the endpoint variable mentioned above doesn't update when performing a search (for example, searching for "money"). Upon console logging, the output is:

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

Queries

  1. What could be the mistake being made here?
  2. What would be the most effective method to resolve this inconsistency?

Answer №1

Absolutely right, the variable searchString was updated in the file App.vue, but it seems like you forgot to monitor this change in the ArticleList component.

The getArticles request method is only triggered upon mounting. To resolve this issue, make sure to watch the searchString prop and then manually call the getArticles() method again.

Answer №2

I have identified a few errors that need to be addressed:

  • Ensure to include @submit.prevent in the <form> tag within the TopBar component to avoid page reload when pressing enter in the input field
  • In the SearchResult component, remember to declare the searchString prop and pass it to the ArticleList component
  • Add watchers in the ArticleList component for the searchString and whatToShow properties to send requests when they change. Additionally, access props directly using this.searchString rather than this.$props.searchString

Although the code appears to be functioning properly now, there are still some glaring errors such as missing vue-router or undeclared props in the HomeView component. The console provides explanations for these issues, making them relatively easy to rectify.

It seems that the .env variables are not accessible for some reason. This may be due to inadvertently selecting a sandbox base utilizing the outdated vue-cli instead of create-vue. Substituting the variables with actual content results in failed API calls, which does not appear to be Vue-related. Review the URL displayed in the console for further insights.

You can find the updated version of the project on this sandbox link

Answer №3

It's surprising that no one has mentioned the obvious: many of your issues arise from trying to replicate store functionality without actually using a store.

While it is possible, you were heading in the right direction by utilizing <App /> as the primary source of truth and syncing its state with deeper nested components that act as the true controls.

However, accomplishing this feat is akin to walking between two distant towns when you could easily take a car or train instead.
In essence, just because something can be done doesn't mean it should be done.

In addition to the lack of a store, the app contains several errors, largely due to applying Vue 2 solutions to a Vue 3 application without consulting the migration guide. While many aspects remain consistent across versions, some key elements have evolved.

Take a look at a basic store setup for your app here.

Some helpful tips:

  • Avoid placing query URL composition logic within the articles list component. This logic belongs in the store, allowing various controls throughout the app to adjust settings (such as search terms, pagination, category, language) without requiring them to reside within the list component.
    The list component should focus solely on presenting the available articles sourced from the store. Decoupling business logic from UI logic brings numerous benefits, too vast to enumerate here.
  • Apply @submit.prevent to the nav form to prevent page reload upon pressing Enter in the input field.
  • Avoid concurrent use of v-model and @change (or @input) on the same element (v-model shorthand encompasses both :value + @input`).
  • Carefully consult documentation on installing and utilizing plugins (both vue-axios and vue-router implementations in the shared sandbox were faulty).
  • Consider implementing debounce for the searchTerm so queries are only sent once the user stops typing.

Answer №4

The code contains numerous errors that need to be addressed:

  1. It was pointed out by @BoussadjraBrahim that the prop in `App.vue` is not bound properly.
  2. In `TopBar.vue`, there are references to `searchForm` and `searchTerm` but they have not been defined at all.
  3. No declaration has been made for `emits` in `TopBar.vue`, so the custom event `search` will not be caught by the `doSearch` function.
  4. In `ArticleList.vue`, there is no listener for changes, resulting in the component only sending a request to `endpoint` when it is mounted without reacting to any further changes.

Answer №5

Can you clarify how you are attempting to transfer the results to the NewsList component from ArticleList.vue? It seems that you have missed specifying where the binding should occur.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The issue persists with Vuetify app bar when attempting to hide overflow

Having an issue with the overflow hidden property not working as expected. It seems to create an extra scroll bar, similar to what is shown in this image. I directly copied the code from the Vuetify website and tested it out on CodePen, but encountered th ...

How can I toggle the visibility of a div based on whether a variable is defined or not?

How can I display a specific div on my webpage only when certain variables in PHP pull out a specific result from a database? I attempted to use the code snippet below, but it's not working as expected. Can someone provide guidance on how to achieve ...

core.js encountered an issue at line 6210: TypeError - The function this.service.addDepartment does not exist

Whenever I attempt to click the 'Add' button on a web application that I'm constructing, an error pops up. core.js:6210 ERROR TypeError: this.service.addDepartment is not a function at AddEditDepComponent.addDepartment (add-edit-dep.componen ...

Error in Vue3: Uncaught TypeError when trying to read properties that are undefined while using the `$on` method

Learn how to use vue3 for implementing a recording function on a webpage. Everything seems to be working fine, but I encountered an error when opening the page. The console displays this message: Uncaught (in promise) TypeError: Cannot read properties of ...

Implementing an onclick function to initiate a MySQL update query

<?php include_once("db.php"); $result=mysql_query("SELECT * FROM stu WHERE receiver='DM4'"); while($row=mysql_fetch_array($result)){ echo "<tr>"; echo "<td>" . $row['ptype'] . "</td>"; echo "<td>" . $row[&apo ...

The array is present both before and after the res.json() call, however it appears empty in the response

When using Express to send a json response with res.json(), I am experiencing an issue where the value of records in the object sent via res.json() is empty. My code snippet looks like this: stats.activities(params).then(res => { processActivities ...

ngModel is not taken into account when processing form data

Attempting to make use of a dynamic form in AngularJS, the code snippet below has been utilized: <dynamic-form template="formTemplate" ng-model="formData" ng-submit="processForm()"> </dynamic-form> The controller script inc ...

Unraveling the mystery: How does JavaScript interpret the colon?

I have a quick question: When I type abc:xyz:123 in my GoogleChrome browser console, it evaluates to 123. How does JavaScript interpret the : symbol in this scenario? ...

The Access-Control-Allow-Headers Error prohibits the use of the Authorization request header field

I am currently working on integrating a VueJS frontend application with an ExpressJS backend server. In my server code, I have implemented the following: var allowCrossDomain = function(req, res, next) { res.header('Access-Control-Allow-Origin&ap ...

Extension: What is the best way to leverage data obtained from an ajax request in order to dynamically modify an already existing element within

I've been trying to find a reliable and comprehensive tutorial for JavaScript (JS) and Ajax, but so far my search has been futile. Unlike Python.org for Python or php.net for PHP, I haven't found a satisfactory resource yet. Any recommendations w ...

Receiving a blank array upon calling res.json() in Node.js script

I'm facing an issue with my code snippet that displays all posts, including the username and display picture of each user. Everything seems to be working fine as the log output is perfect. However, I'm struggling to return this data as a JSON obj ...

What is the most effective way to retrieve distinct values in Mongoose?

I am looking to extract unique values from a collection. Here is an example: const userID = `user1`; const users = await Chat .find({'$or': [{to: userID}, {from: userID}]}) .select(`-_id to from`) .lean(); // users will contain: [ {from: ...

How does gray-matter function in Node.js affect the matter within?

import fs from 'fs'; import path from 'path'; import matter from 'gray-matter'; const postsDirectory = path.join(process.cwd(), 'posts'); // ... ... ... // export function getPostData(id) { const fullPath = ...

Is there a way to horizontally navigate a pallet using Next and Prev buttons?

As someone new to development, I am looking for a way to scroll my question pallet up and down based on the question number when I click next and previous buttons. In my pallet div, there are over 200 questions which are dynamically generated. However, th ...

What steps can be taken to properly display dateTime values in a data table when working with JavaScript (VueJS) and PHP (Laravel)?

I am facing an issue where I am unable to save user inputted date-time values from a modal into a data table. Despite receiving a success message, the dateTime values are not being added to the table. My payload only displays the state and approval fields ...

Verify if the nested arrays within the object consist of any empty elements

Take a look at the object below: { "params": { "time_to_diagnosis": [ { "field": "date_of_diagnosis", "value": "" }, { "field": "date_of_symptom_onset", "value": "2019-09-01" } ], "time ...

Tips on how to correctly pass a .JSON object in the setState function of a reactJS

I am having an issue when attempting to pass a .json file in the following format: this is my class import MyForm from './MyForm'; class CreateProject extends React.Component{ constructor(){ super(); this.state = { categori ...

Locate a piece of text with jQuery and enclose it within a specified container

Check out this code <form method="get" name="form_delivery"> Pick the country where you want your delivery<br> <select name="deliverymethod"> <option value="0" selected="selected">Choose a country / region</option> ...

TRPC fails to respond to the passed configuration or variables (e.g., when enabled is set to false)

Recently started using trpc and I'm trying to grasp how to utilize useQuery (which I've previously worked with in react-query): const IndexPage = () => { const { isLoading, data, isIdle } = trpc.useQuery([ "subscriber.add", { email: ...

Setting up secure HTTPS connections for Node.js and Vue.js

I am encountering an issue with my Laravel and Vue.js site, which also utilizes nodejs. Accessing the site via http works fine, but when attempting to use https, half of the site fails to load and I receive the following error in the console: Access to XML ...