This is the parent page that I pass the data "time"&"breedKey" to child
<template>
<MyChildComponent v-bind:breedKey="breedKey" v-bind:time="time"> </MyChildComponent>
</template>
<script>
data() {
return {
time:[]
breedKey:[]
}
},
<script>
This is the child page that I successfully get the value, but value is not updated when the value in parent is changed.
props: ["breedKey", "time"],
data() {
return {
thedate: this.time,
topic: this.breedKey
}
The data are only initialized with the props value.
You can directly reference to the props if you want to have reactivity.
Vue.config.devtools = false;
Vue.config.productionTip = false;
const MyChildComponent = Vue.component('mychildcomponent', {
template: '#mychildcomponent',
props: ["breedKey", "time"]
})
var app = new Vue({
el: '#app',
components: {
MyChildComponent
},
data() {
return {
time : [],
breedKey: []
}
},
methods: {
addTime() {
this.time.push(Math.floor(Math.random() * 100));
},
addKey() {
this.breedKey.push(Math.floor(Math.random() * 100));
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex#3.1.2/dist/vuex.js"></script>
<div id="app">
<div>
<button #click="addTime">Add time</button>
<button #click="addKey">Add key</button>
<mychildcomponent v-bind:breed-key="breedKey" v-bind:time="time" />
</div>
</div>
<div>
<div style="display:none">
<div id="mychildcomponent">
<div>
time : {{ time }}
breedKey : {{ breedKey }}
</div>
</div>
</div>
Related
I have two child components I have to pass dynamically props from first child to parent and from parent to second child.
Parent
<script>
data: () => ({
model: {}
}),
methods: {
changeData(payload) {
this.model.personalData = {...payload}
}
}
</script>
<template>
<first-child #changeData="(payload) => changeData(payload)"/>
<second-child :enter-object="model" />
</template>
Child one
<script>
data: () => ({
model: {}
}),
methods: {
changeData() {
this.$emit("changeData", this.model);
}
}
</script>
<template>
<v-text-field v-model="model.name" #input="changeData()">
<v-text-field v-model="model.email" #input="changeData()">
</template>
Child two
<script>
props: {
enterObject: {
type: Object,
required: false,
default: () => ({})
}
},
data: () => ({
model: {}
}),
watch: {
enterObject: {
immediate: true,
handler() {
Object.assign(this.model.personalData, this.enterObject.personalData);
}
}
</script>
<template>
<div>
<div v-if="model.personalData.name || model.personalData.email">
<span class="mr-3">{{ model.personalData.name }}</span>
<span>{{ model.personalData.email }}</span>
</div>
<div v-else>
No data
</div>
</div>
</template>
I get data in parent component with no problem, but this data doesn't pass to second child, why I have always "No data" ?
I tested your code and found a few things:
You need to create "personalData" inside the model in "childTwo".
<template>
<div>
// I changed the validation for personalData
<div v-if="model.personalData">
<span class="mr-3">{{ model.personalData.name }}</span>
<span>{{ model.personalData.email }}</span>
</div>
<div v-else>No data</div>
</div>
</template>
export default {
props: {
enterObject: {
type: Object,
required: false,
default: () => ({})
}
},
data: () => ({
model: {
personalData: {}
}
}),
watch: {
enterObject: {
deep: true,
handler() {
// Add a validation in the handler, you can use Object assign inside the validation.
if(this.enterObject) {
Object.assign(this.model.personalData, this.enterObject.personalData)
}
}
}
}
It's worked for me.I hope it helps you.
You have to assign the value of the object using this.$set for more about object reactivity click here
your Parent component should be like this:-
here is the working example
<template>
<div>
<first-child #change-data="(payload) => changeData(payload)" />
<second-child :enter-object="model" />
</div>
</template>
<script>
import FirstChild from "./FirstChild";
import SecondChild from "./SecondChild";
export default {
data: () => ({
model: {},
compKey: 0,
}),
components: {
FirstChild,
SecondChild,
},
methods: {
changeData(payload) {
this.$set(this.model, "test", payload);
//this.model.test = payload;
},
},
};
</script>
I have results data in child component but I should bring it to main component.
Main Component in Parent, so each time I click the button, the result should be gathered in main app
<button #click="showFinalResult">Click me</button>
Child component data here and I just need to show results in json format in parent, the results are input results
results: [],
You can emit event for child for sending data:
Vue.component('Child', {
template: `
<div class="">
</div>
`,
data() {
return {
results: [1,2,3]
}
},
mounted() {
this.$emit('update', this.results);
}
})
new Vue({
el: '#demo',
data() {
return {
res: [],
show: false
}
},
methods: {
getResult(res) {
this.res = res
},
showResults() {
this.show = !this.show
}
}
})
Vue.config.productionTip = false
Vue.config.devtools = false
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo">
<button #click="showResults">Click me</button>
<p v-if="show">{{ res }}</p>
<child #update="getResult" />
</div>
I can normally select all the text with $event.target.select() but in this case I think it is selecting all and then replacing the selection with the computed property. How do you select all after the computed property is finished?
Vue.component('my-component', {
template: `
<div>
My Component
<input type="text" v-model="displayValue" #blur='isInputActive = false' #focus='isInputActive = true;$event.target.select()'></input>
</div>
`,
props:['value'],
data() {
return {
isInputActive: false
};
},
computed: {
displayValue: {
get: function() {
return (this.isInputActive) ? this.value : this.value.toUpperCase();
},
set: function(val) {
this.$emit('input', val);
},
}
},
})
new Vue({
el: '#app',
data() {
return {
test: "Test"
};
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<my-component v-model="test"></my-component>
</div>
You can use the $nextTick which run the callback after the computed property is finished.
#focus='isInputActive = true; $nextTick(() => $event.target.select())'
I'm trying to create a basic form that, once input is submitted, sends data to the parent and is then rendered in a list as a card. Documentation has been pointing me towards using the Event Bus, but it all seems a little too over engineered for such a simple task. Not to mention its not working xD. Am I on the right track here? or missing the whole idea?
The Data seems to be updating on submit, but I'm not seeing a card render. I'm also seeing the follow error,
Property or method "initiativeList" is not defined on the instance but referenced during render.
I do, however, notice a particularly odd change in the render. Instead of a child being rendered in EncounterList.js the child's attributes are merging into the parent .
Any help is greatly appreciated.
EncounterDashboard.js
<template>
<div>
<NewCharacterForm #add-char="addChar" />
<EncounterList v-bind="encounterList" #add-char="addChar" />
</div>
</template>
<script>
import Character from "../classes/Encounter";
import NewCharacterForm from "./NewCharacterForm/NewCharacterForm.vue";
import EncounterList from "./EncounterList/EncounterList";
import EventBus from "./EventBus.js";
export default {
name: "EncounterDashboard",
components: { NewCharacterForm, EncounterList },
data() {
return {
newChar: {},
encounterList: []
};
},
methods: {
addChar(newChar) {
this.newChar = newChar;
this.encounterList.push(newChar);
EventBus.$emit("add-to-list", this.encounterList);
}
}
};
</script>
NewCharacterForm.js
<template>
<div class="new-char-wrapper">
<form class="char-form" ref="form" v-on:submit.prevent="handleSubmit">
<NewCharInput class="name-input" label="NAME" name="name" v-model="name" />
<div class="stat-wrapper">
<NewCharInput
class="init-input"
label="INITIATIVE"
name="initiative"
v-model="initiative"
type="number"
/>
<NewCharInput class="hp-input" label="HP" name="hp" v-model="hp" type="number" />
</div>
<div class="submit-row">
<button class="submit">SUBMIT</button>
</div>
</form>
</div>
</template>
<script>
import NewCharInput from "./NewCharInput";
import Character from "../../classes/Character";
import { uuid } from "vue-uuid";
export default {
name: "NewCharacterForm",
components: { NewCharInput },
data() {
return {
name: "",
initiative: "",
hp: 0
};
},
props: ["addChar"],
methods: {
handleSubmit() {
const charName = this.$refs.form.name.value;
const charInitiative = this.$refs.form.initiative.value;
const charHp = this.$refs.form.hp.value;
const charId = this.$uuid.v4();
const newChar = new Character(charName, charInitiative, charId, charHp);
this.$emit("add-char", newChar);
}
}
};
</script>
EncounterList.js
<template>
<div class="encounter-list">
<div class="header-row">
<h2 class="header col-init">INIT</h2>
<h2 class="header col-name">NAME</h2>
<h2 class="header col-hp">HP</h2>
</div>
<EncounterCard
v-for="character in initiativeList"
v-bind:key="character.id"
v-bind:hp="character.hp"
v-bind:name="character.name"
v-bind:initiative="character.initiative"
/>
</div>
</template>
<script>
import EncounterCard from "../EncounterCard/EncounterCard";
import EventBus from "../EventBus";
export default {
name: "EncounterList",
components: { EncounterCard },
data() {
return {
data: {
initiativeList: []
}
};
},
methods: {
populateList(charList) {
this.initiativeList = charList;
}
},
mounted() {
EventBus.$on("add-to-list", charList => {
this.populateList(charList);
});
}
};
</script>
EncounterCard.js
<template>
<div class="encounter-card-wrapper">
<h1 class="char-init">{{character.initiative}}</h1>
<h1 class="char-name">{{character.name}}</h1>
<h1 class="char-hp">{{character.hp}}</h1>
</div>
</template>
<script>
export default {
name: "EncounterCard",
props: ["character"]
};
</script>
data() {
return {
data: { //Is this what you're trying to do?
initiativeList: []
}
};
},
If the data attribute is intended, "initiativeList" should be changed to "data.initiativeList".
<EncounterCard
v-for="character in data.initiativeList"
v-bind:key="character.id"
v-bind:hp="character.hp"
v-bind:name="character.name"
v-bind:initiative="character.initiative"
/>
and
populateList(charList) {
this.data.initiativeList = charList;
}
Is there a way to simplify this code?
The button should also change the localValue of the child.
Vue.component('my-input', {
template: `
<div>
<b>My Input:</b> <br>
localValue: {{ localValue }} <br>
<input v-model="localValue">
</div>
`,
props: ['value'],
data() {
return { localValue: this.value }
},
watch: {
value () {
this.localValue = this.value
},
localValue () {
this.$emit('input', this.localValue)
}
}
})
new Vue({
el: '#app',
data: () => ({
parentValue: 'Inital value'
}),
methods: {
change () {
this.parentValue = 'Changed value'
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<my-input v-model="parentValue"></my-input>
<button #click="change">Change</button><br>
parentValue: {{ parentValue }}
</div>
I have always faced difficulties when I need to do so.
I will be very grateful for the help!
If you avoid using v-model inside your custom form component, you really only need
<b>My Input:</b> <br>
localValue: {{ value }} <br>
<input :value="value" #input="$emit('input', $event.target.value)">
No data, no watch, that's it.
See https://v2.vuejs.org/v2/guide/components.html#Using-v-model-on-Components
If you really want something representing a value local to your component, the Vue docs favour using computed values over watchers (ref: https://v2.vuejs.org/v2/guide/computed.html#Watchers).
The idea here is to create a computed value with getter and setter to facilitate a simplified one-way data flow.
Vue.component('my-input', {
template: `<div><b>My Input:</b> <br>localValue: {{ localValue }} <br><input v-model="localValue"></div>`,
props: ['value'],
computed: {
localValue: {
get () {
return this.value
},
set (value) {
this.$emit('input', value)
}
}
}
})
new Vue({
el: '#app',
data: () => ({
parentValue: 'Inital value'
}),
methods: {
change () {
this.parentValue = 'Changed value'
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
<my-input v-model="parentValue"></my-input>
<button #click="change">Change</button><br>
parentValue: {{ parentValue }}
</div>
How to pass complex objects to child component (potentially down a few layers):
Parent component:
<child v-model='parentEntity' />
Child component:
model: {
prop: 'modelValue',
event: 'update:modelValue',
},
props: {
modelValue: {
type: Object,
required: true,
},
},
...
entity: {
// getter
get() {
return Object.assign({}, this.modelValue);
},
// setter
set(newValue) {
this.$emit('update:modelValue', newValue);
},
},
...