I know (mostly) how to react to the various events fired by that menagerie of objects living inside the DOM.
To notch things up a bit, I'd like to be able to fire my own bespoke events when appropriate, something I suppose I could pseudo-code as follow :
myObject = {
prop:{ soAndSo }
method : function(args){
//do some stuff that takes forever
"All done and ready, now tell the world"
}
}
The idea being of course that some time down the road I can instantiate a myObject, then even later monitor its behaviour with something to the effect of
aMyObject.onmyevent = function(event){
//do something appropriate for the circumstance
}
The thing is, I have no clue as to where to start regarding the part "now tell the world".
Will you point me in the right direction?
You need to create a faux eventEmitter. Here is one I made while following a tutorial from Pluralsight called React and Flux for Angular Developers: Tutorial
To your question, you 'tell the world' by emitting the event, which is essentially calling all the active listeners you have.
// make your own emitter:
function EventEmitter () {
// holds events buy type; ex:'ADD_TODO'
this._events = {};
}
EventEmitter.prototype.on = function(type, listener) {
// add events to listen for
this._events[type] = this._events[type] || [];
this._events[type].push(listener);
};
EventEmitter.prototype.emit = function(type) {
// emit event base on type
if (this._events[type]) {
this._event[type].forEach(function(listener) {
// call listeners for events:
listener();
});
}
};
EventEmitter.prototype.removeListener = function(type, listern) {
if (this._events[type]) {
this._events[type].splice(this._events[type].indexOf(listener), 1)
}
};
Related
How can I create my own "on" event handler in my class?
For example, WebSocket has multiple "on" event handler as follows.
var ws = new WebSocket("wss://localhost:1000/hoge");
ws.connect();
ws.onopen = function(e){
//TO DO once connection get established
}
ws.onmessage = function(e){
// TO DO once message comes from server sie
}
It might be silly question, but I would like to have simple sample code for the above.
Make those properties getters and setters:
class Foo {
get onsomeevent() {
return this._someeventHandler;
}
set onsomeevent(newVal) {
this._someeventHandler = newVal;
}
trigger() {
if (this._someeventHandler) {
this._someeventHandler();
}
}
}
const f = new Foo();
f.onsomeevent = () => console.log('handler running');
console.log('about to trigger');
f.trigger();
console.log(f.onsomeevent);
Let's say you wanted to have ws.ongameover and you wanted that called when the game was over.
You can simply write code like this:
// assign your function to the ws.ongameover property
ws.ongameover = function() {
console.log("game over");
}
Then, some place else in your code where you see the game is actually over, you can then trigger that like this:
// if there's a gameover handler, then call it
if (ws.ongameover) {
ws.ongameover();
}
Note, it is generally a better design pattern to use events and event handlers because that's more scalable as you can have multiple listeners for the same event and individual pieces of code can add or remove their own listeners. In the browser, they use addEventListener() and in node.js, they use the EventEmitter class interface (the two are similar in concept). With either one, you can create your own event names, code can listen for those event names and then other code can fire those events when they occur.
How can I subscribe/or listen to an event/method in a regular TypeScript file that is part of our AngularJs TypeScript project?
Context: We have a loader, progressBar.ts with a method, updateGlobalProgressBar, that is exposed via the export attribute, that keeps track of the percentage loaded. On the other hand I have a controller, ribbonController, that sets certain properties of the ribbon view.
Is there a way I can subscribe or listen to the updateGlobalProgressBar method in my ribbonController, to know when the loader has reached 100%?
Please note: the progressBar.ts is not an AngularJs service or controller, but a plain TypeScript file with nothing being injected, no constructor method etc.
I have tried a do while loop, but this created an endless loop:
do {
this._scope.loaderHasLoaded = utilities.loadingComplete();
}
while (this._scope.loaderHasLoaded === false);
I have tried an if loop, but this only fired once, and obviously not at the right time:
if (utilities.loadingComplete()) {
this._scope.loaderHasLoaded = true;
} else {
this._scope.loaderHasLoaded = false;
}
I need to somehow listen to a variable or something in the progressBar.ts to inform me when the load is done. I wrote the following helper method that gets called when the load is complete from within the updateGlobalProgressBar:
export function loadingComplete(): boolean {
if (this.loadComplete === true) {
return true
} else {
return false;
}
}
I can call this method etc, but don't know how to subscribe to it?
Any help will be greatly appreciated!
I will attempt to help you out.
Approach:
create event emitters in progressBar.ts that emit events when the progress bar reaches 100% status.
expose the event via export attribute.
subscribe/listen to the event and perform necessary actions.
Trigger event
clean up
Now, I will elaborate with some code:
create event emitters in progressBar.ts that emit events when the progress bar reaches 100% status.
Basic event emitter class and instance creation:
class EventEmitter {
listeners=[];
register(callback) {
var _listeners = this.listeners
_listeners.push(callback);
return function() {
_listeners.splice(_listeners.indexOf(callback), 1);
}
}
trigger() {
var args =arguments;
this.listeners.forEach(callback => callback.apply(null, args));
}
}
const onProgressBarComplete = new EventEmitter();
expose the event via export attribute
export onProgressBarComplete;
subscribe/listen to the event and perform necessary actions.
import {onProgressBarComplete} from './progressBar';
var unListenToEvent = onProgressBarComplete.register(function() {
// code in response to event
});
trigger Event: In updateGlobalProgressBar method add condition
if(progress === 100) { //This condition is up to you
onProgressBarComplete.trigger();
}
clean up.
when you are no longer interested in the event remove subscirption by executing:
unListenToEvent();
Hope This points you in the right direction.
Cheers!
I'm trying to figure out if there is a way to get all of the functions called from an onclick event.
The scenario is something like this:
HTML:
<div onclick="a(); b();">
<a href="javascript();" onclick="c(event);>link</a>
</div>
JS:
var a = function() { console.log('a called'); };
var b = function() { console.log('b called'); };
var c = function(e) {
if (e.call['a']) { e.call['a'].stopPropagation(); }
console.log('b and c called, but not a.');
};
I want to use this to control which functions are stopped from propagating.
I'm afraid there are lots of incantations in this code. You should try and read more about the event flow, and to a lesser extent (because the subject is harder) about javascript execution contexts and possibly the javascript pseudo-protocol.
If I understand you correctly, you want a certain listener to be skipped if a previous listener has already been executed in the event flow - or, more probably, you want some content to be skipped, if some other content is executed first.
The user agent gives you no way to monitor the state of the execution of your listeners, so you need to manage that state yourself. Each listener must register that it has been called, so that next listeners can consult this state and adjust their logic. Or, as such an approach is likely to generate too much complexity (because of the tight coupling between listeners), you'd probably be better off writing some light rules framework, based on your needs.
You've asked a very specific, technical question, but do not hesitate to provide more context (the "why"), as the proper solution should be more a design-based one, rather than a technical-based one.
You could set global variables and set them when they are being called, for example:
aClicked = false;
bClicked = false;
function a() { aClicked = true; }
function b() { bClicked = true; }
var c = function() {
if (!aClicked && bClicked) {
console.log('b and c called, but not a.');
}
};
I am a somewhat green programmer, and quite new to javascript/jquery, but I thought I understood javascript events. Apparently not. I am not able to get event listeners to work as I'd like.
Given javascript:
var Thing = {
//stuff
update: function() {
$.event.trigger({type:'stateUpdate', more:stuff});
}
};
var Room = {
//more stuff
updateHandler: function (e) {
//handle event here
}
};
If I do jquery:
$(document).on('stateUpdate', $Room.updateHandler);
then it works fine, but I can't do either
$(Room).on('stateUpdate', $Room.updateHandler);
or
Room.addEventListerner('stateUpdate', $Room.updateHandler);
The first does nothing, the second gives .addEventListerner is not a function error.
I've googled for hours and can't figure it out. I found something that said .addEventListener only works on objects that implement EventListener, something about handleEvent, and something about functions automatically implementing EventListener. Nothing on how to make an object implement it. Is there no way to add listeners to javascript objects that aren't functions? Am I going to have to create an event handler object, or use 'document' or 'window' and have it call handlers? That seems really ugly.
Should the objects be functions in the first place? Would that work? It seems the current opinion is that making everything functions is just trying to make javascript into something it isn't.
AFAIK there are no way to add a event listener to the plain object, as it is not placed inside DOM. Events are firing inside DOM, and bubbling so your event listener for custom object won't receive it.
There is a http://www.bobjs.com/ framework that can help you implement custom events.
In response to #Barmar (sort of) I believe I worked this out. Confirmation on if this is a a good alternative or not would be nice, though. Basically, I have to do a subscriber thing, right? Almost event/listener, but not quite.
var thing = {
callbacks: {},
regCallback: function (key, which) {
callbacks[key] = which;
},
remCallback: function (key) {
callbacks[key].delete;
}
update: function(e) {
for(var i = 0, len = callbacks.length; i < len;i++){
callbacks[i](e);
};
}
};
var Room = {
updateHandler: function () {
//handle stuff
},
subscribe: function (which, callback) {
which.regCallback('room', callback);
}
unsub: function (which) {
which.remCallback('room');
}
};
//wherever/whenever I need to get updates something like
Room.subscribe(thing, Room.updateHandler);
//unsub
Room.unsub(thing);
Second error is caused by typo: addEventListerner has extra r in it.
Setting:
let's say three events happen in three separate part of some application, the event should be handled by two controllers. The application dispatcher is responsible for sending/receiving events from all parts of the application, and this dispatcher should have an asynchronous event queue.
In some cases, two the of three events are related along some attribute but are not of the same name, and only one should be passed to the controller, the other one may be discarded.
Problem:
Currently, I have a 'queue' that really just bounces the event to the controller, this is unhelpful because I have no way of comparing two events in the queue if only one is ever there at time.
So how do I ensure the events should stay in the queue for a while? I suppose a timeout function could do the trick but is there a better way?
To give credit where it's due, the idea of coalescing events is inspired by Cocoa and I'm basically trying to do something similar.
I don't know much about Cocoa, but I assume that it replaces older events with the latest event until the event is able to be dispatched to the application (i.e. if the application is busy for some reason). I'm not sure what your particular use case is, but if you want to rate-limit the events, I would use setTimeout like this:
function Dispatcher(controllers) {
this.controllers = controllers;
this.events = [];
this.nextController = 0;
}
Dispatcher.prototype = {
_dispatch: function (i) {
var ev = this.events.splice(i, 1);
this.controllers[this.nextController].handle(ev);
this.nextController = (this.nextController + 1) % this.controllers.length;
},
notify: function (ev) {
var index = -1, self = this, replace;
function similer(e, i) {
if (e.type === ev.type) {
index = i;
return true;
}
}
replace = this.events.some(similar);
if (replace) {
this.events[i] = ev;
} else {
// it's unique
index = this.events.push(ev) - 1;
setTimeout(function () {
self._dispatch(index);
}, 100);
}
}
};
Just call notify with the event (make sure there's a type property or similar) and it will handle the magic. Different types of events will be handled uniquely with their own setTimeout.
I haven't tested this code, so there are probably bugs.
I suppose a timeout function could do the trick but is there a better way?
No, there really isn't.
Usually the way to go is using setTimeout(..., 0) if your events are dispatched in the same run loop. So the implementation would look something like this:
var eventQueue = [];
var handlerTimer = -1;
function fireEvent(event, params) {
eventQueue.push([event, params]);
window.clearTimeout(handlerTimer);
handlerTimer = window.setTimeout(resolveQueue, 0);
}
function resolveQueue() {
// process eventQueue and remove unwanted events...
// then dispatch remaining events
eventQueue = [];
}
If you need to handle events from different run loops (for example native events like mouseup and click), you need to use some timeout value other than 0. The exact value depends on how long you want to accumulate events.