Gia: How to pass event object to handler via eventbus - javascript

I am learning how to work with Gia (for small web projects) and I cannot find out how to pass an event object from one component to an event handler of another component over Gia's eventbus.
Here's two basic components, communicating over the eventbus:
class navigation extends Component {
constructor(element) {
super(element);
//
// Define "Sub-components"
this.ref = {
navLinks: [],
};
}
mount() {
//
// Listen for clicks on nav.-links
for (let i = 0; i < this.ref.navLinks.length; i++) {
const link = this.ref.navLinks[i];
link.addEventListener("click", this.handleNavLinkClick.bind(this));
}
}
handleNavLinkClick(e) {
//
// Emit event
let clickedLink = e.target;
if (clickedLink.classList.contains("callHeader")) {
eventbus.emit("callingSubpageHeader");
}
}
}
class subpageHeader extends Component {
mount() {
//
// Listen for call from eventbus
eventbus.on(
"callingSubpageHeader",
this.handleEventBusCall_callHeader.bind(this)
);
}
//
// Eventbus handler(s)
handleEventBusCall_callHeader() {
console.log("The subpage-header was called.");
}
}
The emitting of the event and the subsequent call of the handler inside the second component works just fine. But I would like to pass additional information from the first to the second component when the handler is called. The Gia documentation mentions that the emit method of the eventbus can pass an eventObject to the handler:
Calls any handlers previously registered with the same event name.
Optional event object can be used as a argument, which gets passed
into a handlers as an argument.
eventbus.emit('eventName'[, eventObject]);
Unfortunately, there is no example and I don't know how passing the object works. I tried adding "something" (in this case the link that was clicked in the first component) to the call of the emit-function, but have no idea how/where I can read/use this nor if passing something as an eventObject works this way:
class navigation extends Component {
constructor(element) {
super(element);
//
// Define "Sub-components"
this.ref = {
navLinks: [],
};
}
mount() {
//
// Listen for clicks on nav.-links
for (let i = 0; i < this.ref.navLinks.length; i++) {
const link = this.ref.navLinks[i];
link.addEventListener("click", this.handleNavLinkClick.bind(this));
}
}
handleNavLinkClick(e) {
//
// Emit event
if (clickedLink.classList.contains("callHeader")) {
eventbus.emit("callingSubpageHeader", [e.target]);
}
}
}
It'd be great if someone could explain the concept and syntax of passing an eventObject in this scenario.

The event handler is passed the object from the event as a parameter, so your handler can grab that object as a variable from its function signature, like this:
handleEventBusCall_callHeader(target) {
console.log("The subpage-header was called.");
}
the variable target inside of your event handler is now equal to the object you passed with the event.
When you call the event, you don't need to put your argument in [], that will just put it into an array before passing it which will give you headaches later on. The brackets in the documentation just show that the second argument for eventbus.emit is optional.

Related

Handling of jQuery event using class method changes context

is someway possible to get class context from the inside of its method which is used as jQuery event handler? Following example should explain everything:
class EventHandler {
constructor() {
this.msg = 'I am event handler';
}
handle_on_click(event) {
console.log(this);
console.log(this.msg);
}
}
let handler = new EventHandler();
handler.handle_on_click(null);
// print:
// EventHandler {msg: 'I am event handler'}
// I am event handler
let $a = $('Test').on('click', handler.handle_on_click);
$a.trigger('click');
// print:
// Test
// undefined
Obviously the context was changed in the case of calling handler.handle_on_click via jQuery event, but is there way to get original context?
Thank you.
Before this answer I've tried to find the solution, but I was not successfull. And as it used to be, I found it a moment after. It is duplicate of this question how to access class methods from the jquery events through on().
Explanation is that this is a pointer to current context, which in the case of calling via jQuery on is the element. If you need to get class context, it is necessary to call handler using arrow function:
class EventHandler {
constructor() {
this.msg = 'I am event handler';
}
handle_on_click(event) {
console.log(this);
console.log(this.msg);
}
}
let handler = new EventHandler();
handler.handle_on_click(null);
// print:
// EventHandler {msg: 'I am event handler'}
// I am event handler
let $a = $('Test').on('click', (event) => handler.handle_on_click(event));
$a.trigger('click');
// print:
// EventHandler {msg: 'I am event handler'}
// I am event handler

javascript add event to object class

i want to add event handler to object's class (with JQUERY), but i cannot find solution to my problem. I have code like this:
class Obstacle
{
constructor()
{
this.points = [];
}
}
class Editor
{
constructor()
{
this.obstacle = new Obstacle;
}
addPoint()
{
this.obstacle.points.push(canvas.click( function(e) { return [e.clientX, e.clientY]; }));
alert(this.obstacle.points.length);
}
}
Of course without any results, so i'm asking for any help :).
You've to push the coordinates inside the event handler. In the current code there's no receiver to the return value from the event.
Because this in the click handler refers to the element the event is attached, you need a separate reference to this. A simple way is to store this value to a variable in the outer scope and use that to refer the actual object. Like this:
addPoint() {
const that = this;
canvas.click(function(e) {
that.obstacle.points.push([e.clientX, e.clientY]);
});
}

reaching properties from hasChanged

i am trying to call a function from lit element hasChanged method,but it gives undefined
static get properties() {
let dis=this;
return {
projeid:{type:Number},
_arr:{type:Array},
_input_obj: {type: Object},
_funksiyalar:{type:Object},
input_objectler_array:
{
type:Array,
hasChanged:function(newVal,oldVal){
console.log(dis._funksiyalar);
return true;
}
}
}
}
constructor() {
super();
this._arr = ["No:", "Aciqlama", "Vahid", "Miqdar", "Vahidin Qiymeti", "Toplam"];
this._input_obj=this._arr.reduce((o, key) => Object.assign(o, {[key]: ""},{detaylar:[]}), {});
this.input_objectler_array=[];
this._funksiyalar={
Objectler_Array_hasChanged(newVal) {
let event = new CustomEvent("array-updated", {
detail: {
array: newVal
}
});
this.dispatchEvent(event);
}
};
}
how could i reach a property or method from lit-element hasChanged method?
hasChanged is called immediately and synchronously on set, so in your constructor:
this.input_objectler_array=[];
// hasChanged fires, this._funksiyalar is undefined
this._funksiyalar={...
You should be able to work around this just by defining _funksiyalar first;
this._funksiyalar={...
this.input_objectler_array=[];
// hasChanged fires, this._funksiyalar is now set
Lit uses a Proxy to create property set methods, so it's as if you had something like:
_internalVal;
set input_objectler_array(v) {
const hasChanged = function(newVal,oldVal){
console.log(dis._funksiyalar);
return true;
};
if(hasChanged(v, this._internalVal)) {
this._internalVal = v;
this.requestUpdate(); // this queues up the change to render
}
}
List uses hasChanged to determine whether it needs to change the value, so it's a bad idea to hook side effects like events to it. You might do this if you wanted the event to be cancellable or able to change the value, but I'd recommend either your own custom set method or moving that logic outside of the render control entirely (maybe with a ReactiveController) if you want to do that.
Instead override updated to fire events after a value changes:
updated(changedProperties) {
if (changedProperties.has('input_objectler_array')) {
const event = new CustomEvent('array-updated', {
detail: {
// component has updated, changedProperties holds the old value
array: this.input_objectler_array;
}
});
this.dispatchEvent(event);
}
}
Now this updated fires asynchronously, once the update has happened, so your array-updated event will fire once the component is connected and rendered in the page rather than in the constructor. This will allow programmatic creation to work:
const ele = document.createElement('my-element'); // constructor event fires here
ele.addEventListener('array-updated', doSomething);
containerElement.append(ele); // updated event fires after this

Calling the vue method from inside an event from VueDraggable

I am trying to get drag and drop function working in the vue.js app using vue-draggable https://vuejsexamples.com/vuejs-drag-and-drop-library-without-any-dependency/
The library has few events you can listen to and I would like to execute some logic once the item is dropped. However, I am not able to access vue component 'this' along with the data and methods. I've tried to use this.$dispatch('symDragged', event); but it is not working for the same reason. 'this' is not a vue instance but rather instance of draggable element.
Here is the code:
export default {
components: {
ICol,
SymptomsChooser, MultiSelectEditor, TempPressureChooser, BodyPartsEditor, MandatorySymptomsChooser},
data() {
return {
// data ommited...
options: {
dropzoneSelector: 'ul',
draggableSelector: 'li',
excludeOlderBrowsers: true,
showDropzoneAreas: true,
multipleDropzonesItemsDraggingEnabled: true,
onDrop(event) {
// delete symptom from old basket and add it to new one
let oldBasket = event.owner.accessKey;
let newBasket = event.droptarget.accessKey;
//this is not working
//this.symDragged(this.draggedSymId, oldBasket, newBasket);
},
onDragstart(event) {
this.draggedSymId = event.items[0].accessKey;
}
}
}
},
methods: {
symDragged(symId, oldBasketId, newBasketId) {
console.log("symDragged!");
let draggedSym = this.getSymById(symId);
let basketOld = this.getBasketById(oldBasketId);
this.delSym(basketOld, draggedSym);
this.addSym({baskedId: newBaskedId, sym: draggedSym});
}
//other methods ommited
}
}
So, what is the correct way to call the vue component method from callback event? Or maybe I need to create another event so that vue instance could listen to it?
Thanks for you help!
The problem you are facing is that with this you are referencing to the returned data object scope and not component scope. The best way to solve this is to make reference to the component instance, so later on you can call anything attached to that instance. You can also take a look at codesandbox example https://codesandbox.io/embed/7kykmmmznq
data() {
const componentInstance = this;
return {
onDrop() {
let oldBasket = event.owner.accessKey;
let newBasket = event.droptarget.accessKey;
let draggedItemsAccessKeys = event.items.map(element => element.accessKey);
componentInstance.symDragged(
draggedItemsAccessKeys,
oldBasket,
newBasket
);
}
}
}

Event handler not accessing the correct value from redux state

In my web application, I want to prompt user when he/she tries to close the browser/tab based upon Redux state using event handlers.
I am using the below code to prompt user before exiting based upon 'isLeaving' state.
function mapStateToProps(state) {
const {isLeaving} = state.app.getIn(['abc']);
return {
isLeaving
};
}
#connect(mapStateToProps, {}, undefined, {withRef: true})
export default class MyClass extends React.component {
#autobind
stayOnPage(event) {
if (this.props.isLeaving) {
const message = 'Are you sure you want to leave';
event.returnValue = message;
return message;
}
return false;
}
componentDidMount() {
window.addEventListener('beforeunload', (event) => {
this.stayOnPage(event);
});
}
componentWillUnmount() {
window.removeEventListener('beforeunload', (event) => {
this.stayOnPage(event);
});
}
componentWillReceiveProps(nextProps) {
if (this.props.prop1 !== nextProps.prop2) {
// do something
}
}
render() {
//
}
}
This code works fine. But whenever there is a change in prop1, I see that this.props.isLeaving does not have updated value.
Can somebody help? What is I'm doing wrong here?
You aren't cleaning up correctly in componentWillUnmount. The event handler you're trying to remove is a brand new function closure, not the same instance that you added. You should just attach the actual handler, rather than using an arrow function, like so:
componentDidMount() {
window.addEventListener('beforeunload', this.stayOnPage);
}
componentWillUnmount() {
window.removeEventListener('beforeunload', this.stayOnPage);
}
Possibly what you are seeing is the event triggering on a stale component instance, so it has old state.
React uses synthetic events, basically events are recycled to be more performant. You can read more on that here
What I normally do is pass the value I need on invocation
Not sure if this will work in your specific case because I define my event handlers in JSX instead of accessing the window object (which you might want to do as well but to be honest I'm not sure) but I use this pattern all the time to handle e.target.value properly

Categories