import and call function within Method not working - vue.js

I am creating a small VueJs app, that calls the NASA image API and displays on the screen.
In the Header component (below) I have a search bar, when clicked this will call the Axios method defined in another file, I understood from the documents that if import functions to component then they need to be defined in the 'methods'. However, when I click search nothing displays on the console?
note: the call to NASA has been tested and does work when I include in the component. Which I guess begs the question if I should leave it in the component as I won't use it elsewhere.
but would still like to understand the logic behind the issue.
component code:
<template>
<div>
<h1>Nasa Image Search</h1>
<div class="search-container">
<form action="/action_page.php">
<input type="text" placeholder="Search.." name="search" />
<button v-on:click="search" type="submit">Search</button>
</form>
</div>
</div>
</template>
<script>
import nasa from '../apiCall'
export default {
name: 'Header',
methods: {
search : function(){
nasa
}
}
}
</script>
Axios function call:
import axios from 'axios'
const nasa = () => {
var url = `https://images-api.nasa.gov/search?q=apollo-13&media_type=image`
console.log(url) //bug testing
axios
.get(url)
.then(function(response) {
// handle success
console.log(response)
})
.catch(function(error) {
// handle error
console.error(error)
})
}
export default { nasa }

Your default export is actually an object with a function property, like this:
{
nasa: () => { ... }
}
When you import the object, you give it the name nasa, so you'd actually have to call the function like:
nasa.nasa()
Since you probably intend to just export the function, leave your import as is but change your export to:
export default nasa; // no brackets
And in your component, you don't need to embed that in a method, you can set it directly to the search method:
methods: {
search: nasa
}

Related

Making API call using Axios with the value from input, Vue.js 3

I am making an app using this API. The point I'm stuck with is calling the API. If I give the name of the country, the data of that country comes.
Like, res.data.Turkey.All
I want to get the value with input and bring the data of the country whose name is entered.
I am getting value with searchedCountry. But I can't use this value. My API call does not happen with the value I get. I'm getting Undefined feedback from Console.
Is there a way to make a call with the data received from the input?
<template>
<div>
<input
type="search"
v-model="searchedCountry"
placeholder="Search country"
/>
</div>
</template>
<script>
import axios from 'axios';
import { ref, onMounted} from 'vue';
export default {
setup() {
let data = ref([]);
const search = ref();
let searchedCountry = ref('');
onMounted(() => {
axios.get('https://covid-api.mmediagroup.fr/v1/cases').then((res) => {
data.value = res.data.Turkey.All;
});
});
return {
data,
search,
searchedCountry,
};
},
};
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
I'm work with Vue.js 3
There are a few things wrong with your code:
Your axios call is only called once, when the component mounts (side note here, if you really want to do something like that, you can do it directly within the setup method)
You don't pass the value from searchedCountry to the axios API
Use const for refs
I'd use a watch on the searchedCountry; something like this (I don't know the API contract):
<template>
<div>
<input
type="search"
v-model="searchedCountry"
placeholder="Search country"
/>
</div>
</template>
<script>
import axios from 'axios';
import { ref, watch } from 'vue';
export default {
setup() {
const searchedCountry = ref('');
const data = ref([]);
watch(
() => searchedCountry,
(country) => axios.get(`https://covid-api.mmediagroup.fr/v1/cases/${country}`).then((res) => data.value = res.data.Turkey.All);
);
return {
data,
searchedCountry,
};
},
};
</script>

Vuex is resetting already set states

Have started to play around with Vuex and am a bit confused.
It triggers the action GET_RECRUITERS everytime I load the component company.vue thus also making an api-call.
For example if I open company.vue => navigate to the user/edit.vue with vue-router and them go back it will call the action/api again (The recruiters are saved in the store accordinly to Vue-dev-tools).
Please correct me if I'm wrong - It should not trigger the action/api and thus resetting the state if I go back to the page again, correct? Or have I missunderstood the intent of Vuex?
company.vue
<template>
<card>
<select>
<option v-for="recruiter in recruiters"
:value="recruiter.id">
{{ recruiter.name }}
</option>
</select>
</card>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
middleware: 'auth',
mounted() {
this.$store.dispatch("company/GET_RECRUITERS")
},
computed: mapGetters({
recruiters: 'company/recruiters'
}),
}
</script>
company.js
import axios from 'axios'
// state
export const state = {
recruiters: [],
}
// getters
export const getters = {
recruiters: state => {
return state.recruiters
}
}
// actions
export const actions = {
GET_RECRUITERS(context) {
axios.get("api/recruiters")
.then((response) => {
console.log('API Action GET_RECRUITERS')
context.commit("GET_RECRUITERS", response.data.data)
})
.catch(() => { console.log("Error........") })
}
}
// mutations
export const mutations = {
GET_RECRUITERS(state, data) {
return state.recruiters = data
}
}
Thanks!
That's expected behavior, because a page component is created/mounted again each time you route back to it unless you cache it. Here are a few design patterns for this:
Load the data in App.vue which only runs once.
Or, check that the data isn't already loaded before making the API call:
// Testing that your `recruiters` getter has no length before loading data
mounted() {
if(!this.recruiters.length) {
this.$store.dispatch("company/GET_RECRUITERS");
}
}
Or, cache the page component so it's not recreated each time you route away and back. Do this by using the <keep-alive> component to wrap the <router-view>:
<keep-alive>
<router-view :key="$route.fullPath"></router-view>
</keep-alive>

Cannot read property 'push' of undefined - vue and axios

I have;
An API built from express running on port 2012
An Vue app running on port 8080
The Vue application communicates with the API using Axios.
I have been able to register users and log them in when the user clicks 'register' or 'login' it will submit their data to the API, if the API responses with an OK message, I use this.$router.push('/login') if a user successfully registered and this.$router.push('/dashboard') if a user is successfully logged in from the login page. However I continue to get "cannot read property 'push' of undefined" when I try to call this.$router.push on the dashboard vue.
login.vue (this.$router.push works)
<template>
<form id="login_form" method="post" v-on:submit.prevent="onSubmit">
<input type="text" name="username" class="form-control" v-model="auth.username" placeholder="username" />
<input type="password" name="password" class="form-control" v-model="auth.password" placeholder="password" />
<input type="submit" value="Submit" />
</form>
</template>
<script>
import Vue from 'vue'
import login_axios from '../axios/login_axios.js'
export default{
name: 'login_form',
data:function(){
return{
auth:{
username:'',
password:''
}
}
},
methods:{
onSubmit: login_axios.methods.onSubmit
},
components:{
login_axios
}
}
</script>
This login_vue component imports a javascript file called login_axios.js
login_axios contains a method called onSubmit which is called when the user clicks login/submit. onSubmit checks if res.data.auth.authenticated is true or false, if it is true, it executes this.$router.push to /dashboard, this works. However from the dashboard it does not work.
login_axios.js (this.$router.push works)
import Vue from 'vue'
import axios from 'axios'
import AxiosStorage from 'axios-storage'
let sessionCache = AxiosStorage.getCache('localStorage');
export default {
methods:{
async onSubmit(e){
e.preventDefault();
const res = await axios.post('http://myapi/login', this.auth);
try{
if(res.data.auth.authenticated){
sessionCache.put('authenticated', true);
this.$router.push('/dashboard');
}
} catch (error){
console.log(error);
}
}
}
}
Below is dashboard.vue which imports dashboard_axios.js
dashboard.vue (cannot read property 'push' of undefined)
<template>
<div>
<h1>Dashboard</h1>
Login
Register
Posts
About
</div>
</template>
<script>
import Vue from 'vue'
import dashboard_axios from '../axios/dashboard_axios.js'
export default {
name: 'dashboard',
methods:{
},
components:{
dashboard_axios
}
}
</script>
I have tried a few different things, but I have ended up setting self as a const of this. I defined the function verify_auth in dashboard_axios.js then called it directly after. I would expect this to work as it is just a function which should need called. I may be completely out of the loop as I am no expert at vue, but have been trying to research as much as I can.
dashboard_axios.js (cannot read property 'push' of undefined)
import Vue from 'vue'
import router from 'vue-router'
import axios from 'axios'
import AxiosStorage from 'axios-storage'
const self = this;
let sessionCache = AxiosStorage.getCache('localStorage');
sessionCache.put('authenticated', false);
console.log(sessionCache.get('authenticated'));
function verify_auth(){
if(sessionCache.get('authenticated')){
console.log('successfully verified authentication')
self.$router.push('/')
}else{
console.log('issue verifying authentication')
self.$router.push('/login')
}
}
verify_auth();
export default {
name: 'dashboard_axios',
methods:{
},
data: function() {
},
created: function(){
}
}
I am not 100% sure if this is the answer, but I have found a workaround.
I was importing javascript files such as 'dashboard_axios.js' which did not get loaded in as I wished it would. So instead, I renamed the file to 'dashboard_axios.vue' and added <template></template>, and left it empty, and then wrapped my js code in <script></script> then on the dashboard.vue I called the <dashboard_axios /> tag and it worked as I expected.

VueJS: TypeError: Cannot read property of undefined when Reload

I have a page like this:
<template>
<div class="row flex">
{{posts.id}}
</div>
</template>
<script>
import axios from 'axios'
export default {
async asyncData ({ route }) {
let { data } = await axios.get('http://localhost:8000/api/v1/feeds/' + route.params.id + '/')
return {
posts: data
}
}
}
</script>
When I click link with hot reload (router-link), it display well. But when I reload this window, it appear in 1 seconds and disappear then.
Video: http://g.recordit.co/ht0a0K2X81.gif
Error Log:
How can I fix this?
Add a property to your data i.e dataLoaded: false. When your ajax request has finished, set this.dataLoaded = true. On your template add v-if="dataLoaded. This will mean the template data won't render until you're ready.
You could also do v-if="posts" as another way but I generally have a consistent dataLoaded prop available to do this.
Edit: I just looked at your example again and doing something like this would work:
<template>
<div class="row flex" v-if="posts">
{{posts.id}}
</div>
</template>
<script>
import axios from 'axios'
export default {
data () {
return {
posts: null
}
}
methods:{
loadPosts () {
return axios.get('http://localhost:8000/api/v1/feeds/' + this.$route.params.id + '/')
}
},
created () {
this.loadPosts().then(({data}) => {
this.posts = data
})
}
}
</script>
I've removed the async and just setting posts when the axios request returns it's promise. Then on the template, it's only showing posts is valid.
Edit
You can also use your original code and just add v-if="posts" to the div you have in your template.

Pass data from one component to all with $emit without using #click in VueJS

Trying to learn vuejs I got to the question how to pass any data from one component to all, using $emit but without using any #click.
It is possible some how that the data to be just available and grab it any time, without using the click?
Let's say we have this example with normal #click and $emit.
main.js
export const eventBus = new Vue()
Hello.vue
<template>
<div>
<h2>This is Hello component</h2>
<button
#click="emitGlobalClickEvent()">Click me</button>
</div>
</template>
<script>
import { eventBus } from '../main'
export default {
data () {
return {
msg: 'Welcome to Your Vue.js App'
}
},
methods: {
emitGlobalClickEvent () {
eventBus.$emit('messageSelected', this.msg)
}
}
}
</script>
User.vue
<template>
<div>
<h2>This is User component</h2>
<user-one></user-one>
</div>
</template>
<script>
import { eventBus } from '../main'
import UserOne from './UserOne.vue'
export default {
created () {
eventBus.$on('messageSelected', msg => {
console.log(msg)
})
},
components: {
UserOne
}
}
</script>
UserOne.vue
<template>
<div>
<h3>We are in UserOne component</h3>
</div>
</template>
<script>
import { eventBus } from '../main'
export default {
created () {
eventBus.$on('messageSelected', msg => {
console.log('From UserOne message !!!')
})
}
}
</script>
I want to get this message : Welcome to Your Vue.js App from Hello.vue in all components, but without #click, if is possible.
You can create another Javascript file which holds an Object with your initial state. Similar to how you define data in your components.
In this file your export your Object and import it in all Components which need access to this shared state. Something along the lines of this:
import Store from 'store';
data() {
return {
store
}
}
This might help:
https://v2.vuejs.org/v2/guide/state-management.html
At this point if you app grows even more in complexity you might also start checking out Vuex which helps to keep track of changes(mutations) inside of your store.
The given example is essential a very oversimplified version of Vuex.