How to trigger confetti with if condition - vue.js

I have a confetti that I am using it with this package: https://www.npmjs.com/package/vue-confetti
Here is my template:
<template>
<div>{{ confetti }}</div>
</template>
there is an if condition in the button which I need to render that confetti in the same condition:
But I want to use it at the top of my component because I want to use it more efficiently on the full screen.
data() {
return {
array: [],
confetti: null,
confettiEnabled: false,
};
},
methods: {
getArray() {
this.isLoading = true;
axios
.get(`api`, {
params: {},
})
.then((response) => {
this.array = response.data.data;
this.arrayLength = this.array.length;
})
.finally(() => {
this.isLoading = false;
});
this.confettiEnabled = true;
console.log(this.confettiEnabled);
},
},
mounted() {
this.getArray();
},
and I created watcher for the component:
watch: {
confetti: {
handler() {
if (this.documents.length == 0 && this.confettiEnabled) {
this.confettiEnabled = true;
this.$confetti.start();
setTimeout(() => {
this.$confetti.stop();
}, 3000);
}
},
immediate: true, // This ensures the watcher is triggered upon creation
},
},
But this logic doesnt work even and my confetti never triggers.

Related

Test vue watcher in jasmine which triggers a fetch method

I've implemented a small autocomplete which fetches information based upon the search input. In the frontend everythign works fine but when I try to test my watcher it always states that the items array is empty. I guess something in my async/await structure in the test is wrong but I cannot figure out what it is.
<template>
<v-autocomplete
v-model="inputText"
:items="items"
:search-input.sync="search"
></v-autocomplete>
</template>
<script>
export default {
name: 'Autocomplete',
data() {
return {
items: [],
inputText: '',
search: '',
};
},
watch: {
search(val) {
this.getDataFromApi(val);
},
},
methods: {
getDataFromApi(val) {
this.loading = true;
return fetch(
'https://jsonplaceholder.typicode.com/todos/' +
val
)
.then(response => response.json())
.then(data => {
this.items.push(data);
})
.finally(() => {
this.loading = false;
});
},
},
};
</script>
it('validates the Autocomplete properties', async function(done) {
// GIVEN an instantiated Search
const wrapper = shallowMount(Autocomplete, {
localVue: this.localVue,
propsData: mockProps,
});
await wrapper.setData({ search: 'asdf' });
expect(wrapper.vm._data.items).toEqual([
{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}
]);
});

Why does Vuetify Autocomplete not selecting the data being set?

I use Vuetify autocomplete as a reusable component to display a list of jobs in key/value pairs. In creating record, the component works fine but when editing where data should be filled in, the data has the value but not showing on the component.
JobDropdownSelector.vue:
................................................................................................................................................
<template>
<v-autocomplete
v-model="job"
label="Job Designation"
item-value="id"
item-text="name"
return-object
:items="jobs"
#change="onChange"
>
</v-autocomplete>
</template>
<script>
export default {
props: {
selectedJob: {
type: Object,
default: null
}
},
data() {
return {
jobs: [],
job: null
};
},
methods: {
getDataFromApi() {
axios.get("api/jobs")
.then(response => {
this.jobs = response.data.data;
})
.catch(error => {
console.log(error);
reject();
});
},
onChange(e) {
this.job = e;
this.$emit("onChange", e);
}
},
watch: {
selectedJob: {
deep: true,
immediate: true,
handler(newValue, oldValue) {
this.job = newValue;
}
}
},
mounted() {
this.getDataFromApi();
}
};
</script>
EditForm.vue:
................................................................................................................................................
<template>
<div>
<JobDropdownSelector
v-model="job"
:selectedJob="job"
#onChange="onChangeJob"
>
</JobDropdownSelector>
</div>
</template>
<script>
import JobDropdownSelector from "../components/JobDropdownSelector";
export default {
components: {
JobDropdownSelector
},
data() {
return {
job: null
};
},
methods: {
onChangeJob(e) {
this.job = e;
},
getInitialJob() {
axios.get("api/jobs/22").then(response => {
this.job = response.data.data;
});
}
},
mounted() {
this.getInitialJob();
}
};
</script>
Display
Console
It looks like you overriding job twice: on the onChangeJob method and via v-model on JobDropdownSelector.
You can try use input event to update modek instead of emitting additional event.
JobDropdownSelector.vue
methods: {
onChange(e) {
// You don't need set job because it's connected via v-model.
// this.job = e;
this.$emit("input", e);
}
}
This will cause that job in EditForm.vue should update automatically after dropdown change.

Don't send Axios request when user selects a result

I'm using v-autocomplete from vuetify.js to retrieve a list of values from API Server.
It works fine and my list of values is not empty.
But my problem is when I select the correct value from this list. My script sends another request to server to retrieve another autocomplete list.
Do you have any idea to avoid to send request when a result is selected by the user ? Or to send request only when a key is down ?
My component :
<template>
<div>
<v-autocomplete
v-model="selectValeur"
:loading="loading"
:search-input.sync="search"
:items="resultatsAutocomplete"
class="mb-4"
hide-no-data
hide-details
:label="recherche.label"
></v-autocomplete>
</div>
</template>
<script>
export default {
props: {
recherche: {
type: Object,
default: null,
},
},
data: () => ({
selectValeur: null,
loading: false,
search: null,
resultatsAutocomplete: [],
}),
watch: {
selectValeur(oldval, val) {
console.log(oldval)
console.log(val)
},
search(val) {
val && val !== this.selectValeur && this.fetchEntriesDebounced(val)
console.log(val)
if (!val) {
this.resultatsAutocomplete = []
}
},
},
methods: {
fetchEntriesDebounced(val) {
// cancel pending call
clearTimeout(this._timerId)
// delay new call 500ms
this._timerId = setTimeout(() => {
this.querySelections(val)
}, 500)
},
async querySelections(v) {
if (v.length > 1) {
this.loading = true
try {
const result = await this.$axios.$get(
'myapi/myurl',
{
params: {
racine: v,
},
}
)
this.resultatsAutocomplete = result
console.log(this.resultatsAutocomplete)
this.loading = false
} catch (err) {
console.log(err)
this.loading = false
}
} else {
this.resultatsAutocomplete = []
}
},
},
}
</script>
Thanks,
selectValeur would no longer be null if the user has selected a value, so you could update search() to return if selectValeur is truthy:
export default {
watch: {
search(val) {
if (this.selectValeur) {
// value already selected
return
}
//...
}
}
}
Or you could use vm.$watch on the search property to be able to stop the watcher when selectValeur is set:
export default {
mounted() {
this._unwatchSearch = this.$watch('search', val => {
val && val !== this.selectValeur && this.fetchEntriesDebounced(val)
if (!val) {
this.resultatsAutocomplete = []
}
})
},
watch: {
selectValeur(val) {
if (val && this._unwatchSearch) {
this._unwatchSearch()
}
}
}
}
I found a solution to my problem.
I used the #keyup event to send the axios request and I deleted the watcher on search.
So, the API request are only sent when I press a key.
<template>
<div>
<v-autocomplete
v-model="selectValeur"
:loading="loading"
:items="resultatsAutocomplete"
:search-input.sync="search"
class="mb-4"
hide-no-data
hide-details
:label="recherche.label"
#keyup="keyupSearch"
></v-autocomplete>
</div>
</template>
<script>
export default {
props: {
recherche: {
type: Object,
default: null,
},
},
data: () => ({
selectValeur: null,
loading: false,
resultatsAutocomplete: [],
search: '',
}),
methods: {
keyupSearch(val) {
val &&
val !== this.selectValeur &&
this.fetchEntriesDebounced(this.search)
if (!val) {
this.resultatsAutocomplete = []
}
},
fetchEntriesDebounced(val) {
// cancel pending call
clearTimeout(this._timerId)
// delay new call 500ms
this._timerId = setTimeout(() => {
this.querySelections(val)
}, 500)
},
async querySelections(v) {
if (v.length > 1) {
this.loading = true
try {
const result = await this.$axios.$get(
'my-api/my-url',
{
params: {
sid: this.$route.params.sid,
service: this.$route.params.service,
type: this.recherche.mode,
racine: v,
},
}
)
this.resultatsAutocomplete = result
console.log(this.resultatsAutocomplete)
this.loading = false
} catch (err) {
console.log(err)
this.loading = false
}
} else {
this.resultatsAutocomplete = []
}
},
},
}
</script>

Variable not updated after vuex mutation

I am creating a settings page, where I fetch some data from the API and I am using Vuex to handle mutations.
I can see that the Vuex completes properly, but value for my dailyCount variable doesn't update in frontend.
This is my Settings component:
<template>
<div>
<div class="row col">
<h1>Settings</h1>
</div>
<div class="row col">
<div class="well">
<form class="form-inline">
<input type="number" v-model="dailyCount" />
{{ dailyCount }}
</form>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'settings',
data () {
return {
dailyCount: 500
};
},
created () {
this.$store.dispatch('settings/fetchSetting');
},
computed: {
isLoading() {
return this.$store.getters['user/isLoading'];
},
hasError() {
return this.$store.getters['user/hasError'];
},
error() {
return this.$store.getters['user/error'];
},
},
}
</script>
I do mutations here:
import SettingsAPI from '../api/settings';
export default {
namespaced: true,
state: {
isLoading: false,
error: null,
settings: null,
},
getters: {
isLoading (state) {
return state.isLoading;
},
hasError (state) {
return state.error !== null;
},
error (state) {
return state.error;
},
user (state) {
return state.user;
},
},
mutations: {
['FETCHING_SETTINGS'](state) {
state.isLoading = true;
state.error = null;
state.settings = null;
},
['FETCHING_SETTINGS_SUCCESS'](state, settings) {
state.isLoading = false;
state.error = null;
state.settings = settings;
},
['FETCHING_SETTINGS_ERROR'](state, error) {
state.isLoading = false;
state.error = error;
state.settings = null;
},
},
actions: {
fetchSetting ({commit}) {
commit('FETCHING_SETTINGS');
return SettingsAPI.get()
.then(res => {commit('FETCHING_SETTINGS_SUCCESS', res.data);})
.catch(err => commit('FETCHING_SETTINGS_ERROR', err));
},
},
}
And call to a server is done here (api/settings.js - it is imported in mutation file):
import axios from 'axios';
export default {
get() {
return axios.get('/user');
},
}
Can you see what am I doing wrong? I am trying to debug it using Vuejs debug toolbar, but all seems to work fine.
You need to get store state from vuex and inject to Vue component, either by this.$store.state or this.$store.getters.
For example:
<script>
export default {
name: 'settings',
data () {
return {
dailyCount: 500
};
},
created () {
this.$store.dispatch('settings/fetchSetting');
},
computed: {
isLoading() {
return this.$store.getters['user/isLoading'];
},
hasError() {
return this.$store.getters['user/hasError'];
},
error() {
return this.$store.getters['user/error'];
},
settings() {
return this.$store.state.settings
}
},
watch: {
settings () {
this.dailyCount = this.settings.dailyCount
}
}
}
</script>

Computed property "main_image" was assigned to but it has no setter

How can I fix this error "Computed property "main_image" was assigned to but it has no setter"?
I'm trying to switch main_image every 5s (random). This is my code, check created method and setInterval.
<template>
<div class="main-image">
<img v-bind:src="main_image">
</div>
<div class="image-list>
<div v-for="img in images" class="item"><img src="img.image"></div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Item',
data () {
return {
item: [],
images: [],
}
},
methods: {
fetchImages() {
axios.get(`/api/item/${this.$route.params.id}/${this.$route.params.attribute}/images/`)
.then(response => {
this.images = response.data
})
.catch(e => {
this.images = []
this.errors.push(e)
})
},
},
computed: {
main_image() {
if (typeof this.item[this.$route.params.attribute] !== 'undefined') {
return this.item[this.$route.params.attribute].image_url
}
},
},
watch: {
'$route' (to, from) {
this.fetchImages()
}
},
created () {
axios.get(`/api/item/${this.$route.params.id}/`)
.then(response => {
this.item = response.data
})
.catch(e => {
this.errors.push(e)
})
this.fetchImages();
self = this
setInterval(function(){
self.main_image = self.images[Math.floor(Math.random()*self.images.length)].image;
}, 5000);
},
}
</script>
Looks like you want the following to happen...
main_image is initially null / undefined
After the request to /api/item/${this.$route.params.id}/ completes, it should be this.item[this.$route.params.attribute].image_url (if it exists)
After the request to /api/item/${this.$route.params.id}/${this.$route.params.attribute}/images/ completes, it should randomly pick one of the response images every 5 seconds.
I'd forget about using a computed property as that is clearly not what you want. Instead, try this
data() {
return {
item: [],
images: [],
main_image: '',
intervalId: null
}
},
methods: {
fetchImages() {
return axios.get(...)...
}
},
created () {
axios.get(`/api/item/${this.$route.params.id}/`).then(res => {
this.item = res.data
this.main_image = this.item[this.$route.params.attribute] && this.item[this.$route.params.attribute].image_url
this.fetchImages().then(() => {
this.intervalId = setInterval(() => {
this.main_image = this.images[Math.floor(Math.random()*this.images.length)].image;
})
})
}).catch(...)
},
beforeDestroy () {
clearInterval(this.intervalId) // very important
}
You have to add setter and getter for your computed proterty.
computed: {
main_image: {
get() {
return typeof this.item[this.$route.params.attribute] !== 'undefined' && this.item[this.$route.params.attribute].image_url
},
set(newValue) {
return newValue;
},
},
},