How to change a custom event name but alias the old one as well? - alias

I'm firing a custom event listener ...
export const customEvent = new Event("HelloOld", {bubble: false});
And I'm dispatching that event on an API response.
.then(res => {
document.dispatchEvent(customEvent);
})
Now that event listener (HelloOld) is already being used heavily by others, but I need to change its name to "HelloNew".
So, how to change the event name without breaking the old one? I thought of aliasing, but I don't know how to approach that.

Related

How to copy a Vuex store and import it into a new store

I am trying to write a way for in Electron to duplicate Vuex commits from one Window to another with IPC. This way devs can use the Vuex store between browser windows seamlessly without manually using IPC calls to send everything, causing lots of duplicate code.
So far, I have written a basic Vue plugin to handle pushing commits around
With only some base code using store.subscribe, I IPC send all commits to main, which then forwards the commits to all renderer windows, applying the commits to their respective stores. Each new browser Windows is able to forward the data to all others. To prevent endless loops, I check the event and payload and verify it's different than the current value before applying it.
This all works. It's not even that slow amazingly. But there is a problem, When the window is opened, the store is fresh. I need a way to export the entire store, IPC it to the new window, and import the store.
Is there any way in Vuex to generically export the entire state, then import it again?
After some time, I found a way to sync vuex store between main window and another IPCRenderer Window. I would not explain how to subscribe to mutation using plugins since the question is about replacing the state. Here are the logic steps to replace the state in newWindow:
When opening a new window from main window, we can call IPCRenderer
and send our entire state (this.$store.state)
For example:
this.ipcRenderer.send("open-new-window",cloneObject(this.$store.state));
What we should note here, we could not send a complex object like DOM Object to our new window because you will get circular JSON error, so here I use script (UTIL.cloneObject(this.$store.state)) for convert complex object to avoid circular JSON Error. You can see the code here or you can find in another place since this question is not about converting to circular json
In ipcMain you can store this state in the global variable, for example:
let temporaryState
ipcMain.on('open-new-window', async(event, data) => {
//Another code...
newWindow = new BrowserWindow(options);
temporaryState = data
//this is data is your temporary state from mainWindow
//Another code for open new window
})
Request to rehydrate state in new window if new window already in "mounted stage", for example:
mounted() {
this.ipcRenderer.send("ask-rehydrate");
}
Back to ipcMain, you can listen to rehydrate request from newWindow and send the state that you already store in global variable, for example:
ipcMain.on('ask-rehydrate', (event) => {
newWindow.webContents.send("currentState", temporaryState);
}
Listen again in mounted stage to replace the current stage in "newWindow":
mounted() {
this.ipcRenderer.send("ask-rehydrate");
this.ipcRenderer.once("currentState", (event, state) => {
new Promise((resolve, reject) => {
logger.debug("Current state will be replaced with %o", state);
this.$store.replaceState(state);
resolve();
}).then(() => {
//Do something});
});
}
Congratulation now your state in newWindow will replaced with the current state in main window, here the mutation also sync if you subscribe the mutation using plugin. We should do this in mounted stage of vue since we need to wait until the store already initiated.

Programatically assign handler for native event in Vue JS?

I am trying to leverage a Vue mixin to add behavior when a native event happens. Using a mixin will allow me to share that across several components. Specifically, when a field component (or button, or checkbox, etc.) has focus, and the Escape key is pressed, the field loses focus.
A similar Stack Overflow question seemed to indicate I could listen for native events (see code comment about multiple events).
However, the Vue Documentation for programmatically adding an event listener using $on says that it will
Listen for a custom event on the current vm...
(Emphasis added)
Unsure if the custom event remark is absolute or based on the context, I have been experimenting. I have been trying to listen for the native keyup event (using the Vue alias keyup.esc) but have had no success. So I am wondering if it is indeed limited to custom events, and if so, why?
You can see my experiment in a code sandbox. The custom event works, the native does not.
The mixin looks like so:
// escape.mixin.js
export default {
created() {
// Custom event
this.$on("custom-event", function() {
console.log("Custom event handled by mixin");
});
// Native event
this.$on(["keyup.esc", "click"], function() {
alert("Native event handled!");
});
}
};
The main point of all this is to be able to add the behavior to a set of components by adding to how the event is handled, without overriding behavior that might also exist on the component. The secondary goal is to provide the behavior by simply adding the mixin, and not having to do component level wiring of events.
So a component script would look something like this:
// VText component
import escapeMixin from "./escape.mixin";
export default {
name: "VText",
mixins: [escapeMixin],
methods: {
onFocus() {
console.log("Has Focus");
this.$emit("custom-event");
}
}
};
Also, I was trying to avoid attaching the listener to the <input> element directly with vanilla JS because the Vue documentation suggested that letting Vue handle this was a good idea:
[When using v-on...] When a ViewModel is destroyed, all event listeners are automatically removed. You don’t need to worry about cleaning it up yourself.
Solution
skirtle's solution in the comment below did the trick. You can see it working in a code sandbox.
Or here's the relevant mixin:
export default {
mounted() {
this.$el.addEventListener("keyup", escapeBlur);
},
beforeDestroy() {
this.$el.removeEventListener("keyup", escapeBlur);
}
};
function escapeBlur(e) {
if (e.keyCode === 27) {
e.target.blur();
console.log("Lost focus");
}
}

Prevent $emit from emitting more than once in Vue

I have an emit call within my Vue project to update search results, but the emit is being called at least 4 times, because the emit call is defined at various spots, so the data is sometimtes emitted with it and at other spots it is not.
I am using a global bus to perform an emit.
this.$root.bus.$emit('resetValuesInInfiniteLoader', filteredEntities);
this.$root.bus.$on('resetValuesInInfiniteLoader', function (filteredEntities) {});
I tried to name the emits calls differently, and also tried to use a different global vue bus but both options did not really work well.
Any idea how I can do this in an efficient manner so that the $emit is always only called once? How do I need to set it up to ensure that the emit is always only called once? I also tried using $once, which did not work, or tried to destroy the $emit. Can someone give me a small fiddle example or so maybe so I understand how to do this in the right way?
I have found this to be the case also and feel that there are some problems with using it in multiple locations. My understanding is that global event busses are not recommended in most applications as they can lead to a confusing tangle of events. The recommendation is that you use a state management solution like vuex.
But anyway, just a couple of points with your code above. I don't know how you created your bus but I have known to create it as such:
//main.js
const EventBus = new Vue()
Object.defineProperties(Vue.prototype, {
$bus: {
get: function () {
return EventBus
}
}
})
This creates it and makes it global. It can then be triggered in a component or components with:
<button #click="$bus.$emit('my-event')">click</button>
or
methods: {
triggerMyEvent () {
this.$bus.$emit('my-event', { ... pass some event data ... })
}
}
and listened to:
created () {
this.$bus.$on('my-event', ($event) => {
console.log('My event has been triggered', $event)
this.eventItem = 'Event has now been triggered'
//this.$bus.$off('my-event')
})
},
I have found that it works sometimes. I don't know why but it will work then it will trigger several events and I think it is because it isn't finalised or something. You may note I have commented out this.$bus.off which certainly stops it but it then doesn't work again. So I don't know what that's all about.
So there you go, a total non-answer, as in, Yes I've had that too, No I cant fix it.
I went with using vuex store, it seems a lot easier to communicate with any component within the application, has the advantage of global communication, yet does not have the caveat of sending multiple actions such as emit events

How to remove event listeners in Aurelia?

How to remove event listeners in Aurelia?
This doesn’t appear to do anything:
detached(){
window.removeEventListener('scroll', this.windowScroll);
}
The event is still firing when i change routes.
I am attaching it in the constructor() in my view-model file:
window.addEventListener('scroll', this.windowScroll.bind(this));
I also tried deactivate() and neither are firing when I change routes.
There is at least one, but maybe two issues here.
Setting up an event listener
If you can't use the Aurelia binding for event delegation (for which scroll may or may not be a case, I haven't tried it), then you should use the attached lifecycle callback to set up your event handlers, not the constructor. The reason being that, unless you specify your view model is transient, the constructor will be called once. Instead, you really want Aurelia to turn on your event handlers every time it is attached.
attached = () => {
window.addEventListener('scroll', this.onScroll);
}
How to write a lifecycle callback
In general you should write your lifecycle callbacks using the arrow notation. This is because, IIRC, your this may get reassigned during the activation lifecycle. The arrow notation in TypeScript/ES6 will preserve your this lexically, i.e., it is what you expect it to be.
detached = () => {
window.removeEventListener('scroll', this.onScroll);
}
It's worth noting that you need to define your bindable function up in the constructor if you want to unbind it again on detach:
export MyClass {
constructor() {
this.handleBodyClick = e => {
console.log(e.target);
};
}
attached() {
document.addEventListener('click', this.handleBodyClick);
}
detached() {
document.removeEventListener('click', this.handleBodyClick);
}
Taken directly from this excellent post: http://ilikekillnerds.com/2016/02/using-event-listeners-in-aurelia/

ReactTestUtils.Simulate can't trigger event bind by addEventListener?

Here is the example:
http://jsfiddle.net/hulufei/twr4thuh/7/
It just worked when bind onClick in virtual dom(like line 18), but If I comment line 18 and comment off line 8 to bind click with addEventListener, it failed.
So what's the problem?
TestUtils triggers events within react's synthetic event system, so the native event that addEventListener listens for is never going to be triggered. You will need to use the native click method on the element in your test:
var events = Events();
ReactTestUtils.renderIntoDocument(events);
events.refs.button.getDOMNode().click();
events.state.event.should.equal('click');
Additionally, you've misspelled clickHandler in your addEventListener definition.
jsfiddle
You can also simplify adding your event listener by reusing your prop definition:
componentDidMount: function () {
this.refs.button.getDOMNode().addEventListener('click', this.clickHandler);
},
Note:
Is there a reason why you want to use addEventListener instead of just passing an onClick attribute for your button? Unless there's a specific and good reason otherwise, i'd suggest doing things the react way when handling events for sanity :)
Edit
I originally mentioned that I did not know what TestUtils' SimulateNative.click did not trigger the event. I was wrong in thinking that it ever would since it would be simulating a native click event within the react even system. #thilo pointed me in the right direction :)
I had many problems while testing addEventListener, and I got the following conclusion.
You can create the events listener with pure javascript, jquery, but when running the tests with Jest I always had a problem.
The rendering of ReactTestUtils does not work directly with the document, and when we do:
For example, our events were added in the document, when rendering with ReactTestUtils it creates a div and renders it in the div, This way I could not get Simulate to trigger the call.
My first solution was to use jquery to create the listener and to test I did the render manually by appending the div in document.body, and triggered the events with the dispachEvent of javascript. But I thought the code was dirty, not the best way to work.
I made a sample code by adding the event and testing it with Jest, also have a test teaching to get all the listener that were created.
You can find the code here: https://github.com/LVCarnevalli/create-react-app/tree/master/src/components/datepicker
Component:
componentDidMount() {
ReactDOM.findDOMNode(this.datePicker.refs.input).addEventListener("change", (event) => {
const value = event.target.value;
this.handleChange(Moment(value).toISOString(), value);
});
}
Test:
it('change empty value date picker', () => {
const app = ReactTestUtils.renderIntoDocument(<Datepicker />);
const datePicker = ReactDOM.findDOMNode(app.datePicker.refs.input);
const value = "";
const event = new Event("change");
datePicker.value = value;
datePicker.dispatchEvent(event);
expect(app.state.formattedValue).toEqual(value);
});
Links:
window.addEventListener not triggered by simulated events: https://github.com/airbnb/enzyme/issues/426
Creating and triggering events: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events