I have a customized toast component:
<template>
<div v-show="isToastVisible" class="toast-wrapper">
<div class="toast-conatiner">
{{ message }}
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
props: {
message: {
type: String,
default: '',
},
isToastVisible: {
type: Boolean,
default: false,
},
},
};
</script>
I pass state to the property:
<toast :is-toast-visible="isToastVisible" message="please say the word again"/>
I have an action to control the value of isToastVisbible. When I want to toast some message , I set the state to true.
What I want is to make the toast auto closed in 3 seconds, it means that I have to call a setTimeout function when I set isToastVisbible to true.
commit('SET_TOAST_VISIBLILITY', true);
setTimeout(() => {
commit('SET_TOAST_VISIBLILITY', false);
}, 3000);
I try to add watch on isToastVisbible, but it only can works at first time.
<template>
<div v-show="localVisible" class="toast-wrapper">
<div class="toast-conatiner">
{{ message }}
</div>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex';
export default {
props: {
message: {
type: String,
default: '',
},
isToastVisible: {
type: Boolean,
default: false,
},
},
data: () => ({
localVisible: false,
}),
watch: {
isToastVisible(v) {
if (v) {
this.localVisible = true;
setTimeout(() => {
this.localVisible = false;
}, 3000);
}
},
},
};
I am trying to do a better design on the component, but I don't know what is the best practice of this. Hope somebody could give me a hand, thanks a lot.
Try to use toast component with v-model directive. In toast you may use timer to emit hide event:
setTimeout(() => this.$emit('input'), 3000);
On the parent component you may use this one:
<toast-component v-model="isVisible"></toast-component>
Or without v-model sugar:
<toast-component :value="isVisible" #input="isVisible = false"></toast-component>
Related
I have two component vue
My first component / parent component like this :
<template>
...
<page-listing
:lastPage="lastPage"
:perPage="perPage"
:page="page"
:changePage="changePage"
></page-listing>
...
</b-row>
</template>
<script>
import { mapActions, mapGetters } from "vuex";
import PageListing from "#/views/app/reports/PageListing";
export default {
components: {
"page-listing": PageListing
},
methods: {
...mapActions(["getReport"]),
changePage(pageNum) {
this.loadPage(pageNum);
},
loadPage(page){
this.geReport({page});
},
},
computed: {
...mapGetters({
perPage: "reportPerpage",
lastPage: "reportLastPage",
}),
page: {
get() {
return this.$store.getters.reportPage
},
set(newValue) {
this.$store.dispatch('setReportPage', newValue);
}
},
},
watch: {
search() {
this.page = 1;
},
},
mounted() {
this.loadPage();
}
};
</script>
My second component /child component like this :
<template>
...
<b-colxx xxs="12">
<b-pagination-nav
:number-of-pages="lastPage"
:link-gen="linkGen"
:value="page"
#change="(a)=>changePage(a)"
:per-page="perPage"
align="center"
use-router
>
</b-pagination-nav>
</b-colxx>
...
</template>
<script>
export default {
props: [
"items",
"lastPage",
"perPage",
"page",
"changePage",
],
methods: {
linkGen(pageNum) {
return pageNum === 1 ? '?' : `?page=${pageNum}`
},
}
};
</script>
The pagination works. When I load the page, the pagination appears and I click page 2, the list success appears. But page number two is not selected. I have to click 2x so it can be selected
How can I solve this problem?
Please help. Thanks
Note :
I use https://bootstrap-vue.org/docs/components/pagination-nav?page=2
Variable named jso disappears when the page is refreshed. Also, is there any other way to send store information than method?
It will work when the page is refreshed and reopened without using a button.
view/userProfile.vue
<template>
<div>
<v-list>
<v-list-item>
{{userdata['username']}}</v-list-item>
<v-list-item> {{userdata['id']}}</v-list-item>
<v-list-item> {{userdata['email']}}</v-list-item>
<v-list-item> {{userdata['phone_number']}}</v-list-item>
<v-list-item> {{userdata['first_name']}} {{userdata['last_name']}}</v-list-item>
<v-list-item > {{userdata['gender']}}</v-list-item>
{{userdata['educational_status']}}
</v-list>
<hr>
{{this.profileData.gender}}
{{jso}} --> variable that disappears on page refresh
</div>
</template>
<script>
Only jso disappears on refresh page:
import Vuetify from "vuetify"
import {UserData} from "../../store/userModule";
import {JsonChoiceData} from "../../store/choiceStore";
import jsonDict from "../../jsonFiles/data.json"
import JsonFile from "../../jsonFiles/jsonfile"
export default {
name: "userProfile",
data(){
return {
profileData:{
username:'',
first_name:'',
last_name: '',
email:'',
phone_number:'',
birthday:'',
gender:'',
educational_status:'',
martial_status:'',
},
}
},
created(){
this.$store.dispatch('initUserData')
this.$store.dispatch('inijson')
},
computed:{
jso(){
return this.$store.getters.user
},
userdata (){
for(var i in this.$store.getters.getUser){
return this.$store.getters.getUser[i]
}
return this.$store.getters.getUser},
},
methods:{
getjsondata(){
console.log(this.userdata['gender'] + "methods")
this.$store.dispatch('getJsonData',this.userdata['gender'])
console.log(this.userdata['gender'])
}
},
mounted(){
this.getjsondata()
}
}
</script>
<style scoped>
</style>
store
import JsonFiles from '../jsonFiles/jsonfile'
import Jsondict from '../jsonFiles/data.json'
import jsonfile from "../jsonFiles/jsonfile";
export const JsonChoiceData = {
state: {
user: [],
},
getters: {
user(state) {
return state.user
},
},
mutations: {
inijson(state, user) {
state.user = user
},
getsonData: function (state, userinput) {
var getJsoncleandata = jsonfile.JsonData(userinput, Jsondict.Gender)
state.user = getJsoncleandata
return getJsoncleandata
}
},
actions: {
inijson(context){
context.commit('inijson', this.getsonData)
},
getJsonData(context,userinput){
context.commit('getsonData',userinput)
}
}
}
getsonData is a mutation and shouldn't be used as payload of another mutation. You are also trying to dispatch initUserData action which is not inside your store. I think that you can try to commit getsonData mutation inside your inijson action.
mutations: {
...,
getsonData: function(state, userinput) {
const getJsoncleandata = jsonfile.JsonData(userinput, Jsondict.Gender);
state.user = getJsoncleandata;
}
...
},
actions: {
inijson(context) {
context.commit('getsonData', null)
},
...
}
Then inside created hook of your component dispach only inijson action:
...
created() {
this.$store.dispatch('inijson')
},
...
If you see strange date make sure that jsonfile.JsonData(userinput, Jsondict.Gender) doesn't return a Promise.
Instead of using global $store you can also consider to use vuex store mappers. component binding helpers
I have a <form> in vue. I send that form to server, get a JSON response, print it to console. It works fine.
However I need to take that JSON response and display it on another page. For instance, I have two .vue files: GetAnimal.vue that has the form and retrieves the animal data from an API and a DisplayAnimal.vue that displays animal's data. I need to direct the response animal data from GetAnimal.vue to DisplayAnimal.vue.
GetAnimal.vue:
<template>
<form v-on:submit.prevent="getAnimal()">
<textarea v-model = "animal"
name = "animal" type="animal" id = "animal"
placeholder="Enter your animal here">
</textarea>
<button class = "custom-button dark-button"
type="submit">Get animal</button>
</form>
</template>
<script>
import axios from 'axios';
export default {
name: 'App',
data: function() {
return {
info: '',
animal: ''
}
},
methods: {
getAnimal: function() {
axios
.get('http://localhost:8088/animalsapi?animal=' + this.animal)
.then(response => (this.info = response.data));
console.log(this.info);
}
}
}
</script>
response:
retrieves a JSON with animal data, say like this:
{
"fur-color": "yellow",
"population": 51000,
"isExtinct": false,
"isDomesticated": true
}
and I now want to give that JSON to a DisplayAnimal.vue at /viewanimal endpoint:
DisplayAnimal.vue:
<template>
<div>
<p>Animal name: {{animal}}}</p>
<p>Fur color: {{furColor}}</p>
<p>Population: {{population}}</p>
<p>Is extinct: {{isExtinct}}</p>
<p>Is domesticated: {{isDomesticated}}</p>
</div>
</template>
How would I do that? I know I can redirect via this.$router.push({ path });, but I've only used it for navigation, while here JSON response needs to be passed. Is this even a correct / good practice way of approaching this?
EDIT:
I tried this:
in GetAnimal.vue I added this data:
data: function() {
return {
animal: {
name: 'Cat',
furColor: 'red',
population: '10000',
isExtinct: false,
isDomesticated: true
}
and in DisplayAnimal.vue this:
<script>
export default {
props: {
animal: {
name: {
type: String
},
furColor: {
type: String
},
population: String,
isExtinct: String,
isDomesticated: String
}
}
}
</script>
and in GetAnimal.vue I added this:
methods: {
animals: function() {
alert("animals");
this.$router.push({name: 'viewanimal',
query: {animal: JSON.stringify(this.animal)}});
},
to try to display that test animal using the display component. However it just didn't work - I get an empty page.
Using Vuex, you can solve this easily
Working example on netlify
https://m-animalfarm.netlify.app/
code on github
https://github.com/manojkmishra/animalfarm
GetAnimal.vue ( I have disabled axios call for testing and hardcoded info)
<template>
<form v-on:submit.prevent="getAnimal()">
<textarea v-model = "animal" name = "animal" type="animal" id = "animal"
placeholder="Enter your animal here">
</textarea>
<button class = "custom-button dark-button"
type="submit">Get animal</button>
</form>
</template>
<script>
import axios from 'axios';
export default
{
name: 'App',
data: function() { return { info: '', animal: '' } },
methods: {
getAnimal: function() {
// axios
// .get('http://localhost:8088/animalsapi?animal=' + this.animal)
// .then(response => (this.info = response.data),
this.info={"fur-color": "yellow","population": 51000,"isExtinct":
false,"isDomesticated": true },
this.$store.dispatch('storeAnimals', this.info)
//);
}
}
}
</script>
DisplayAnimal.vue
<template>
<div>
<p>Animal name: {{stateAnimal.animal}}</p>
<p>Fur color: {{stateAnimal.furColor}}</p>
<p>Population: {{stateAnimal.population}}</p>
<p>Is extinct: {{stateAnimal.isExtinct}}</p>
<p>Is domesticated: {{stateAnimal.isDomesticated}}</p>
</div>
</template>
<script>
import {mapState, mapGetters} from 'vuex';
export default {
computed:{ ...mapState({ stateAnimal:state => state.modulename.stateAnimal }),
},
}
</script>
modulename.js ( store module)
export default
{
state: {stateAnimal:null, },
getters:{ },
mutations:
{ ['STORE_ANIMALS'] (state, payload)
{ state.stateAnimal = payload;
console.log('state=',state)
},
},
actions:
{ storeAnimals: ({commit}, data) =>
{ console.log('storeanim-data-',data);
commit( 'STORE_ANIMALS', data );
},
}
}
Index.js (for vuex store), you can disable persistedstate as its for saving state if page is refreshed
import Vue from 'vue'
import Vuex from 'vuex'
import modulename from './modules/modulename'
import createPersistedState from "vuex-persistedstate";
Vue.use(Vuex)
export default new Vuex.Store({
plugins: [createPersistedState({ storage: sessionStorage })],
state: { },
mutations: { },
actions: { },
modules: { modulename }
})
State is available/shared for all the components
well first of all create a second folder call it services and create service.js for you axios call- good practice and cleaner code overall.
second use vuex. this kind of data is best used with vuex.
As far as I understand GetAnimal.vue is the parent component and you wish to display it in the child component DisplayAnimal.vue.
If so and you wish to see if this works just use props.
you can also send that same information or any other information for the child back to the parent using an $emit().
STRONGLY recommended to use vuex in order to manage the state
Vue.component('product',{
props:{
message:{
type:String,
required:true,
default:'Hi.'
}
},
template:`<div>{{message}}</div>`,
data(){...}
})
//html in the other component you axios call is in this component //<product meesage="hello"></product>
I would pass the animal name/id as a route param to the display page and have that component responsible for fetching and displaying the relevant animal data. This avoids the situation where a user could visit the display page directly via the URL and see an incomplete page.
In situations where you want to share local state between pages, as others have pointed out you'd probably want to use Vuex.
EDIT:
I'm adding some code to my answer as requested by the OP.
Routes:
const routes = [
{ path: "/", component: SearchAnimals },
{ path: "/viewanimal/:name", component: DisplayAnimal, name: "displayAnimal" }
];
DisplayAnimal.vue:
<template>
<div>
<p>Animal name: {{animal.name}}</p>
<p>Fur color: {{animal.furColor}}</p>
<p>Population: {{animal.population}}</p>
<p>Is extinct: {{animal.isExtinct}}</p>
<p>Is domesticated: {{animal.isDomesticated}}</p>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "DisplayAnimal",
data: () => ({
animal: {}
}),
methods: {
fetchAnimal(name) {
axios
.get(`http://localhost:8088/animalsapi?animal=${name}`)
.then(response => {
this.animal = response.data;
});
}
},
created() {
this.fetchAnimal(this.$route.params.name);
}
};
</script>
SearchAnimals.vue:
<template>
<form v-on:submit.prevent="onSubmit">
<textarea
v-model="animal"
name="animal"
type="animal"
id="animal"
placeholder="Enter your animal here"
></textarea>
<button type="submit">Get animal</button>
</form>
</template>
<script>
export default {
name: "SearchAnimal",
data: () => ({
animal: ""
}),
methods: {
onSubmit() {
this.$router.push({
name: "displayAnimal",
params: { name: this.animal }
});
}
}
};
</script>
Obviously this is a bare-bones example, so doesn't contain any error handling etc., but it should get you up and running.
been trying to use https://element.eleme.io/#/en-US/component/message
in my component via
import Message from "#/components/ElementUi/Message";
and inside that component just normal include from element-ui
<template>
<div>
<el-button :plain="true" #click="open">{{label}}</el-button>
</div>
</template>
<script>
import { Message, Button } from "element-ui";
// import lang from "element-ui/lib/locale/lang/ru-RU";
// import locale from "element-ui/lib/locale";
import "element-ui/lib/theme-chalk/index.css";
import "../../../public/css/element-variables.scss";
//locale.use(lang);
import Vue from "vue";
Vue.use(Message);
Vue.use(Button);
export default {
props: ["label", "content", "type", "duration", "run"],
data() {
return {};
},
methods: {
open() {
Message({
showClose: true,
message: this.content,
type: this.type,
duration: this.duration,
dangerouslyUseHTMLString: true
});
}
},
created() {
if (this.run == 1) {
// this.open();
}
},
beforeCreate() {}
};
</script>
What happens when my component updates this annoying message automatically display with 0 content, but I want to to be rendered only when some conditions are met
like
<div
v-if="searchCompleted.status && searchCompleted.codingType=='reverse' && searchCompleted.fullMatch == false "
>
<message
:duration="3000"
:label="'info'"
:content="'<p>No data</p>'"
:type="'message'"
></message>
</div>
</div>
The documentation lacks of information how not to show the element on initial render of component, so there must be some pure javascript option how to disable that thing and enable when needed?
I'm learning about Vuex right now and I'm running into some trouble. While trying to create a getter on my vuex instance I'm getting this error when trying to render from one of my components:
Getter should be a function but "getters.doubleCounter" is 20
store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
counter: 10
},
getters: {
doubleCounter: state => {
return state.counter * 2;
}
}
});
MY COMPONENT:
<template>
<div>
<p>This is a message from services</p>
<button v-on:click="increment">+</button>
<button v-on:click="decrement">-</button>
{{ counter }}
</div>
</template>
<script>
export default {
computed: {
counter() {
return this.$store.getters.doubleCounter;
},
},
methods: {
increment: function () {
this.$store.state.counter++
},
decrement: function () {
this.$store.state.counter--
}
}
}
</script>
Again when I try to render the page that this component is on. It fails while giving me the title error message in the console. Any help would be great! Thanks!
Try this.
doubleCounter: (state) => {
return state.counter * 2;
}
I'm not sure what's causing your error but you are certainly not meant to be directly manipulating state outside of a mutation.
At no point should your code ever assign anything directly to a state property. For example, this is bad
this.$store.state.doubleCounter++
Here's what you should have.
Vue.component('counter', {
template: `
<div>
<p>This is a message from services</p>
<button v-on:click="increment">+</button>
<button v-on:click="decrement">-</button>
{{ counter }}
</div>
`,
computed: {
counter() {
return this.$store.getters.doubleCounter;
},
},
methods: {
increment: function () {
this.$store.commit('increment')
},
decrement: function () {
this.$store.commit('decrement')
}
}
})
const store = new Vuex.Store({
state: {
counter: 10
},
mutations: {
increment(state) { state.counter++ },
decrement(state) { state.counter-- }
},
getters: {
doubleCounter: state => {
return state.counter * 2;
}
}
})
new Vue({
el: '#app',
store
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<script src="https://unpkg.com/vuex#3.0.1/dist/vuex.js"></script>
<div id="app"><counter/></div>