Using Bacon.js with FlightJS - javascript

Is Bacon compatible with Twitter's Flight? I saw this talk where they are apparently being used together (https://www.youtube.com/watch?v=D0N1NdE-9u0) but couldn't get a minimal example to work.
This is my flight component with traditional event handling alongside Bacon's event stream.
The latter just logs undefined for the data.a and it turns out what's being passed to the function registered with onValue is actually the event object (named e in the traditional handler function) with no access to the data object.
define(function (require) {
'use strict';
var c = require('flight/lib/component'),
$ = require('jquery'),
B = require('bacon');
$.fn.asEventStream = B.$.asEventStream;
return c(f);
function f() {
this.after('initialize', function () {
// traditional handler
this.on('dummyData', function (e, data) {
console.log('jquery: ' + data.a);
});
// Bacon handler
this.$node.asEventStream('dummyData').onValue(function (data) {
console.log('bacon: ' + data.a);
});
// emit data object
this.trigger('dummyData', { a: 'b' });
});
}
});

You can pass an optional argument to asEventStream function that you can use to map the event payload:
this.$node.asEventStream('dummyData', function(e, data) {
return data;
}).onValue(function (data) {
console.log('bacon: ' + data.a);
});
Take a look at the examples here: https://github.com/baconjs/bacon.js#%24-aseventstream

Related

Storing functions in array. Is it a good practice?

var sing = function(name) {
console.log(name + " is SINGING");
}
var cry = function(name) {
console.log(name + " is CRYING");
}
var list = [sing, cry];
for(var func of list) {
func('foo');
}
This is exactly what I want in my code. But I am not sure if its a good practice.
Yes, in some situations, it is the preferable design to store functions in an array.
Imagine the following EventEmitter class. You can register event listeners using a method called on and dispatch an event using emit. The functions are stored in an array:
var EventEmitter = function () {
this._events = {};
};
EventEmitter.prototype.on = function (event, listener) {
if (!this._events.hasOwnProperty(event)) {
this._events[event] = []; // stores the listeners bound to the event's name
}
this._events[event].push(listener); // add the listener to the event's listeners array
};
EventEmitter.prototype.emit = function (event) {
var args = Array.slice(arguments, 1);
if (this._events.hasOwnProperty(event)) {
this._events[event].forEach(function (listener) {
listener.apply(null, args);
});
}
}
var emitter = new EventEmitter();
// push the first function to the array
emitter.on('event-a', function (data) {
console.log('Event A was fired!');
});
// push the second function to the array
emitter.on('event-a', function (data) {
console.log('Second listener to event A');
});
emitter.on('event-b', function (a, b, c) {
console.log('Event B:', a + b + c);
});
emitter.emit('event-a');
setTimeout(function () {
emitter.emit('event-b', 2, 3, 4);
}, 1500);
This is why I would use it in some situations. I don't consider it bad practice.
var functionName = function(name, action) {
console.log(name + " is "+ action );
}
functionName("Aditya", "Programing");
Aditya, I think you should choose this format. It is always good practice to keep a single function for similar kind of tasks to perform.

PubsubJs and *this* is undefined when subscribing to a message

I have the following code:
someClass1 = function () {
this.doStuff = function () {
PubSub.publish('topic1', { id: 1 });
}
}
someClass2 = function () {
this.forename = 'bob2';
PubSub.subscribe("topic1", function (msg, data) {
log(msg, data, this.forename);
});
}
function log() {
console.log(arguments);
}
var c1 = new someClass1();
var c2 = new someClass2();
c1.doStuff();
and I am using the pubsubjs library (https://github.com/federico-lox/pubsub.js)
The code is simple - publish a message and handle it in another class (someClass2) using PubSub
My question is that when I publish a message and handle it in someClass2, this is undefined. This occurs at the line: log(msg, data, this.forename);
This means I cant access any of the someClass2 properties/functions. What do I need to do to get the this to not be undefined? Is this possible? Are they other libraries that will help? Am I doing it wrong....
All help apprenticed! Thanks
You're passing an unbound function to subscribe. Such a function has "no idea" about this. You have to bind it:
PubSub.subscribe("topic1", (function (msg, data) {
log(msg, data, this.forename);
}).bind(this));
this is not what you expect inside the callback, just cache a copy of this as another variable outside:
someClass2 = function () {
this.forename = 'bob2';
var that = this;
PubSub.subscribe("topic1", function (msg, data) {
log(msg, data, that.forename);
});
}

Binding events to functions within objects

I'm running into a peculiar issue with jQuery/JS in general.
I'm working on a JS file that let me create portlets for a client site. I'm using SignalR to communicate changes to the users.
The following piece of code gives me a bit of a headache as to why it won't work.
Eenheden: function (poPortlet) {
this.moPortlet = poPortlet;
this.Init = function () {
$(this.moPortlet).find('.portlet-title').text($(this.moPortlet).attr('data-cpv-type') + ' Eenheden')
};
this.BindHub = function () {
CPV.moHub.on('onEenheidUpdate', this.Events.Update);
};
this.Events = {
Update: function (pnId, psStatus) {
CPV.Log('Error', 'Update: ' + pnId + ' ' + psStatus);
}
};
}
I am trying to bind the function this.Events.Update on the SignalR event onEenheidUpdate. Instances of these Eenheiden objects are not unique on the pages. The idea is that they contain the same data, but can get filtered, creating a different portlet depending on some configs.
My problem is that the onEenheidUpdate function doesn't trigger the proper event. I want to do it like this so I can use references I set for the unique object, such as the jQuery object I set on initialization.
Problem
Your problem is that when jQuery or javascript in general triggers an event callback, the "context" (value of this) is changed. For example
var MyModule = {
init: function () {
$('.amazing-element').on('click', function () {
// This breaks because `
// `this` is not MyModule
// When the callback is triggered
// `this` is `<p class="amazing-element"></p>`
this.onClick()
})
},
onClick: function () {
console.log('wee, I was clicked')
}
}
Function.prototype.bind to the rescue!
var MyModule = {
init: function () {
$('.amazing-element').on('click', function () {
this.onClick()
}.bind(this))
// ^ here we bind the context of the callback to the
// correct version of `this`.
},
onClick: function () {
console.log('wee, I was clicked')
}
}
So your exact example would look like:
Eenheden: function (poPortlet) {
this.moPortlet = poPortlet;
this.Init = function () {
$(this.moPortlet).find('.portlet-title').text($(this.moPortlet).attr('data-cpv-type') + ' Eenheden')
};
this.BindHub = function () {
CPV.moHub.on('onEenheidUpdate', this.Events.Update.bind(this));
// ^ change here
};
this.Events = {
Update: function (pnId, psStatus) {
CPV.Log('Error', 'Update: ' + pnId + ' ' + psStatus);
}
};
}

Rivets.js event handlers with custom arguments

I've just started with Rivets.js, which looks promising as simple data-binding framework.
I've arrived at the point that I don't know how to pass "custom arguments" to the rv-on-click binder, so I tried to take the idea from this: https://github.com/mikeric/rivets/pull/34
My code:
rivets.binders["on-click-args"] = {
bind: function(el) {
model = this.model;
keypath = this.keypath;
if(model && keypath)
{
var args = keypath.split(' ');
var modelFunction = args.shift();
args.splice(0, 0, model);
var fn = model[modelFunction];
if(typeof(fn) == "function")
{
this.callback = function(e) {
//copy by value
var params = args.slice();
params.splice(0, 0, e);
fn.apply(model, params);
}
$(el).on('click', this.callback);
}
}
},
unbind: function(el) {
$(el).off('click', this.callback);
},
routine: function(el, value) {
}
}
This code is working, my question is: is this the correct way?
If you want to pass a custom argument to the event handler then this code might be simpler:
rivets.configure({
// extracted from: https://github.com/mikeric/rivets/issues/258#issuecomment-52489864
// This configuration allows for on- handlers to receive arguments
// so that you can onclick="steps.go" data-on-click="share"
handler: function (target, event, binding) {
var eventType = binding.args[0];
var arg = target.getAttribute('data-on-' + eventType);
if (arg) {
this.call(binding.model, arg);
} else {
// that's rivets' default behavior afaik
this.call(binding.model, event, binding);
}
}
});
With this configuration enabled, the first and only argument sent to the rv-on-click handler is the value specified by data-on-click.
<a rv-on-click="steps.go" data-on-click="homePage">home</a>
This is not my code (I found it here), but it does work with Rivets 0.8.1.
There is also a way to pass the current context to the event handler. Basically, when an event fires, the first argument passed to the handler is the event itself (click, etc), and the second argument is the model context.
So lets say that you are dealing with a model object that is the product of a rv-each loop...
<div rv-each-group="menu.groups">
<input rv-input="group.name"><button rv-on-click="vm.addItem">Add item</button>
___ more code here ___
</div>
Then you can access the current "group" object in the event handler like this:
var viewModel = {
addItem: function(ev, view) {
var group = view.group;
}
};
More details on this technique can he found here https://github.com/mikeric/rivets/pull/162
I hope this helps.
There is another answer here:
https://github.com/mikeric/rivets/issues/682
by Namek:
You could define args formatter:
rivets.formatters.args = function(fn) {
let args = Array.prototype.slice.call(arguments, 1)
return () => fn.apply(null, args)
}
and then:
rv-on-click="on_click | args 1"
Please note that args is not a special id, you can define anything instead of args.
To pass multiple arguments use space: "on_click | args 1 2 3 'str'"

Is it possible for different classes to detect 'emits' from eachother in Nodejs?

I am struggling with what seems to be a simple concept which makes me think what I am doing can't be done.
In nodejs, if class objectA.emits('hey there'), can class objectB.on('hey there') repsond with 'yo'?
Object A and B have nothing to do with eachother other than they both extend EventEmitter and are in the same nodejs app.
Sorry if this question has been asked before, I can't find it.
Yes
That's pretty much it.
When dealing with Observer/Publisher-Subscriber patterns (or Mediator Patterns), the point is that it really doesn't matter what type of class it is that's doing the "emitting".
Assuming that A is an emitter:
var B = { doStuff : function () { console.log("Yo!"); } };
A.addListener("someEvent", B.doStuff);
A.emit("someEvent");
If you actually want them to talk back and forth, then you need to manually subscribe them to one another...
Assuming that both A AND B are emitters:
B.doStuff = function () { this.emit("B's event", "Yo!"); };
A.doThing = function (param) { console.log(param); };
B.addListener("B's event", A.doThing);
A.addListener("A's event", B.doStuff.bind(B));
A.emit("A's event");
Alternatively, you should look into a Mediator pattern (which also "emits", but is intended to be 1 object which mediates between many objects who don't know one another, but use the same event-names and pass well-defined data-structures, like a good API should).
Assuming that Mediator is an emitter, and A, B and C aren't:
var A = {
getData : function (request) { /* network call */ this.dataCallback(data); },
dataCallback : function (data) { Mediator.emit("data-recieved", data); }
},
B = {
display : document.getElementById("data-display"),
showData : function (data) { /* make DOM representation */ }
},
C = {
input : document.getElementById("request-input"),
button : document.getElementById("request-button"),
getRequest : function () {
var request = this.input.value;
this.requestData(request);
this.disableButton();
},
requestData : function (request) { Mediator.emit("data-request", request); },
disableButton : function () { this.button.disabled = true; },
enableButton : function () { this.button.disabled = false; }
};
Mediator.addListener("data-request", A.getData.bind(A));
Mediator.addListener("data-received", B.showData.bind(B));
Mediator.addListener("data-received", C.enableButton.bind(C));
C.button.addEventListener("click", C.getRequest.bind(C), false);
So now you've got 3 classes who know nothing about one another, each has its own special purpose, and the only expectations that they have of "one another" are that event-names and data-types are appropriate.
They all know about Mediator.
If you want Mediator to be further abstracted, then you can pass a reference to it when you're making your class:
function A (param1, param2) {
var emitter = null;
this.setEmitter = function (myEmitter) { emitter = myEmitter; };
this.emit = function (evt, data) {
if (!emitter) { return; }
emitter.emit(evt, data);
};
this.subscribe = function (evt, callback) {
if (!emitter) { return; }
emitter.addListener(evt, callback);
};
/* rest of the object */
};
var a = new A();
var b = new A();
a.setEmitter(Mediator);
a.subscribe("some-evt", a.doSomething.bind(a));
b.setEmitter(Mediator);
b.subscribe("other-evt", b.doSomethingElse.bind(b));
a.emit("other-evt", { /* data */ });
a and b don't have to be the same class, here, at all.
And now they DO work in the way that you're imagining.
Both have used Dependency Injection ("Inversion of Control") to point to the same emitter (Moderator), so they're both working off of the same event-list, without even knowing it, and using their own methods to subscribe to Moderators events.

Categories