Consider the following example Vue component:
Template:
<template>
<input
id="pin"
v-model="pin"
type="password"
name="pin"
placeholder="Pin"
#input="removeSpace($event.target)"
/>
</template>
Script:
<script>
import { ref } from 'vue'
const pin = ref('')
const removeSpace = (target) => {
pin.value = target.value.replace(/\s/g, '')
}
</script>
How would I go about moving the removeSpace function in this component to VueX4 store? So I can use it in multiple components? Can't seem to get it to work, the input field doesn't update.
I have tried something as follows:
Template:
<template>
<input
id="test"
v-model="store.state.testPin"
type="text"
name="test"
placeholder="test"
#input="store.dispatch('removeSpace', $event.target)"
/>
</template>
VueX store:
import { createStore } from 'vuex'
const store = createStore({
state: {
testPin: ''
},
actions: {
removeSpace(state, target) {
state.testPin = target.value.replace(/\s/g, '')
}
}
})
export default store
Related
Index.js from the Store Folder
import Vue from "vue";
import Vuex from "vuex";
import state from "../store/state";
import mutations from "../store/mutations";
import actions from "../store/actions";
Vue.use(Vuex);
export default new Vuex.Store({
state,
mutations,
actions,
});
State.js
/* eslint-disable */
import cats from "../data/cats.js";
import dogs from "../data/dogs.js";
export default {
cats,
dogs,
};
Actions.js
// method add pet -> dispatch action -> mutate -> state changes
export default {
addPet: ({ commit }, payload) => {
console.log("payload iss", payload);
commit("appendPet", payload);
},
};
Mutations.js
export default {
appendPet: (state, { specie, pet }) => {
console.log("specie and pet are", specie, pet);
state[specie].append(pet);
},
};
Home.vue
<template>
<div class="home">
<h1>Adopt a New Best Friend</h1>
<button #click="formShow" class="btn btn-primary">Add New Pet</button>
<b-form #submit.prevent="handleSubmit" v-if="showForm === true">
<b-form-group id="input-group-2" label="Pet's Name:" label-for="input-2">
<b-form-input
id="input-2"
v-model="formData.name"
type="text"
placeholder="Enter name"
required
></b-form-input>
</b-form-group>
<b-form-group id="input-group-3" label="Specie:" label-for="input-3">
<b-form-select
id="input-3"
v-model="formData.specie"
:options="['cats', 'dogs']"
required
></b-form-select>
</b-form-group>
<b-form-group id="input-group-2" label="Pet's Age:" label-for="input-2">
<b-form-input
id="input-2"
v-model="formData.age"
type="number"
placeholder="Enter Age"
required
></b-form-input>
</b-form-group>
<b-button type="submit" variant="primary">Submit</b-button>
<b-button type="reset" variant="danger">Reset</b-button>
</b-form>
<!-- <b-card class="mt-3" header="Form Data Result">
<pre class="m-0">{{ formData }}</pre>
</b-card> -->
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
name: "Home",
data() {
return {
showForm: false,
formData: {
age: 0,
specie: null,
name: "",
},
};
},
methods: {
...mapActions["appendPet"],
formShow() {
this.showForm = !this.showForm;
},
// mapActions and appendPet do not show on console
handleSubmit() {
console.log("hello this is the handle submit method");
// console.log("mapActions", ...mapActions);
// console.log("appendPet gnnn.................", this.appendPet);
const { specie, age, name } = this.formData;
console.log("specie , age and name are", specie, age, name);
const payload = { specie, pet: { name, age } };
this.appendPet(payload);
},
},
};
</script>
The template part of my home.vue is working fine but I am not being able to mutate my state. I am getting nothing when I console.out the mapActions, I think that the appendPet method that mutates the state is not being called as the payload is not reaching the Action.js , not reaching the actions of my store. May be the mapActions is not being executed as through it, I am calling the appendPet.
mapActions is a function and you are using it as an Object:
You have: ...mapActions["appendPet"]
And you need: ...mapActions(["appendPet"])
Check the docs for further info:
You are using mapActions on mutate function.
I receive the following warning:
[Vue warn]: Extraneous non-emits event listeners (addData) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option.
at <UserData onAddData=fn<bound dataSubmit> >
at <App>
in my Vue3 app. I use emits:["add-data"] in UserData.vue but the warning still appears.
Here are the relevant parts of the vue project:
app.vue
<template>
<div class="columns">
<div class="column">
<user-data #add-data="dataSubmit" />
</div>
<div class="column">
<active-user #delete-user="deleteUser" v-for="user in users" :key="user.id" :name="user.name" :age="user.age" :id="user.id" />
</div>
</div>
</template>
<script>
export default {
data() {
return {
users: []
}
},
methods: {
dataSubmit(name, age) {},
deleteUser(id) {}
}
}
</script>
UserData.vue
<template>
<h2>Add new user:</h2>
<form #submit.prevent="submitData">
<label>Name*</label>
<input type="text" v-model="name" placeholder="Name" />
<label>Age*</label>
<input type="text" v-model="age" placeholder="Age" />
<button>add</button>
</form>
</template>
<script>
export default {
emits: ["add-data"],
data() {
return {
name: "",
age: ""
}
},
methods: {
submitData() {
this.$emit("add-data", this.name, this.age)
}
}
}
</script>
main.js
import { createApp } from 'vue'
import UserData from './components/UserData.vue'
import ActiveUser from './components/ActiveUser.vue'
import App from './App.vue'
const app = createApp(App);
app.component("active-user", ActiveUser);
app.component("user-data", UserData);
app.mount('#app')
It works fine, but it just displays the warning.
If I change the emits part to emits: ["add-data", "addData"] the warning disappears.
There is an open bug for this:
https://github.com/vuejs/vue-next/issues/2540
For now you'll need to use emits: ['addData'] to avoid the warning.
I am new to vue.
so here is my vue template
<template>
<div class="app-container">
<el-dialog :visible="dialogVisible"
title="View order"
:before-close="() => dialogVisible = false">
<order-form :oneorder="oneorderdata" #success="handleAdd" #cancel="dialogVisible = false" />
</el-dialog>
<el-table v-loading="loading" :data="orders" border>
<el-table-column prop="order.id" label="Id" />
<el-table-column prop="order.fullName" label="Full Name" />
<el-table-column prop="order.address.name" label="Address" />
<el-table-column prop="order.status" label="Status" />
<el-table-column label="View" prop="order.id" min-width="150">
<template slot-scope="{row}">
<el-col style="flex: 0">
<el-button icon="el-icon-plus" type="primary" #click="senddata(row.order)">
View Order
</el-button>
</el-col>
</template>
</el-table-column>
</el-table>
</div>
</template>
when i click the button the dialog is true so i call the order-form component
and i want to pass data to it. component is opening normally but error said that oneorderview is not found although i am showing it in console normally in senddata method.
i tried
v-bind:oneorder="oneorderview"
but it didn't work , it it shows no error.
thanks in advance.
this is my script
<script lang="ts">
import Vue from "vue";
import Component from "vue-class-component";
import { getOrders } from "#/api/store";
import dataStore from "#/store/modules/data";
import { OrderView } from "../../../models";
import { OrderInput } from "#/models";
import DataModule from "#/store/modules/data";
import OrderForm from "./form.vue";
type _OrderInput = OrderInput;
interface TableRowData {
editMode: boolean;
order: OrderView;
editedOrder: _OrderInput;
}
#Component({
components: {
OrderForm,
},
})
export default class Orders extends Vue {
orders: TableRowData[] = [];
oneorderview: any;
loading = false;
dialogVisible = false;
searchKey = "";
async created() {
await DataModule.ensureLoaded();
this.fetchData();
}
async fetchData() {
this.loading = true;
await dataStore.loadorders();
if (dataStore.orders.hasLoaded) {
this.orders = dataStore.orders.data.map(order => ({
editMode: false,
order,
editedOrder: { ...order }
}));
}
this.loading = false;
}
handleAdd() {
this.dialogVisible = false;
}
senddata(selectedorder: any) {
this.oneorderview = selectedorder;
this.dialogVisible = true;
console.log('hahahaha', this.oneorderview);
}
}
</script>
and how to handle data in child component.
You're using class components in Vue typescript. To pass props you need to either use the classic Vue properties or by annotating your class members:
import { Component, Prop } from 'vue-property-decorator'
#Component
export default class App extends Vue {
// Makes a "exampleProperty" a component prop with the default value of 'Example'
#Prop({default: 'Example'})
exampleProperty: string
}
I'm new with VueJS, and I'm creating a VueJS app where you can get some informations about a Github User,
(example: https://api.github.com/users/versifiction)
I created a store with VueX, but I need to update the value written by the user in the input,
My "inputValue" is always at "" (its default value) and when I type inside the input, the store value still at ""
I tried this :
Input.vue
<template>
<div class="input">
<input
type="text"
:placeholder="placeholder"
v-model="inputValue"
#change="setInputValue(inputValue)"
#keyup.enter="getResult(inputValue)"
/>
<input type="submit" #click="getResult(inputValue)" />
</div>
</template>
<script>
import store from "../store";
export default {
name: "Input",
props: {
placeholder: String,
},
computed: {
inputValue: () => store.state.inputValue,
},
methods: {
setInputValue: (payload) => {
store.commit("setInputValue", payload);
}
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped></style>
and this :
store/index.js
import Vue from "vue";
import Vuex from "vuex";
import axios from "axios";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
inputValue: "",
},
getters: {
getInputValue(state) {
return state.inputValue;
}
},
mutations: {
setInputValue(state, payload) {
console.log("setInputValue");
console.log("payload ", payload);
state.inputValue = payload;
},
},
});
According to the vuex docs in the form handling section you should do :
:value="inputValue"
#change="setInputValue"
and
methods: {
setInputValue: (event) => {
store.commit("setInputValue", event.target.value);
}
}
The simplest and elegant way to bind vuex and a component would be to use computed properties.
The above code would become,
<input
type="text"
:placeholder="placeholder"
v-model="inputValue"
#keyup.enter="getResult(inputValue)"
/>
and inside your computed properties, you'll need to replace inputValue with following code.
computed: {
inputValue: {
set(val){
this.$store.commit(‘mutationName’, val)
},
get() {
return this.$store.stateName
}
}
}
I use Vue.js 2.5.13 + Vee-Validate 2.0.3. My code structure is:
./component-one.vue:
<template>
<div>
<input type="text" name="input_one" id="input_one"
v-model="input_one"
v-validate="'required'"
:class="{'is-danger': errors.has('input_one')}" />
<component-two></component-two>
<button #click="submitForm">Submit!</button>
</div>
</template>
<script>
import Vue from 'vue'
import VeeValidate from 'vee-validate'
import ComponentTwo from './component-two.vue'
Vue.use(VeeValidate, {
events: 'input|blur'
})
export default {
name: "component-one",
components: {
ComponentTwo
},
data() {
return {
input_one: '',
}
},
methods: {
submitForm: function () {
// Validate before submit form
this.$validator.validateAll().then((result) => {
if (result) {
alert('From Submitted!')
return
}
alert('Correct them errors!')
})
}
}
}
</script>
./component-two.vue:
<template>
<div>
<input type="text" name="input_two" id="input_two"
v-model="input_two"
v-validate="'required'"
:class="{'is-danger': errors.has('input_two')}" />
</div>
</template>
<script>
export default {
name: "component-two",
data() {
return {
input_two: ''
}
}
}
</script>
How to validate field from ComponentTwo, when I click to button #click="submitForm" in ComponentOne (for save all form data at this component).
I have huge form, who made by similar small Vue-components (all collected in ComponentOne), would be great to validate all of them in one place.
You can trigger validateAll() on the component manually trough vue reference, like:
parent component
<template>
<div>
<input type="text" name="input_one" id="input_one"
v-model="input_one"
v-validate="'required'"
:class="{'is-danger': errors.has('input_one')}" />
<component-two ref="validateMe"></component-two>
<button #click="submitForm">Submit!</button>
</div>
</template>
<script>
import Vue from 'vue'
import VeeValidate from 'vee-validate'
import ComponentTwo from './component-two.vue'
Vue.use(VeeValidate, {
events: 'input|blur'
})
export default {
name: "component-one",
components: {
ComponentTwo
},
data() {
return {
input_one: '',
}
},
methods: {
submitForm: async function () {
// Validate before submit form
const result = await this.$validator.validateAll() && await this.$refs.validateMe.$validator.validateAll()
if (result) {
alert('From Submitted!')
return
}
alert('Correct them errors!')
}
}
}
</script>