I've been trying to figure this out for hours and I'm not sure what's wrong with my vue custom events.
<!-- part of the vue template: the main element -->
<!-- not working <button #click='$emit(\"open-modal-zone\")'></button> -->
<!-- not working <button #click='activateFilterModalViaEmit'></button> -->
<modal-zone :filter-obj='filterObj' #open-modal-zone='modalActive=true' #close-modal-zone='modalActive=false' v-if='modalActive' ></modal-zone>
<!-- the component in question -->
Vue.component('modal-zone', {
props : ["filterObj", "modalActive"],
template: "<div id='modal-zone'>" +
"<p #click='$emit(\"close-modal-zone\")'>Close</p>" +
"<filter-modal-dialog-controls :filter-obj='filterObj'></filter-modal-dialog-controls>" +
"</div>"
});
<!-- filter-dialog-controls component omitted (I haven't gotten that far yet) -->
// the vue js of my app
var v = new Vue({
el : "#editing-div",
data : {
modalActive : false,
filterObj : {
filterModalActive : false
}
},
methods : {
activateFilterModalViaEmit : function(){
this.$emit("open-modal-zone"); //why doesn't this work?
}
activateFilterModal : function(){
this.modalActive = true; // this works.. but I want $emit!
}
}
});
The problem is that while I'm able to emit a close event from inside the component area, (the #click on the element), I can't do so from my main vue object's methods, and neither can I do it by sticking this.$emit("open-modal-zone") it into the button which I'd like to use to open this modal component.
So there you have it, I would like to intuitively do my events through the $emit functions, but what can I do to make my open function actually work in this case?
Emit is for events from child to parent communication. There no meaning in emitting event in root component
If you need communicate from parent to child you just pass props from parent to child.
Other options would be global event bus. See some example here
Or for more complex situation you could use vuex store.
Related
In my Laravel 9 project, I have a parent component, whose template includes a child component.
template>
....
....
<div v-if=item.is_visible class="col-4">
<note-details-modal-component v-bind:is_visible=item.is_visible :note_id= item.id>
</note-details-modal-component>
</div>
On clicking a button in the parent component, I set is_visible to true that renders the child component through v-if. In the child component, when I press a button, it calls a method that emits an event to the parent.
closeNow: function() {
console.log('closeNow');
// this.$parent.$emit('close_note',false);
this.$emit('close_note',false);
} ,
In the parent component, I have a method.
close_note(value)
{
console.log('In parent');
this.is_visible = ! this.is_visible;
},
When I click the close button in the child component, it calls CloseNow() in the child component, and I see that in the console.log. However, that event does not emit to the parent. I have tried all suggestions that I could find on the web. Also, I do not see any errors in the Dev console.
Could someone please tell me what's wrong in my code that is preventing the event from emitting from the child component to parent?
Thanks in advance.
The thing is that nothing refer to the emit you made. If you have this:
closeNow: function() {
console.log('closeNow');
this.$emit('close_note',false);
}
You should mention the close_note when you call the child component. It should be something like that:
<note-details-modal-component v-bind:is_visible=item.is_visible :note_id= item.id #theEmitName="theFunctionYoucall">
</note-details-modal-component>
where theEmitName is close_note and the function you call has the same name. This medium can be usefull : https://medium.com/js-dojo/component-communication-in-vue-js-ca8b591d7efa
There seems to be a lot of discussion around this topic such as Stackoverflow answer using hub, Stackoverflow answer using refs, so I really like to ask experts to provide for once a clear concise answer to this question. If the answer is also just not possible please state that!
Here is the scenario:
There are two components, a parent and a child
<Parent> // There is a button here that can be clicked to emit an event using 'this.$emit()'
<Child></Child> // The child listens and once hears the event, it does something
</Parent>
What to be achieved?
Clicking the button in the Parent emits a certain event, the child will be constantly listening and once it hears the event it executes an action, such as calling a method of its own.
What is out there about this so far?
Using a hub, in Vue Hub it is clearly stated this is for Non
Parent-Child Communication, so what is the point in using it for a
parent-child communication?
Using Refs, which is given as an end solution when it is not possible to use props and events. So why it is not possible with events at first place?
My own thought
It seems to me the firing of an event and listening to it is only possible from child to parent, basically one way communication. The parent is able to emit an event but child component(s) are not able to capture the event. Why? I tried this and didn’t work:
In the parent component I have (triggered by clicking a button in the parent component):
methods: {
generateCharts: function () {
this.$emit('generate-charts')
console.log('charts generated')
}
In the child component I have:
mounted () {
this.parent.$on('generate-charts', function () {
console.log('event captured') // Here nothing gets logged to the console
})
}
Update
Just came across this answer Vue $emit.
Apparently this is not possible at all with Vue.
At first instance it seems it is a deficiency because I have been in several situations where I needed to fire an event from parent and listen to it in the child.
I can imagine there must be a reason why this is not possible with Vue, it is probably a design consideration, Vue experts explaining why this is the case, and what is the better design approach to solve in general a scenario to pass events from parent to child, would be very appreciated.
The answer is to use props and react to changes to those props. It is a little confusing to get used to at first because it seems like a lot of code to to do something simple but as your application gets more complex, the one way data flow enforced by the use of props really helps with debugging and reasoning about what the code is trying to accomplish.
For instance, a modal. Your parent sets showChildModal = true with a button click, this value is passed to the child as a prop. The child is watching for changes to the prop, when it sees it set to true it opens the modal. Finally, when the modal is closed, the child $emit('close') which the parent is watching for, and when it sees that it sets showChildModal = false
Vue.component('child', {
template: '#child',
props: ['showModal'],
name: 'child',
watch: {
showModal: function(show) {
if (show) {
window.setTimeout(() => {
if (window.confirm("Hi, I'm the child modal")) {
this.$emit('close');
} else {
this.$emit('close');
}
}, 100)
}
}
}
})
var vm = new Vue({
el: '#el',
data() {
return {
showChildModal: false
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="el">
Parent
<p>
<button #click="showChildModal = true">Click to show child modal</button>
</p>
<hr>
<p>
<child :show-modal="showChildModal" v-on:close="showChildModal = false"> </child>
</p>
</div>
<script type="x-template" id="child">
<div>
Child
<p>
modal open? {{ showModal }}
</p>
</div>
</script>
I have a parent Vue which enables or disables "edit" mode. In non-edit mode all components are read only.
I've implemented this via a data object and all works fine.
I've split out some of the components in child components.
From the parent an $emit message is sent with the new edit mode state:
methods: {
toggleMode () {
this.editMode = !this.editMode
this.$emit('edit-mode-change', this.editMode)
}
Using Vue DevTools I can see the message is emitted.
However, I can't seem to receive it in my child component!I've looked a the docs, but none of the examples match this case. This is what I have currently (in the child component):
methods: {
onEditModeChange: function (mode) {
console.log('mode is', mode)
this.editMode = mode
}
Also tried:
events: {
onEditModeChange: function (mode) {
console.log('mode is', mode)
this.editMode = mode
}
Plus I'm getting an browser console error as follows:
[Vue warn]: Invalid handler for event "edit-mode-change": got false
(found in <Dimensions> at /home/anthony/Projects/Towers-Vue/src/components/assets/Dimensions.vue)
I'm sure I'm doing something basic wrong, but the docs don't reference the events: {} block, yet I've seen it on other code. Nor does it show how to implement a listener.
Thanks for taking the time to view this post, if you can point me in the right direction, it's much appreciated.
In Vue 2, events only flow laterally or up, not down.
What you really want is to simply pass a prop into your components.
In the parent JS:
toggleMode () {
this.editMode = ! this.editMode;
}
In the parent template:
<child-component-1 :editMode="editMode"></child-component-1>
...same for others...
Then simply accept editMode as a prop in each of your child components:
{
props: ['editMode']
}
You can now use editMode within your child's template. It'll track the parent's editMode, so no need for manual events/watchers.
The way vue2 works is by having a one-direction flow of the data, from parent to child, so in your parent component you can have
<template>
<child-component :isEditing="editMode"></child-component>
</template>
<script>
export default {
methods: {
toggleMode () {
this.editMode = !this.editMode
this.$emit('edit-mode-change', this.editMode)
}
}
}
and in child component you use props to get the data
Vue.component('child-component', {
props: ['isEditing'],
template: '<span>edit mode: {{ isEditing }}</span>'
})
we have cover the edit mode for the child. now if you want to send data from child to parent, then child needs to "emit" a signal to the parent, as props are "read only"
in child component you do at any point
someMethod() {
this.$emit('editDone', dataEdited);
}
and in your parent component you "intercept" the message using on:
<template>
<child-component
:isEditing="editMode"
#editDone="someParentMethod"></child-component>
</template>
Greetings!
I am currently working on making lazy-loading possible together with "more-routing" in Polymer. I already got a first working implementation of lazy loading (inspired by app-router that unfortunately does not support Polymer 1.0 completely), but I have no idea how I can pass all events from the imported child element up to my lazy-loading element.
So, in my example I want the event "hi" passed from page-login to lazy-loader and from lazy-loader upwards to the element containing lazy-loader - but without having knowledge of that specific event in lazy-loader. So I need something like "catch all events fired by child element".
That's what I have :
page-login:
ready:function() {
this.fire("hi");
},
using lazy-loader:
<lazy-loader on-hi="hi" href="../pages/page-login.html" id="login" route="login"></lazy-loader>
calling load :
document.getElementById("login").load()
Lazy-loader (reduced version):
<dom-module id="lazy-loader">
<template>
<span id="content">Loading...</span>
</template>
<script>
window.lazyLoaded = window.lazyLoaded || [];
Polymer({
is: "lazy-loader",
properties: {
href:{
type:String,
value:""
},
element:{
type:String,
value:""
}
},
load:function(finishFunc) {
if(window.lazyLoaded.indexOf(this.href)===-1) {
var that = this;
this.importHref(this.href, function(e) {
window.lazyLoaded.push(that.href);
if(!that.element) {
that.element = that.href.split('/').reverse()[0];
that.element = that.element.split(".")[0];
}
var customElement = document.createElement(that.element);
// here I need something like "customElement.on("anyEvent")"
// => that.fire("anyEvent")
Polymer.dom(that.$.content).innerHTML = "Loading done.";
Polymer.dom(that.$.content).appendChild(customElement);
Polymer.dom.flush();
that.fire("loaded");
});
}
}
});
</script>
It seems like I have to revoke my question - If I fire the event in the attached-handler, not in ready, it gets passed through automatically - at least in the current version of Polymer. Not sure if this is intended behavior, but for now the problem is solved. An answer still would be interesting, maybe for other cases (like event logging)
How should one access state (just state, not the React State) of child components in React?
I've built a small React UI. In it, at one point, I have a Component displaying a list of selected options and a button to allow them to be edited. Clicking the button opens a Modal with a bunch of checkboxes in, one for each option. The Modal is it's own React component. The top level component showing the selected options and the button to edit them owns the state, the Modal renders with props instead. Once the Modal is dismissed I want to get the state of the checkboxes to update the state of the parent object. I am doing this by using refs to call a function on the child object 'getSelectedOptions' which returns some JSON for me identifying those options selected. So when the Modal is selected it calls a callback function passed in from the parent which then asks the Modal for the new set of options selected.
Here's a simplified version of my code
OptionsChooser = React.createClass({
//function passed to Modal, called when user "OK's" their new selection
optionsSelected: function() {
var optsSelected = this.refs.modal.getOptionsSelected();
//setState locally and save to server...
},
render: function() {
return (
<UneditableOptions />
<button onClick={this.showModal}>Select options</button>
<div>
<Modal
ref="modal"
options={this.state.options}
optionsSelected={this.optionsSelected}
/>
</div>
);
}
});
Modal = React.createClass({
getOptionsSelected: function() {
return $(React.findDOMNode(this.refs.optionsselector))
.find('input[type="checkbox"]:checked').map(function(i, input){
return {
normalisedName: input.value
};
}
);
},
render: function() {
return (
//Modal with list of checkboxes, dismissing calls optionsSelected function passed in
);
}
});
This keeps the implementation details of the UI of the Modal hidden from the parent, which seems to me to be a good coding practice. I have however been advised that using refs in this manner may be incorrect and I should be passing state around somehow else, or indeed having the parent component access the checkboxes itself. I'm still relatively new to React so was wondering if there is a better approach in this situation?
Yeah, you don't want to use refs like this really. Instead, one way would be to pass a callback to the Modal:
OptionsChooser = React.createClass({
onOptionSelect: function(data) {
},
render: function() {
return <Modal onClose={this.onOptionSelect} />
}
});
Modal = React.createClass({
onClose: function() {
var selectedOptions = this.state.selectedOptions;
this.props.onClose(selectedOptions);
},
render: function() {
return ();
}
});
I.e., the child calls a function that is passed in via props. Also the way you're getting the selected options looks over-fussy. Instead you could have a function that runs when the checkboxes are ticked and store the selections in the Modal state.
Another solution to this problem could be to use the Flux pattern, where your child component fires off an action with data and relays it to a store, which your top-level component would listen to. It's a bit out of scope of this question though.