Backbone pass object with event - events

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");

Related

How do you get data back from a react-redux store?

Using React-Redux
I have a select list that when the user chooses one of the options, a item is created and placed in the database (if it matters, the reason its a select box is that there are multiple variations of the same item and what variation is important).
This is working correctly.
My problem is that I am not sure how I can get the id of the new item out of the redux store.
And just for chuckles, the prior developer set all this up with sagas. So I am still coming up to speed on how it all works together.
So when the select box is checked, the function checkFunction is called that calls the function createItem in the saga file. These functions are below:
in Repositories.jsx
checkFunction = (data) => {
const {createItem} = this.props;
// data holds the info that we need to send to the action
const created = createItem(data);
// once data comes back, redirect the user ot the details of the created item
// need id of created item
// navigateTo(`/item-details/${created.id}`);
}
in Repositories.saga.js
export function* createItem(action) {
try {
const {payload: newItem} = action;
// call api to create item
const created = yield call(requestAPI.post, ITEMS_URL, newItem);
// send message that its been done
yield put(actions.repositories.createItem.ok(created));
// send message to refresh item list
yield put(actions.inventories.fetchItems.start());
} catch (e) {
yield put(actions.repositories.createItem.fail(e));
}
}
I don't understand how to return the id of the created item once its created. I feel like I am missing something basic here. Can anyone point me in the right direction?
Actually getting data from saga back to react component is not trivial. There are multiple approaches to do what you need although each has its downside.
1. Call navigateTo in the saga.
export function* createItem(action) {
...
const created = yield call(requestAPI.post, ITEMS_URL, newItem);
yield call(navigateTo, `/item-details/${created.id}`)
}
This would be my recommended solution if you can get the navigateTo function into the saga. Navigation is a side effect and sagas are there to deal with side effects. Make sure to use the call effect, changing the url by directly calling the function can lead to some issues.
2. Store the latest created item id in redux store
In your reducer, when action actions.repositories.createItem.ok(created) is dispatched, store the created item info and then send the latest created item to the component. Finally you can use componentDidUpdate or useEffect to call navigateTo when the prop changes.
render() {
const created = Redux.useSelector(state => state.created);
useEffect(() => navigateTo(`/item-details/${created.id}`), [created])
...
}
This has the disadvantage that the component will rerender becuase of the changed created value.
3. Send callback in the createItem action
You can put a function into your action and then call it from the saga and essentially using it as a callback.
Component:
checkFunction = (data) => {
const {createItem} = this.props;
// data holds the info that we need to send to the action
const created = createItem(data, (created) => {
navigateTo(`/item-details/${created.id}`);
});
}
Saga:
export function* createItem(action) {
...
const created = yield call(requestAPI.post, ITEMS_URL, newItem);
action.callback(created)
...
}
The problem with this approach is that functions are not serializable and so you ideally should avoid them in your actions. Also, technically, there could be multiple sagas handling the same action and then it gets kind of confusing who should call the callback.

RxJS: How would I "manually" update an Observable?

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.

Rxjs Promise like Observable

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.

Is it possible to set a Knockout dependent observable with AJAX POST

Using Knockout 2.0 and MVC3 Razor forms, I am not able to make a dependent observable work when I introduce an ajax method. I have set up a set of observables that are a part of a calculation, and when I return the product of those observables, I am able to set a SPAN tag with the correct result. However, when I try to use the ajax method to handle those observables and return a result, I get unpredictable behavior. First, it appears the ajax POST does not pick up one of the observables when the INPUT fields are updated (var b POSTs to the action method as 0, but then is eventually updated), and then it seems that I am not able to set the result even when it evaluates correctly. It appears there is timing issue with either the observables or the ajax call. Although simply keeping performing the calculation in javascript works fine, my intent is to call ajax methods for more complicated logic. I have removed the call to ko.applybindings from doc.ready(), and also moved the SCRIPT methods to the bottom of the page- this was the only way I found to make this partly functional. My viewModel is set up as follows:
var viewModel = {
a: ko.observable(0),
b: ko.observable(1),
c: ko.observable(2),
// commented this out, since
// the dependent observable will handle this
// d: ko.observable(0)
};
In my dependent observable:
viewModel.d = ko.dependentObservable(function () {
var theResult = 0;
$('.theLabel').css("visibility", "visible");
theResult=viewModel.a() * viewModel.b() * viewModel.c();
// if we return here we get a valid result
return (theResult);
// prefer to call ajax method
// first check to ensure one variable is set
if (viewModel.a() > 0) {
$.ajax("/myCalculation/getResult", {
data: ko.toJSON(viewModel),
type: "post",
context: viewModel,
contentType: "application/json",
success: function (result) {
// can't set visibility here
$('.theLabel').css("visibility", "visible");
// the POST does not pick up some observables, or
// does not the set dependent observable at all
return result;
}
});
}
});
There is quite a bit wrong with the function you have setup.
1.) You are returning from your function before making your AJAX call. The code after your return statement will never execute.
2.) Even if you omit the first return statement, your AJAX call is Asynchronous... which means it will execute in the background and return control to the outer scope immediately. Since you don't have a return statement, then you are going to return undefined.
3.) The comment in your success callback suggests you are expecting the return statement to propagate all the way up to your computed observable. THAT return statement is only scoped to the callback, and not the outer observable. The return value will be used by jQuery, and your observable will long since have returned.
If you want an observable to call an AJAX function you need a seperate value to store the results of the asynchronous call.
Here is a simplified example:
var viewModel = function(){
var $this = this;
$this.a = ko.observable();
$this.b = ko.observable();
$this.results = ko.observable();
//No need to assign this computed observable to a variable
// because the results will be stored in '$this.results'
// we just need this to handle the automatic updates
ko.computed(function(){
var data = {
a: $this.a(),
b: $this.b()
};
$.post("/do/some/stuff", data, function(results){
$this.results(results);
});
});
};

Backbone.js : change not firing on model.change()

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.

Resources