I think I must be misunderstanding something fundamental, because in my mind this should be the most basic case for an observable, but for the life of my I can't figure out how to do it from the docs.
Basically, I want to be able to do this:
// create a dummy observable, which I would update manually
var eventObservable = rx.Observable.create(function(observer){});
var observer = eventObservable.subscribe(
function(x){
console.log('next: ' + x);
}
...
var my_function = function(){
eventObservable.push('foo');
//'push' adds an event to the datastream, the observer gets it and prints
// next: foo
}
But I have not been able to find a method like push. I'm using this for a click handler, and I know they have Observable.fromEvent for that, but I'm trying to use it with React and I'd rather be able to simply update the datastream in a callback, instead of using a completely different event handling system. So basically I want this:
$( "#target" ).click(function(e) {
eventObservable.push(e.target.text());
});
The closest I got was using observer.onNext('foo'), but that didn't seem to actually work and that's called on the observer, which doesn't seem right. The observer should be the thing reacting to the data stream, not changing it, right?
Do I just not understand the observer/observable relationship?
In RX, Observer and Observable are distinct entities. An observer subscribes to an Observable. An Observable emits items to its observers by calling the observers' methods. If you need to call the observer methods outside the scope of Observable.create() you can use a Subject, which is a proxy that acts as an observer and Observable at the same time.
You can do like this:
var eventStream = new Rx.Subject();
var subscription = eventStream.subscribe(
function (x) {
console.log('Next: ' + x);
},
function (err) {
console.log('Error: ' + err);
},
function () {
console.log('Completed');
});
var my_function = function() {
eventStream.next('foo');
}
You can find more information about subjects here:
https://github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/subject.md
http://reactivex.io/documentation/subject.html
I believe Observable.create() does not take an observer as callback param but an emitter. So if you want to add a new value to your Observable try this instead:
var emitter;
var observable = Rx.Observable.create(e => emitter = e);
var observer = {
next: function(next) {
console.log(next);
},
error: function(error) {
console.log(error);
},
complete: function() {
console.log("done");
}
}
observable.subscribe(observer);
emitter.next('foo');
emitter.next('bar');
emitter.next('baz');
emitter.complete();
//console output
//"foo"
//"bar"
//"baz"
//"done"
Yes Subject makes it easier, providing Observable and Observer in the same object, but it's not exactly the same, as Subject allows you to subscribe multiple observers to the same observable when an observable only send data to the last subscribed observer, so use it consciously.
Here's a JsBin if you want to tinker with it.
var observer = Observable.subscribe(
function(x){
console.log('next: ' +
var my_function = function(){
Observable.push('hello')
One of the way to update an observable.
Related
When using Dependency injection in Angular I often need to subscribe to an observable that I haven't yet created!
I often end up using something like this:
// create behavior subject OF Observable<number>
const subject = new BehaviorSubject<Observable<number>>(EMPTY);
// subscribe to it, using flatMap such as to 'unwrap' the observable stream
const unwrappedSubject = subject.pipe(flatMap((x: number) => x));
unwrappedSubject.subscribe(s => console.log(s));
// now actually create the observable stream
const tim = timer(1000, 1000);
// set it into the subject
subject.next(tim);
This uses flatMap to 'unwrap' the observable contained in the subject.
This works fine, but frankly it always feels 'icky'.
What I really want is something like this, where the consumer of the subject treats the instance of the Subject as Observable<number> without having to pipe it every usage.
const subject = new UnwrappingBehaviorSubject<number>(EMPTY);
subject.subscribe((x: number) => console.log(x));
// this could use 'next', but that doesn't feel quite right
subject.setSource(timer(1000, 1000));
I'm aware that I could subscribe to the timer and hook it up directly to the subject, but I also want to avoid an explicit subscribe call because that complicates the responsibility of unsubscribing.
timer(1000, 1000).subscribe(subject);
Is there a nice way to achieve this?
The Subject.ts and BehaviorSubject.ts source files get more complicated than I expected. I'm scared I'll end up with horrible memory leaks if I try to fork it.
I think this would be another way to solve it:
foo.component.ts
export class FooComponent {
private futureObservable$ = new Observable(subscriber => {
// 'Saving' the subscriber for when the observable is ready.
this.futureObservableSubscriber = subscriber;
// The returned function will be invoked when the below mentioned subject instance
// won't have any subscribers(after it had at least one).
return () => this.futureObservableSubscription.unsubscribe();
}).pipe(
// You can mimic the Subject behavior from your initial solution with the
// help of the `share` operator. What it essentially does it to *place*
// a Subject instance here and if multiple subscriptions occur, this Subject instance
// will keep track of all of them.
// Also, when the first subscriber is registered, the observable source(the Observable constructor's callback)
// will be invoked.
share()
);
private futureObservableSubscriber = null;
// We're using a subscription so that it's easier to collect subscriptions to this observable.
// It's also easier to unsubscribe from all of them at once.
private futureObservableSubscription = new Subscription();
constructor (/* ... */) {};
ngOnInit () {
// If you're using `share`, you're safe to have multiple subscribers.
// Otherwise, the Observable's callback(i.e `subscriber => {...}`) will be called multiple times.
futureObservable$.subscribe(/* ... */);
futureObservable$.subscribe(/* ... */);
}
whenObservableReady () {
const tim = timer(1000, 1000);
// Here we're adding the subscription so that is unsubscribed when the main observable
// is unsubscribed. This part can be found in the returned function from the Observable's callback.
this.futureObservableSubscription.add(tim.subscribe(this.futureObservableSubscriber));
}
};
Indeed, a possible downside is that you'll have to explicitly subscribe, e.g in the whenObservableReady method.
With this approach you can also have different sources:
whenAnotherObservableReady () {
// If you omit this, it should mean that you will have multiple sources at the same time.
this.cleanUpCrtSubscription();
const tim2 = timer(5000, 5000);
this.futureObservableSubscription.add(tim2.subscribe(this.futureObservableSubscriber));
}
private cleanUpCrtSubscription () {
// Removing the subscription created from the current observable(`tim`).
this.futureObservableSubscription.unsubscribe();
this.futureObservableSubscription = new Subscription();
}
I know observables in RxJS 5 (and elsewhere) are lazily executed. In other words, they aren't executed until there is a subscriber. However, I'm trying to prefetch some data. Is there a way to trigger the observable before subscribing to it?
let obs = Rx.Observable.create(observer => {
console.log('Observer executed');
// This would actually be fetching data from a server:
observer.next(42);
});
// Something like obs.warmup() happens here
console.log('Observer is ideally called before this point.');
// Some time later this is called, and hopefully the data is already retrieved.
obs.subscribe(value => {
console.log('Got ' + value);
});
You would like to make a cold observable hot. (what are hot and cold observables)
So if you already have a cold observable you can use the publish operator alongside with connect.
let obs = Rx.Observable.create(observer => {
console.log('Observer executed');
// This would actually be fetching data from a server:
observer.next(42);
}).publish(); // create a ConnectableObservable
obs.connect(); // Run the observer
// Something like obs.warmup() happens here
console.log('Observer is ideally called before this point.');
// Some time later this is called, and hopefully the data is already retrieved.
obs.subscribe(value => {
console.log('Got ' + value);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.0-rc.1/Rx.js"></script>
But usually there is a much simpler way. I assume you have an external source of events, which you want to convert to an observable. The correct way is to use a Subject.
let obs = new Rx.Subject();
console.log('Observer executed');
obs.next(42); // subscribers would receive this...
// it could be something like `service.on("event", e => obs.next(e));`
// Something like obs.warmup() happens here
console.log('Observer is ideally called before this point.');
// Some time later this is called, and hopefully the data is already retrieved.
obs.subscribe(value => {
console.log('Got ' + value);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.0-rc.1/Rx.js"></script>
I'd like to find a receipt for provide a Promiselike Observable
i mean:
that Observable provides a single value and completes,
and any subscriber (before and after completion) should get that single value.
i came out with a combination of Rx.Observable.create publishLast and connect.
var getPromiseLike=function(){
console.log('getPromiseLike()');
//// var an_object={}; <--- this should happen inside Obs function
var prom_like = Rx.Observable.create(function(obs) {
var an_object={}; /// <--- here !
setTimeout(function(){
obs.onNext(an_object);
obs.onCompleted();
},500);
}).publishLast();
prom_like.connect();
return prom_like;
};
var promiselike=getPromiseLike();
var obj1;
promiselike
.subscribe(function(obj){
console.log('onNext1');
obj1 = obj;
},
function(err){},
function(){
console.log('onComplete1');
});
setTimeout(function(){
promiselike
.subscribe(function(obj){
console.log('onNext2 obj1===obj :'+(obj1===obj)); //true
},
function(err){},
function(){
console.log('onComplete2');
});
},1000);
/*LOGS:
getPromiseLike()
script.js:19 onNext1
script.js:24 onComplete1
script.js:31 onNext2 obj1===obj :true
script.js:35 onComplete2
*/
is this the simplest solution or there's some builtin that i missed?
plnkr
In RxJS typically a promise like construct is modeled with AsyncSubject.
It has many of the properties that promises have and is useful:
It only receives one value like a promise and will call next only with that value.
It caches that value for all future computations.
It's a Subject, so it's like a deferred in some promise libraries.
Note: Personally I see no issue with mixing promises with observables, I do it in my own code and Rx plays with promises nicely.
Reading up on tutorials of Backbone, it seems that when the add event is fired from a collection, the item added is sent along with the event (same goes for remove). I can't find any documentation on this feature on the backbonejs.org site and was curious if there was a way I could send an object along with my custom events. Secondly, is something like this possible in Marionette?
Each object defined by Backbone mixes in Backbone.Events which means you can trigger events with object.trigger. It is defined as
trigger object.trigger(event, [*args])
Trigger callbacks for the given event, or space-delimited list of events. Subsequent arguments
to trigger will be passed along to the event callbacks.
You just have to pass additional arguments to get them in your callbacks.
For example,
var m = new Backbone.Model();
m.on('custom', function(more) {
console.log(more);
});
m.trigger('custom', 'more info');
will log more info
See http://jsfiddle.net/nikoshr/HpwXe/ for a demo
You would trigger an event with a reference to the object to emulate the behavior of backbone :
var m = new Backbone.Model();
m.on('custom', function(model, more) {
console.log(arguments);
});
m.trigger('custom', m, 'more info');
http://jsfiddle.net/nikoshr/HpwXe/1/
And in a derived model:
var M = Backbone.Model.extend({
custom: function() {
this.trigger('custom', this);
}
});
var m = new M();
m.on('custom', function(model, more) {
console.log(model);
});
m.custom();
http://jsfiddle.net/nikoshr/HpwXe/2/
Yes of course, you can use Backbone.Event
var collection = Backbone.Collection.extend();
collection = new collection();
collection.on("message", function(message){
console.log(message);
});
var model = new Backbone.Model();
collection.add(model);
model.trigger("message", "This is message");
About what types of events you can see to backbone documentation.
This is demo
Also you can use Event Aggregator from Marionette.js
An event aggregator implementation. It extends from Backbone.Events to provide the core event handling code in an object that can itself be extended and instantiated as needed.
var vent = new Backbone.Wreqr.EventAggregator();
vent.on("foo", function(){
console.log("foo event");
});
vent.trigger("foo");
I'm facing a "change event not firing" issue on Backbone.js =/
Here my view of User model :
window.UserView = Backbone.View.extend({
...
initialize: function()
{
this.model.on('destroy', this.remove, this);
this.model.on('change', function()
{
console.log('foo');
});
},
render: function(selected)
{
var view = this.template(this.model.toJSON());
$(this.el).html(view);
return this;
},
transfer: function(e)
{
var cas = listofcas;
var transferTo = Users.getByCid('c1');
var transferToCas = transferTo.get('cas');
this.model.set('cas', cas);
console.log('current model');
console.log(this.model);
//this.model.change();
this.model.trigger("change:cas");
console.log('trigger change');
transferTo.set('cas', transferToCas);
console.log('transferto model');
console.log(transferTo);
//transferTo.change();
transferTo.trigger("change:cas");
console.log('trigger change');
}
});
Here, the User model :
window.User = Backbone.Model.extend({
urlRoot: $('#pilote-manager-app').attr('data-src'),
initialize: function()
{
this.set('rand', 1);
this.set('specialite', this.get('sfGuardUser').specialite);
this.set('name', this.get('sfGuardUser').first_name + ' ' + this.get('sfGuardUser').last_name);
this.set('userid', this.get('sfGuardUser').id);
this.set('avatarsrc', this.get('sfGuardUser').avatarsrc);
this.set('cas', new Array());
if (undefined != this.get('sfGuardUser').SignalisationBouclePorteur) {
var cas = new Array();
_.each(this.get('sfGuardUser').SignalisationBouclePorteur, function(value)
{
cas.push(value.Signalisation);
});
this.set('cas', cas);
}
}
});
In User model, there is "cas" attribute, which is an array of objects.
I read in others topics that change events are not fire on model.set if attributes are not a value.
So, I try to trigger directly the change event with model.change() method.
But, I have no "foo" log in my console ...
I'm pretty new to backbone and I was having this same problem.
After doing some research, I found a few posts that shed a little bit more light on why this was happening, and eventually things started to make sense:
Question 1
Question 2
The core reason has to do with the notion of reference equality versus set/member equality. It appears that to a large extent, reference equality is one of the primary techniques backbone uses to figure out when an attribute has changed.
I find that if I use techniques that generate a new reference like Array.slice() or _.clone(), the change event is recognized.
So for example, the following code does not trigger the event because I'm altering the same array reference:
this.collection.each(function (caseFileModel) {
var labelArray = caseFileModel.get("labels");
labelArray.push({ Key: 1, DisplayValue: messageData });
caseFileModel.set({ "labels": labelArray });
});
While this code does trigger the event:
this.collection.each(function (caseFileModel) {
var labelArray = _.clone(caseFileModel.get("labels")); // The clone() call ensures we get a new array reference - a requirement for the change event
labelArray.push({ Key: 1, DisplayValue: messageData });
caseFileModel.set({ "labels": labelArray });
});
NOTE: According to the Underscore API, _.clone() copies certain nested items by reference. The root/parent object is cloned though, so it will work fine for backbone. That is, if your array is very simple and does not have nested structures e.g. [1, 2, 3].
While my improved code above triggered the change event, the following did not because my array contained nested objects:
var labelArray = _.clone(this.model.get("labels"));
_.each(labelArray, function (label) {
label.isSelected = (_.isEqual(label, selectedLabel));
});
this.model.set({ "labels": labelArray });
Now why does this matter? After debugging very carefully, I noticed that in my iterator I was referencing the same object reference backbone was storing. In other words, I had inadvertently reached into the innards of my model and flipped a bit. When I called setLabels(), backbone correctly recognized that nothing changed because it already knew I flipped that bit.
After looking around some more, people seem to generally say that deep copy operations in javascript are a real pain - nothing built-in to do it. So I did this, which worked fine for me - general applicability may vary:
var labelArray = JSON.parse(JSON.stringify(this.model.get("labels")));
_.each(labelArray, function (label) {
label.isSelected = (_.isEqual(label, selectedLabel));
});
this.model.set({ "labels": labelArray });
Interesting. I would have thought that .set({cas:someArray}) would have fired off a change event. Like you said, it doesn't seem to, and I can't get it to fire with .change() BUT, I can get the events to work if I just do model.trigger('change') or model.trigger('change:attribute')
This would allow you to trigger the change event without that random attribute hack.
If someone could explain what is going on with events, Backbone, and this code, that would help me learn something too... Here is some code.
Ship = Backbone.Model.extend({
defaults: {
name:'titanic',
cas: new Array()
},
initialize: function() {
this.on('change:cas', this.notify, this);
this.on('change', this.notifyGeneral, this);
},
notify: function() {
console.log('cas changed');
},
notifyGeneral: function() {
console.log('general change');
}
});
myShip = new Ship();
myShip.set('cas',new Array());
// No event fired off
myShip.set({cas: [1,2,3]}); // <- Why? Compared to next "Why?", why does this work?
// cas changed
// general change
myArray = new Array();
myArray.push(4,5,6);
myShip.set({cas:myArray}); // <- Why?
// No event fired off
myShip.toJSON();
// Array[3] is definitely there
myShip.change();
// No event fired off
The interesting part that might help you:
myShip.trigger('change');
// general change
myShip.trigger('change:cas');
// cas changed
I find this interesting and I hope this answer will also spawn some insightful explanation in comments which I don't have.