I am emitting an event called emit-event-main2-counter in Main2.vue
Why will the data cntr in Bottom.vue not update?
App.vue
<template>
<div class="app">
<Main2 />
<Bottom/>
</div>
</template>
<script>
import Main2 from "./components/Main2";
import Bottom from "./components/Bottom";
export default {
components: {
Main2,
Bottom
},
}
</script>
<style scoped>
h1 {
color: red;
}
</style>
Main2.vue
<template>
<div>
main2 template <span class="text1">{{message}}</span>
<button type="button" v-on:click="btnClickButton">my click</button>
<div>{{counter}}</div>
</div>
</template>
<script>
import appInput from "./appInput.vue";
export default {
data: () => {
return {
message: "theText",
counter: 0,
}
},
components: {
appInput,
},
methods: {
btnClickButton(e) {
this.$root.$emit('emit-event-main2-counter', this.counter)
console.log('button');
this.counter +=1;
}
}
}
</script>
<style scoped>
.text1 {
color:red;
}
.text2 {
color:blue;
}
</style>
Bottom.vue
<template>
<div class="Bottom" v-on:emit-event-main2-counter="cntr = $event">
bottom text and cntr ({{cntr}})
</div>
</template>
<script>
export default {
data: () => {
return {
cntr: 0
}
},
}
</script>
<style scoped>
</style>
You could emit an event to parent from Main2 having as parameters this.counter and in the parent one receive that and pass it through props to Bottom
In Main2 :
this.$emit("emit-event-main2-counter",this.counter);
in the parent component :
<template>
<Main2 v-on:emit-event-main2-counter="sendToBottom"/>
<Bottom :cntr="pcounter"/>
....
</template>
data:{
pcounter:0
},
methods:{
sendToBottom(c){
this.pcounter=c
}
}
Bottom should have property called cntr
props:["cntr"]
Bottom.vue
<template>
<div class="Bottom" >
bottom text and cntr ({{cntr}})
</div>
</template>
<script>
export default {
props:["cntr"],
data: () => {
return {
}
},
}
</script>
If you want to use root events, you need to emit the event with this.$root.$emit() and also listen to the event on the root like this: this.$root.$on().
You should use it directly in the script part. Listen to the root event e.g. in the created() hook and then disable it with $off in the beforeDestroy() hook.
However, I wouldn't encourage you to use $root events. It is usually better to communicate between the components like #BoussadjraBrahim proposed in his answer.
If you have a more complex application, it makes sense to take a look at Vuex and store the complete state in the Vuex store. By doing this, you can watch the global application state in the components and react if it changes. In this scenario, you would use the Vuex store instead of a root EventBus.
Related
I am trying to pass a function when a button is clicked, the button is clicked in a child element, then passed through a parent element to another child component, and i dont want to use the store for that, How can i do that?
components/footer/footer.vue
-- This is where the button is clicked
<template>
<div class="footer-bottom-header-menu-bar mob" #click="showMenu">
<img src="~/assets/svg/menubar.svg" alt="+" />
</div>
</template>
<script>
export default {
methods: {
showMenu() {
this.$emit("show-menu");
}
}
}
</script>
layouts/default.vue
--This is the parent component where that receives the click function and is to pass it into the app-header
<template>
<div>
<app-header />
<Nuxt />
<app-footer #show-menu="showMenu()"/>
</div>
</template>
<script>
import header from "~/components/header/header";
import footer from "~/components/footer/footer";
export default {
components: {
'app-header': header,
'app-footer': footer
},
methods: {
showMenu() {
console.log("clicked");
}
}
}
</script>
components/header/header.vue
-- I want the click function to perform an action inside this component
<script>
export default {
data() {
return {
showMenuBar: false
}
},
}
</script>
Why are you worried about passing a function around?
When you emit the show-menu event simply toggle a piece of data in your parent component like this:
<template>
<div>
<app-header :showMenuBar="showMenuBar" />
<Nuxt />
<app-footer #show-menu="showMenu"/>
</div>
</template>
<script>
import header from "~/components/header/header";
import footer from "~/components/footer/footer";
export default {
components: {
'app-header': header,
'app-footer': footer
},
data() {
return {
showMenuBar: false;
};
},
methods: {
showMenu() {
// I would make this more dynamic than always
// hardcoding it to true, but you get the idea
this.showMenuBar = true;
}
}
}
</script>
Then in your AppHeader simply take it in as a prop:
<script>
export default {
props: {
showMenuBar: {
type: Boolean,
default: false,
},
}
</script>
You can declare any attribut on your parent component:
data() {
return { toBeWatched: 0 };
}
Then pass it like a props from the parent to the header child:
<app-header :Trigger="toBeWatched" />
When you listen to the #show-menu event (comming from footer child),
make any change on your attribut:
<app-footer #show-menu="toBeWatched++" />
Finally you can watch for this change on your header child and
trigger your function.
<script>
export default {
data() {
return {
showMenuBar: false
};
},
props: ['Trigger'],
watch: {
Trigger() {
this.showMenuBar = !this.showMenuBar; // or do whatever you want
console.log('showMenuBar : ' + this.showMenuBar);
}
}
};
</script>
Anybody knows how to provide a ref to child components. For example:
Parent component provide the ref:
<template>
<div ref="myRef" />
</template>
<script>
export default {
name: 'SearchContainer',
provide: {
parentRef: this.$refs.myRef
}
}
</script>
</style>
And Child component inject it:
<template>
<div />
</template>
<script>
export default {
name: 'SearchCard',
inject: ['parentRef']
}
</script>
</style>
Generally the interaction between parent and child component should be done using props and emitted events :
<template>
<div />
</template>
<script>
export default {
methods:{
changeParentStyle(){
this.$emit('change-style')
}
}
}
</script>
</style>
parent :
<template>
<child #change-style="changeStyle" />
</template>
<script>
export default {
name: 'SearchContainer',
methods:{
changeStyle(){
//change the property that affects your style
}
}
}
</script>
</style>
I have manage to achieve providing ref to children. But in my opinion if you want to communicate betwwen parent and children I think you have got your answer already by Boussadjra Brahim.
Still providing ref has its own use cases. I am usinig it for dialogs.
Here is my solution:
<!-- Parent Component -->
<template>
<div ref="myRef"></div>
</template>
<script>
export default {
name: 'SearchContainer',
methods:{
getMyRef(){
return this.$refs.myRef
}
},
provide() {
return{
parentRef: this.getMyRef
}
}
}
</script>
<!-- Child Component -->
<template>
<div></div>
</template>
<script>
export default {
name: 'SearchContainer',
methods:{
useRef(){
this.parentRef().data // Can access data properties
this.parentRef().method() // can access methods
}
}
inject: ['parentRef']
}
</script>
Code
CodeEditor.vue:
<template>
<div class="ace-container">
<div class="ace-editor" ref="ace"></div>
</div>
</template>
<script>
import ace from 'ace-builds'
import 'ace-builds/webpack-resolver'
import 'ace-builds/src-noconflict/theme-monokai'
import 'ace-builds/src-noconflict/mode-javascript'
export default {
mounted() {
this.aceEditor = ace.edit(this.$refs.ace, {
maxLines: 60,
minLines: 10,
fontSize: 14,
theme: this.themePath,
mode: this.modePath,
tabSize: 4
})
},
data() {
return {
aceEditor: null,
themePath: 'ace/theme/monokai',
modePath: 'ace/mode/javascript'
}
},
methods: {
setCode(code) {
this.aceEditor.setValue(code);
},
getCode() {
return this.aceEditor.getValue();
},
}
}
</script>
<style>
.ace-editor {
width: 600px;
height: 600px;
}
</style>
QuizExecution.vue: (partly)
<template>
<v-app height="100%">
<div id="qz-wrapper">
<!--
<v-textarea id="programmingText" v-model="answerData[question.id]"
#change="saveAnswer(qe.id, question.id)" label="Code" outlined></v-textarea>
-->
<CodeEditor id="programmingText" v-model="answerData[question.id]"
#change="saveAnswer(qe.id, question.id)"></CodeEditor>
</div>
</v-app>
</template>
<script>
import Vue from 'vue'
import CodeEditor from "./CodeEditor";
export default {
components: {CodeEditor},
data() {
return {
// ..
}
}
}
</script>
<style scoped>
</style>
Description
With vuetify's <v-textarea>, I can use v-model to bind its content to a data property, in bi-direction dynamically, so that could init on load, and save on change with a #change property.
Then I want to replace the input area with ace-editor, which support features like syntax highlight.
So, I have defined a component as in CodeEditor.vue, then import & use it in QuizExecution.vue.
But, the v-model and #change won't work on the <CodeEditor> tag.
Questions
How to apply v-model and #click on this <CodeEditor> with in QuizExecution.vue.
Aka. init it with data from container component, and retrieve its content on change and trigger an event to save.
Or, is there anyway to achieve the same result: init on creation & save on change.
You can use props and watch the change events with #update_question_id;
<CodeEditor id="programmingText" :question_id="answerData[question.id]"
#update_question_id="answerData[question.id]=#event"
></CodeEditor>
....
watch:{
answerData(){
saveAnswer(this.qe.id, this.question.id)
}
}
CodeEditor.vue:
You can get the question_id value with props. I think it would be string or number.
And also watch question_id then use $emit to send change $event to main component.
export default {
props:{
question_id: [String,Number]
},
watch:{
question_id(val){
this.$emit("update_question_id",val)
}
}
.....
Is it possible to call a function in an imported component from "parent element"? Because I have a few modules that I wanna have in my app and show them dynamically in my app. I though if I import a component like Header in my main App.vue folder it will recognize the function from the main App.vue folder.
Example of App.vue:
<template>
<div>
<div class="m-grid m-grid--hor m-grid--root m-page">
<!-- <loader></loader> -->
<mobile-menu-partial></mobile-menu-partial>
<header-partial></header-partial>
<div :is="currentComponent"></div>
<div v-show="!currentComponent" v-for="component in componentsArray" :key="component.id">
<button #click="swapComponent(component)">{{component}}</button>
</div>
<button #click="swapComponent(null)">Close</button>
<footer-partial></footer-partial>
</div>
</div>
</template>
<script>
import Loader from '#/components/partials/Loader.vue'
import MobileMenu from '#/components/partials/MobileMenu.vue'
import Header from '#/components/partials/Header.vue'
import Footer from '#/components/partials/Footer.vue'
export default {
data () {
return {
currentComponent: null,
componentsArray: ['dashboard', 'schedule', 'locations', 'mileage']
}
},
name: 'App',
components: {
'loader': Loader,
'mobile-menu-partial': MobileMenu,
'header-partial': Header,
'footer-partial': Footer
},
methods: {
swapComponent: function (component) {
console.log('component', component)
this.currentComponent = component
if (component === null) {
console.log('anywhere_main')
this.$router.push('/')
} else {
this.$router.push('/' + component)
}
}
}
}
</script>
<style>
</style>
So can I have access of the function swapComponent in my header-partial? Because there are my modules for opening.
Calling child component's method from parent component
Say you have a child-comp component and want to call its childMethod method from the parent.
Use ref="someVariableName" on the parent's template and this.$refs.someVariableName on the parent's JavaScript.
Parent's template:
<div id="app>
<child-comp ref="myChild"></child-comp>
<button #click="callChildMethod">GO</button>
</div>
Parent's JavaScript code:
{
data: // ...
// ...
methods: {
callChildMethod: function () {
this.$refs.myChild.childMethod();
}
}
Calling parent's method from child
Emit an event in <child-comp> and listen to it in parent.
Parent's template (listening to an event):
<div id="app>
<child-comp #eventname="parentsMethod"></child-comp>
</div>
Notice that #eventname="parentsMethod" is the same as v-on:eventname="parentsMethod".
Parent's JavaScript code:
{
data: // ...
// ...
methods: {
parentsMethod: function (event) {
console.log('parent called', event);
}
}
Child's JavaScript code:
{
data: // ...
// ...
methods: {
someChildMethod: function () {
this.$emit('eventname', {some: 'object value'});
}
}
Now whenever, at the child, you call someChildMethod it will trigger the event and, since the parent is listening to it, it will execute parentsMethod in the parent.
I need help with modals, i tried to get this question sorted yesterday but then it got even worse. So please any help would be appreciated.
I have a parent component and inside of there i have a modal (child component)
In my parent the code:
<template>
<div class="all">
<Button type="primary" #click="modalUp()">Press me</Button>
<appTest #changed = "modal1 = $event" :modal1='modal1'> </appTest>
{{modal1}}
</div>
</template>
<script>
/* eslint-disable */
import test from '~components/test.vue'
export default {
data(){
return{
modal1: false
}
},
components: {
appTest: test
},
methods: {
modalUp() {
this.modal1 = true
}
},
watch:{
modal1: function(){
this.$on('changed', (data)=>{
console.log(data)
})
}
}
}
</script>
<style lang="css" scoped>
</style>
inside of the child component (appTest) i have this
<template>
<div id="" >
<Modal v-model="modal1" title="MODALLLL" #on-ok="ok" #on-cancel="cancel">
<p>#twitter</p>
<p>#facebook</p>
<p>Good</p>
{{modal1}}
</Modal>
</div>
</template>
<script>
/* eslint-disable */
export default {
props: ['modal1'],
data() {
return {
}
},
methods: {
ok() {
this.$Message.info('all good');
},
cancel() {
this.$Message.info('Cancel');
this.$emit('changed')
}
},
watch:{
modal1: function(){
this.$emit('changed', this.modal1)
}
}
}
</script>
<style lang="css" scoped>
</style>
So this code works in one way, the modal shows up correctly but once its gone and when we get back to parent component it gives me this vue warning AVOID MUTATING PROP
I checked the docs and everything but vuejs docs give examples like 2+2 which is not helpful in this case. I watched videos on the internet etc but still don't know how to get it done in a proper way.
What would be the best way to get it working?
I'm using modal from iview
Change your child to use a computed value.
export default {
props: ['modal1'],
computed:{
showModal:{
get(){return this.modal1},
set(v){ this.$emit("changed", v)}
}
},
}
And update the child template to
<Modal v-model="showModal" ...></Modal>
Doing this, whever you change modal1 in the parent, the value will be updated in the Modal component, and whenever the Modal component changes the value, it will be sent to the parent.