Method that includes fetch wont change the value of an object - vuejs2

A method that contains a fetch API call, wont update the user object
I have a login component witch is only active if the object user is null.
The login button activates the method witch activates a fetch with a put request, and gets data back from my back-end. The API call works and gets the wanted data, but when i try to update my object it stays null, so my login component wont disappear.
this is the login component that needs to disappear when the object user updates
<template>
<div>
<div v-if="user === null">
<Login class="login" v-on:check-user="checkUser"/>
</div>
</div>
</template>
This is where i initialize the user object and the method that is suppose to update the said object.
the alert returns the whole object as it should be but the login component isn't disappearing
data(){
return{
user: null,
coupons:[],
shops:[],
}
},
methods:{
checkUser(mak){
alert(this.user);
fetch("http://localhost:8080/kupom/kupoman/user", {
method: "PUT",
headers: {
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify(mak)
})
.then(response=> {
return response.json();
})
.then(data=>{
this.user=data;
alert(JSON.stringify(this.user));
});
},

Related

Call API automatically to fetch data with prop value when component is displayed in Vue.js

I have a page which displays a list of mutual funds. With each mutual fund, I have a button to display their NAV history. This button calls a component which has an embedded API call to fetch the NAV history. I pass the fund code for which the data is to be fetched as a prop to the component. However, I am not able to trigger the API call automatically when the prop is called.
this is my code as of now:
Parent component (main page):
<template>
<!-- some code -->
<a href="#" #click="fetchNavHistory(fund)">
<v-icon small>history</v-icon>
</a>
<!-- some more code -->
<NAVHistory
:show="showNavHistory"
:amfi_code="amfi_code"
ref="history"
#hide="showNavHistory=false"
/>
</template>
export default {
name: "FundList",
components: {
NAVHistory
},
data() {
return {
showNavHistory: false,
amfi_code: 0
}
},
methods:{
fetchNavHistory(fund){
this.amfi_code = fund.amfi_code
this.showNavHistory = true
var child = this.$refs.history
child.fetchNavHistory()
}
}
}
Child component (where NAV history is displayed):
<template>
<!-- some code -->
</template>
<script>
export default {
props: {
show: Boolean,
amfi_code: Number
},
data(){
return{
apiURL: process.env.VUE_APP_BASEURL,
navHistory: [],
}
},
methods: {
async fetchNavHistory(){
try{
const response = await fetch(this.apiURL + '/navhistory', {
method: 'POST',
body: JSON.stringify({"amfi_code": this.amfi_code}),
headers: {'content-type': 'application/json; charset=UTF-8'},
})
const data = await response.json()
console.log(data)
this.navHistory = data
} catch(error){
console.log(error)
}
}
}
}
</script>
At first I tried calling the fetchNavHistory() method on updated() event. But that kept calling the API non-stop when the component was displayed on the screen.
Then I tried adding a watch for the show prop. But that didn't work at all.
Finally, as a workaround, I called the API from the parent component itself. While that is working, it is calling the component with the previous value of the amfi_code, rather than the updated value. So the first time it gets called, the amfi_code is passed as 0.
Is there a way to safely trigger the API call when the component is displayed, i.e., the show prop is set to true?
You can try watch with deep:true option that way the watch will be triggered when a component will be mounted. Or you can call API on mounted hook and check show prop in it.
deep:true means a watch will look at if changes occur not only for a watched prop but additionally at all nested props.
immediate:true means that a watch will fire after a component is mounted (when a watched prop has initial value).

Data's getters able to watch non-reactive localStorage, but not for computed?

I have a section that shows a Login button when there a user is not authenticated(as shown in localStorage), otherwise username and a 'logout botton' will be shown.
The code works when I put the authenticated inside the data block. However, if I put it in the computed field, it always shows login
#template
<router-link v-if="!authenticated" to="login">Log In</router-link>
<template v-else>
Logged in as {{username}}
<button #click="logout">Log out</button>
</template>
#script
data: function() {
return {
// get authenticated() { //this works
// return localStorage.getItem('authenticated');
// },
}
},
computed: function() {
return {
authenticated: function() { //this does not work
return localStorage.getItem('authenticated');
}
}
},
Vue isn't able to observe changes to local storage items, and so the authenticated computed property will forever return the same value because Vue caches the values of computed properties.
In this scenario you would typically use a method:
methods: {
authenticated() {
return localStorage.getItem('authenticated')
}
}
although your getter would work too.

Detect browser close or page change VueJs

I try to detect when user change/insert into an input and he try to change/close page to give him a warning. I do some research but till now I didn't find anything.
<b-form-group label="Name" label-for="name-input">
<b-form-input
id="name-input"
v-model="name"
></b-form-input>
</b-form-group>
created() {
document.addEventListener('beforeunload', this.handlerClose)
},
handlerClose: function handler(event) {
console.log('CHANGE!!!!');
},
Detect navigating to a different page or close the page
You can try using the same eventhandler beforeunload on the window object, not the document object, as stated in the MDN Web Docs for example ( https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event ). The event should handle both cases, switching page and closing page.
<script>
export default {
created() {
window.addEventListener('beforeunload', (event) => {
// Cancel the event as stated by the standard.
event.preventDefault();
// Chrome requires returnValue to be set.
event.returnValue = '';
});
}
}
</script>
This event enables a web page to trigger a confirmation dialog asking the user if they really want to leave the page. If the user confirms, the browser navigates to the new page, otherwise it cancels the navigation.
About your second question to detect whetever changes has been made : This eventhandler does not detect changes.
In order to mantain a state whetever the user made a change, e.g. to a form, I would outsource this state with a data prop isChanged and initialize it with false. Then use Vue directives v-on:change or v-on:input to change the prop from false to true.
<template>
<div>
<input type="text" #change="userMadeChange" v-model="inputText" />
</div>
</template>
<script>
export default {
data() {
return {
inputText : "",
isChanged : false
}
},
methods : {
userMadeChange() {
this.isChanged = true;
}
}
}
</script>
The easier way is to simply compare the stringified JSON of your selected data. If they are equivalent, then we know that the data has not been changed/updated/mutated by the user.
Here's a simple setup:
Create a method that generates the JSON for the user data that you want to observe for changes.
When the compoonent/app is created, you cache the data that it is created with and store/cache it
Create a computed property that simply returns the current state of the user data and cached user data
In the beforeunload handler, you can then check the returned value of this computed property to determine of the user has mutated data or not.
See proof-of-concept below:
new Vue({
el: '#app',
// COMPONENT DATA
data: {
// Dummy data
firstName: 'John',
lastName: 'Doe',
// Cache form data
cachedFormData: null,
},
// COMPONENT LIFECYCLE HOOK
created: function() {
// Create a cache when component/app is created
this.cachedFormData = this.formDataForComparison();
document.addEventListener('beforeunload', this.handlerClose);
},
// COMPUTED PROPERTIES
computed: {
// Compares cached user data to live data
hasChanged() {
return this.cachedFormData !== this.formDataForComparison();
}
},
// COMPONENT METHODS
methods: {
// Callback handler
handlerClose: function() {
if (this.hasChanged) {
// Logic when change is detected
// e.g. you can show a confirm() dialog to ask if user wants to proceed
} else {
// Logic when no change is detected
}
},
// Helper method that generates JSON for string comparison
formDataForComparison: function() {
return JSON.stringify({
firstName: this.firstName,
lastName: this.lastName
});
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model="firstName" />
<input type="text" v-model="lastName" />
<br />
<br />
<span>Has user changed data? <strong>{{ hasChanged }}</strong></span>
</div>
An alternative method would be simply storing a flag that has a ground state of false, as proposed by the other answer. The flag state is switched to true wheneveran input/change event is detected on the element. However, there are several disadvantages associated with this method:
Even when the user undo his/her changes, it will still marked as changed. This constitutes a false positive.
You will need to either bind watchers to all the v-model members, or bind input/change event listeners to all input elements on the page. If your form is huge, there is a chance that you will forget to do this to an input element.

adding headers to nuxt for making api rest call with axios

Inside my nuxt application i need to make call to an external api like this (file.vue) :
<template>
<div class="container">
<h1>{{ post.title }}</h1>
<pre>{{ post.body }}</pre>
<p><nuxt-link to="/posts">Back to the list</nuxt-link></p>
</div>
</template>
<script>
export default {
async asyncData({ app }) {
let { data } = await app.$axios.$get(`http://my.api.adress:8001/competition/sport/4?_format=json&limit=20&offset=0`)
return { post: data }
},
head() {
return {
title: this.post.title
}
}
}
</script>
To make this call works i need to pass tree arguments to my headers. Anyone has an idea on how to do that to make it work for all api call in nuxt ?
You can set headers using the axios module for nuxt (which you already do).
Taken from their documentation:
setHeader(name, value, scopes='common')
name: Name of the header
value: Value of the header
scopes: Send only on specific type of requests.
Examples:
// Adds header: `Authorization: 123` to all requests
this.$axios.setHeader('Authorization', '123')
// Overrides `Authorization` header with new value
this.$axios.setHeader('Authorization', '456')
// Adds header: `Content-Type: application/x-www-form-urlencoded` to only
// post requests
this.$axios.setHeader('Content-Type', 'application/x-www-form-urlencoded', [
'post'
])
// Removes default Content-Type header from `post` scope
this.$axios.setHeader('Content-Type', false, ['post'])
Please refer to the documentation for more information: https://github.com/nuxt-community/axios-module#setheadername-value-scopescommon

Vue 2 triggering method on child component

I will be happy if i can either trigger and event or call a method within the vue-button component when the user object gets updated. I need to make sure that this only happens on this specific vue-button and not on any other ones on the page.
The button controls a state object that styles the button depending on response from the server. Will display red if an error response is returned or green if a successful response is returned. In ether case the button then is also disabled so users can't spam click it. I need to have the ability to reset the button from the parent when the user is updated.
I do not believe that a global event bus is the solution because i need granular control over which components respond to the event and this button is used in a lot of places.
<template>
<div class="row">
<div class="col-12">
<basic-input-field :resource="user" :set-resource="setUserProperty" property="name" label="Name"></basic-input-field>
<basic-input-field :resource="user" :set-resource="setUserProperty" property="email" label="Email"></basic-input-field>
<basic-input-field :resource="user" :set-resource="setUserProperty" property="password" label="Password"></basic-input-field>
<vue-button :on-click="updateUser" label="Save"></vue-button>
</div>
</div>
</template>
<script>
import axios from 'axios';
import basicInputField from '../general/forms/basic-input-field.vue';
import vueButton from '../general/buttons/vue-button.vue';
export default {
name: 'user',
data() {
return {
};
},
mixins: [],
components: {
basicInputField,
vueButton
},
computed: {
user() {
return this.$store.state.user;
},
},
mounted() {
this.$httpGet('user', {id: 5});
},
watch: {
'user': function (newUser) {
// I want to trigger an event inside the component vue-button
// I do not want to trigger then event in every vue-button component on the page just this vue-button
// I need to call a resetStatus method within the vue-button component when the user changes
// This would have worked in vue 1 with an event 'resetStatus' that would call the method in the vue-button component
this.$broadcast('resetStatus');
}
},
methods: {
setUserProperty(property, value) {
this.$store.commit('UPDATE_MODULE_RESOURCE', {module: 'user', resource: property, value: value});
},
updateUser() {
return this.$httpPut('user', {id: this.user.id}, this.user);
}
},
};
</script>