Vue. Change the contents of one slot from another - vue.js

I have a component with certain slots.
<v-split-container ref="splitContainer" class="panel-panel">
<template slot="left">
Test
</template>
<template slot="right">
<router-view></router-view> //Show Compontent A
</template>
<template slot="bottom"> //Slot B
Hello
</template>
</v-split-container>
Can I, from component A, change the contents of slot B by calling a function inside the component?

Hi you can do it with Scoped Slot. I have created an example for you how to do it. Please not that I am using v-slots(Some context here) only usable from vue v2.6.
Please take a look at the code example: https://codesandbox.io/s/2030jo20j0?fontsize=14
Child Component
<template>
<div>
<div>
<slot name="msgSlot">{{msg}}</slot>
</div>
<br>
<slot name="bottom" :updateMsg="updateMsg"></slot>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data: () => ({
msg: "Default Message"
}),
methods: {
updateMsg(text) {
this.msg = text;
}
}
};
</script>
Parent Component
<template>
<div id="app">
<HelloWorld>
<template v-slot:msgSlot></template>
<template v-slot:bottom="{updateMsg}">
<input type="text" v-model="msg">
<button #click="updateMsg(msg)">Change Msg</button>
</template>
</HelloWorld>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
data: () => ({
msg: ""
}),
components: {
HelloWorld
}
};
</script>

Related

Vue.js Passing a variable in a loop to another component

As a beginner in Vue.js,I am trying to create a Todo app, but problems seems to passing a variable to another component when looping.
i want to pass item to another embedded component.
Here what I have with all the components:
in main.js:
import Vue from "vue";
import App from "./App.vue";
import Todo from "./components/Todo";
import itemComponent from "./components/itemComponent";
Vue.config.productionTip = false;
Vue.component('todo', Todo);
Vue.component('itemcomponent', itemComponent);
new Vue({
render: h => h(App)
}).$mount("#app");
in App.vue:
<template>
<div id="app">
<todo></todo>
<img alt="Vue logo" src="./assets/logo.png" width="25%" />
<!-- <HelloWorld msg="Hello Vue in CodeSandbox!" /> -->
</div>
</template>
<script>
export default {
name: "App",
components: {
},
};
</script>
in Todo.vue:
<template>
<div>
<input type="text" v-model="nameme" />
<button type="click" #click="assignName">Submit</button>
<div v-for="(item, index) in result" :key="item.id">
<itemcomponent {{item}}></itemcomponent>
</div>
</div>
</template>
<script>
import { itemcomponent } from "./itemComponent";
export default {
props:['item'],
data() {
return {
nameme: "",
upernameme: "",
result: [],
components: {
itemcomponent,
},
};
},
methods: {
assignName: function () {
this.upernameme = this.nameme.toUpperCase();
this.result.push(this.nameme);
this.nameme = "";
},
},
};
</script>
in itemComponent.vue:
<template>
<div>
<input type="text" value={{item }}/>
</div>
</template>
<script>
export default {
props: {
item: String,
},
data() {
return {};
},
};
</script>
what is my mistake? thanks for help.
Quite a bunch of mistakes:
You should import single page components inside their parent components and register them there, not in main.js file. EDIT: Explaination to this is given in docs
Global registration often isn’t ideal. For example, if you’re using a build system like Webpack, globally registering all components means that even if you stop using a component, it could still be included in your final build. This unnecessarily increases the amount of JavaScript your users have to download.
You have a component registration in Todo.vue, but you have misplaced it inside data so its is just a data object that is not getting used, move into components.
In your loop you have item.id, but your item is just a plain string, it does not have an id property. Either change item to object with id property, or simply use the index provided in loop (not recommended).
Pass your item as a prop, not mustache template. So in Todo.vue replace {{ item }} with :item="item"
In your ItemComponent.vue you have mustache syntax once again in the attribute. Change value={{item }} to :value="item"
So here's the final code:
main.js
import Vue from "vue";
import App from "./App.vue";
Vue.config.productionTip = false;
new Vue({
render: h => h(App)
}).$mount("#app");
in App.vue:
<template>
<div id="app">
<todo></todo>
<img alt="Vue logo" src="./assets/logo.png" width="25%" />
<!-- <HelloWorld msg="Hello Vue in CodeSandbox!" /> -->
</div>
</template>
<script>
import Todo from './components/Todo.vue';
export default {
name: "App",
components: {
Todo,
},
};
</script>
in Todo.vue:
<template>
<div>
<input type="text" v-model="nameme" />
<button type="click" #click="assignName">Submit</button>
<div v-for="(item, index) in result" :key="index">
<itemcomponent :item="item"></itemcomponent>
</div>
</div>
</template>
<script>
import { itemcomponent } from "./itemComponent";
export default {
props:['item'],
data() {
return {
nameme: "",
upernameme: "",
result: [],
};
},
methods: {
assignName: function () {
this.upernameme = this.nameme.toUpperCase();
this.result.push(this.nameme);
this.nameme = "";
},
},
components: {
itemComponent,
}
};
</script>
itemComponent.vue:
<template>
<div>
<input type="text" :value="item"/>
</div>
</template>
<script>
export default {
props: {
item: String,
},
data() {
return {};
},
};
</script>

Vuejs change component element value dynamicaly

I have a component in common between two other components A,B. This shared Comp has a button and its name changes depending on the component i use. How do I set the name dynamic?
I thought v-model solved the problem
What am I missing?
App.vue:
<test-a></test-a>
<test-b></test-b>
sharedComp.vue:
<template>
<div>
{{ btnValue }}
<input type="button" v-model="btnValue" />
</div>
</template>
<script>
export default {
data() {
return {
btnValue: "",
};
},
};
</script>
CompA.vue
<template>
<div>
<shared-comp
v-for="(item, index) in 3"
:key="index"
:value="'A'"
></shared-comp>
</div>
</template>
<script>
import SharedComp from "./SharedComp.vue";
export default {
components: { SharedComp },
};
</script>
CompB.vue
<template>
<div>
<shared-comp :value="'B'"></shared-comp>
</div>
</template>
<script>
import SharedComp from "./SharedComp.vue";
export default {
components: { SharedComp },
};
</script>
You have to define the properties you pass to your component inside of the 'sharedComp'.
Try something like:
<template>
<div>
{{ value }}
<input type="button" v-model="value" />
</div>
</template>
<script>
export default {
props: ['value'],
};
</script>
For further information on Props in Vue check the documentation page: https://v2.vuejs.org/v2/guide/components-props.html

Passing v-model to Child Component

I'm using Vue 3. In the example below I have 3 simple components, Name Component, Phone Component and Submit Component. How do I get in the respective properties of the Submit Component, what was entered in the inputs?
Name Component:
<template>
<div>
<input type="text" v-bind="name">
</div>
</template>
Phone Component:
<template>
<div>
<input type="text" v-bind="phone">
</div>
</template>
Submit Component:
<template>
<name></name>
<phone></phone>
</template>
<script>
export default {
data() {
return {
name: '',
phone: '',
}
}
}
</script>
<template>
<section>
<childComponent ref="nameOfRef" />
</section>
</template>
methods: {
submit() {
let Data = this.$refs.nameOfRef.$data; // nameOfRef.data or nameOfRef.$data or nameOfRef.phone or nameOfRef.name
}
},
Check I think it will help to catch your parent component data.

How to change parent data from a child component?

After doing some digging, it seems that from child to parent you should update data by emitting events (not by v-model). This is my attempt of doing that (to no avail).
App.vue
<template>
<div>
<HelloWorld :count="count" #update:count="count= $event" />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
name: "App",
data() {
return {
count: 10
};
},
components: {
HelloWorld
}
};
</script>
HelloWorld.vue
<template>
<div class="hello">
<input
type="number"
min="0"
:value="count"
#input="$emit('input', $event.target.value)"
style="width:6em"
/>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
count: Number
}
};
</script>
Where am I going wrong? Ideally, I would like that changes in the input field of the child component would change the deposit field of the parent. Thanks!
I think that doesn't work because you're missing emits: ['update:count'] option in child component, But I recommend to name the prop as modelValue in child component and use v-model directive in parent instead of #update:count event :
<template>
<div>
<HelloWorld v-model="count" />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
name: "App",
data() {
return {
count: 10
};
},
components: {
HelloWorld
}
};
</script>
HelloWorld.vue :
<template>
<div class="hello">
<input
type="number"
min="0"
:value="count"
#input="$emit('update:modelValue', $event.target.value)"
style="width:6em"
/>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
modelValue: Number
},
emits: ['update:modelValue'],
};
</script>
This allows you to create a custom input

Passing parents props to slot in Vue,js

I have in my application a DocumenContainer component which has multiple ChartContainer components. The ChartContainer has a slot in which I put various types of Charts (bar chart, Line Chart etc.). I would like to pass the data isOuput to the child component which is a slot
ChartContainer (simplified):
<template>
<div class="card-body">
<slot v-slot="isOutput"></slot>
</div>
</template>
<script>
export default {
data() {
return {
isOutput : false,
}
}
</script>
DocumentContainer:
<chart-container title="Stats Model" v-slot="slotProps" :documentId="id">
{{slotProps.isOuput}}
<v-bar-chart :docId="id"></v-bar-chart>
</chart-container>
I tried passing the isOutput to the parent (DocumentContainer) with v-slot. The problem right now is that I'm only able to print {{slotProps.isOutput}}. I would like to pass that slotProps.isOutput as a props to the <v-bar-chart> and
<v-bar-chart :isOuput="slotProps.isOutput" :docId="id"></v-bar-chart>
is giving me undefined in the bar-chart props.
Is there a simpler way than to pass the data to the parent and to the child? How can I achieve this?
I think this is something to do with the context
It will work if you use v-bind instead
<v-bar-chart v-bind="{ isOutput: slotProps.isOutput, docId: id }"></v-bar-chart>
Example code
const Component1 = {
template: `
<div>
<h2>Component 1</h2>
<button #click="isOutput = !isOutput">Toggle</button>
<slot :isOutput="isOutput"></slot>
</div>
`,
data() {
return {
isOutput: false,
}
}
};
const Component2 = {
props: ['isOutput'],
template: `
<div>
<h2>Component 2</h2>
isOutput: {{String(isOutput)}}
</div>
`
};
new Vue({
el: '#app',
components: {
Component1,
Component2
}
});
<script src="https://unpkg.com/vue#2.6.10/dist/vue.min.js"></script>
<div id="app">
<h1>Home</h1>
<Component1>
<template v-slot="slotProps">
isOutput: {{String(slotProps.isOutput)}}
<Component2 v-bind="{ isOutput: slotProps.isOutput }">
</Component2>
</template>
</Component1>
</div>