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

Serving pages with Node JS and loading .js files on the client side

Here is a simple JS file that will be familiar to those who have worked with Socket.IO in NodeJS and Express: var express = require('express'), app = express(), server = require('http').createServer(app), io = require(&apos ...

Display only one field and hide the other field using a jQuery IF/ELSE statement

Hey there! I have a situation where I need to toggle between two fields - one is a text field and the other is a text_area field. When a user clicks on one field, the other should be hidden and vice versa. I've tried using JQuery for this: $(document ...

javascript - developing a neutral constructor

Similar Question: Constructors in Javascript objects I am exploring the concept of creating classes in JavaScript. I am struggling to grasp it fully. Now, I am curious if it's possible to create a constructor in JavaScript, similar to what can b ...

Remove a particular row from a database table

I'm facing an issue with my code. I want to be able to remove a row by clicking on a remove button within that row, but I'm unsure of how to accomplish this. <tbody id="myTable"> <?php if (!isset($_SESSION)){ ...

Issues encountered with Angular POST requests

I have established a registration and login system using passport.js. Additionally, I am incorporating Angular.js in the front-end. However, when Angular is used, the user signup process does not work as expected. Below you can find the code snippets for b ...

Having issues initializing jQuery knob on a jQuery mobile webpage

I'm trying to implement the jquery knob plugin to showcase a circular rating system, but I'm encountering difficulties in getting it to display properly. Below is the code snippet I'm using - can someone please point out what's causing ...

Troubleshooting CORS, OPTIONS, and PreFlight issues when using Axios to consume a REST API in a Vue.js project

Currently, I am working on developing the front end for a PHP-based application that utilizes a custom management tool. My approach involves using Vue.js to construct the front end and specifically employing Axios to make API calls. I have set up a Lights ...

Combining CK Editor 5 from source with Vue JS and utilizing components locally results in duplication

I am facing an issue with my page components setup as follows: BlogEntryPointComponent includes NewBlogComponent and BlogEditComponent both the NewBlogComponent and BlogEditComponent are using the same code snippet: import BlogEditor from '../../.. ...

Discovering feedback from JavaScript

I have been attempting to call JavaScript from a Visualforce page. The code snippet below shows a sample of the Visualforce page where I am trying to call a get method and am curious to see the response. Once I click on preview in the developer console, th ...

v-tooltip specifically designed for the append-icon

I'm currently working with Vuetify and facing an issue with applying a tooltip only for the append-icon in a v-text-field. The current problem is that the tooltip does not work for icons at all! View code on CodePen <v-tooltip bottom> < ...

Tips for utilizing a Three.js curve to guide the movement of a mesh along a specified path

Developing an animation, currently at this stage: http://jsfiddle.net/CoderX99/66b3j9wa/1/ Please avoid delving into the code, it's written in CoffeeScript and may not be beneficial for your mental well-being. Imagine a "nordic" landscape with ship ...

Querying a subarray in MongoDB

Here is a document I have: { "_id" : "someId", "name" : "myTeam", "team" : [ { "entity" : "size", "value" : 14 }, { "entity" : "returns", ...

Dealing with AJAX errors in React components after mounting

Facebook suggests that the optimal method for loading initial data in a React component is to execute an AJAX request within the componentDidMount function: https://facebook.github.io/react/tips/initial-ajax.html It would look something like this: ...

Holding off on completing a task until the outcomes of two parallel API requests are received

Upon page load, I have two asynchronous API calls that need to be completed before I can calculate the percentage change of their returned values. To ensure both APIs have been called successfully and the total variables are populated, I am currently using ...

How can we verify the validity of URLs based on their length, presence of capital letters, and specific whole words?

I'm currently working on a piece of code that verifies the URL provided by users during sign-up for my application. I want to implement the following restrictions: URL must be at least 3 characters long No capital letters allowed Avoid certain words ...

The ever-changing world of list items with dynamic editions

One of my tasks involves displaying a list of elements through the ng-repeat directive. Each element is contained within its own div block. My goal now is to enable editing for each block, so that when users click on an edit button, the content of the div ...

When the submit button is clicked, only the last form within the function will be submitted

I currently have a submit button that is meant for 3 different forms. When submitting, I use the following approach: restaurantCreateForm = function(){ document.getElementById("restaurant-features" ).submit(); document.getElementById("information_ ...

Attempting to transform a numerical value into CSS syntax

Currently, I am attempting to loop through several DIV elements, extract a numerical value from each DIV, and then based on that value matching a specific value in the JavaScript code, assign a particular CSS Class back to the original DIV. This is the sn ...

How can you stop a document event listener from triggering?

Utilizing document.addEventListener('touchstart', this.onDocument); allows me to recognize when a user taps outside of an element. However, within my button click handler, the following code is present: toggleItemActive(e) { e.stopPropa ...

Encountering a Vue3/Vite/Vuetify 3 build issue: The variable was not declared with !default in the imported module

Struggling to implement SASS Variables in my Vue 3, Vuetify 3, Vite build as I keep running into an error regarding variables not being declared with !default Following the guidelines provided in theVuetify 3 documentation for configuring SASS Variables. ...