I am adding a model to a collection using the create method and the api is responding just fine. The model seems to have been properly returned and see the console.dir( resp ); which is what I was looking for. However, when I try to access runningorderid, which is the id as defined with idAttribute, the response is null. I presume this is something to do with the async nature of the response, but I don't know how to deal with it.
var resp = window.app.RunningOrderCollection.create(
{ runningorderid: null, listitemid: 1, starttime: n} ,
{ wait: true }
);
console.dir( resp );
console.dir( resp.get("strt") );
console.dir( resp.id );
collection.create, as all methods related to server requests, is indeed asynchronous. In your case, you could listen to the sync event to get the desired result.
From http://backbonejs.org/#Collection-create
Creating a model will cause an immediate "add" event to be triggered
on the collection, as well as a "sync" event, once the model has been
successfully created on the server.
For example:
resp.on('sync', function(model) {
console.dir( resp );
console.dir( resp.get("strt") );
console.dir( resp.id );
});
To circumvent the async nature of the server-operations of collections and models, bind the actions to be taken after the operations to the events that are triggered when those operations are finished. For example the backbone.js documentation has the following to say about Collection's create-function:
Creating a model will cause an immediate "add" event to be triggered on the collection, as well as a "sync" event, once the model has been successfully created on the server. Pass {wait: true} if you'd like to wait for the server before adding the new model to the collection.
So, you have passed {wait: true}, so the collection will trigger an add event when the model has been created on the server and added to the collection. With this logic:
window.app.RunningOrderCollection.on('add', function(resp) {
console.dir( resp );
console.dir( resp.get("strt") );
console.dir( resp.id );
});
var model = window.app.RunningOrderCollection.create(
{ runningorderid: null, listitemid: 1, starttime: n} ,
{ wait: true }
);
Check out the exellent catalog of events in the backbone.js documentation for more info!
Hope this helps!
Related
var jsonDataNewsFeedGlobal
var jsonDataNewsFeedGlobal = $.getJSON('https://newsapi.org/v1/articles?source=the-verge&sortBy=top&apiKey=938c99f8bd25454ab488d241db84b493');
jsonDataNewsFeedGlobal.done(function(data) {
console.log(data);
console.log(data.source);
});
console.log(jsonDataNewsFeedGlobal.data.source);
My Uncertainty:
i apologize, i should have been using "data" instead of the "jsonDataNewsFeedGlobal"
this part works now,
my real question is how do i set this data to a global variable and use it where ever i want after its done loading.
If I understand correctly here is what you want but please try to give more information in your question.
// Assign handlers immediately after making the request,
// and remember the jqxhr object for this request
var jqxhr = $.getJSON( "example.json", function(data) {
console.log( "success" );
// Here you can append your data where you want
$('selector').append(data);
}).done(function() {
console.log( "second success" );
}).fail(function() {
console.log( "error" );
}).always(function() {
console.log( "complete" );
});
// Perform other work here ...
// Set another completion function for the request above
jqxhr.complete(function() {
console.log( "second complete" );
});
Is taken directly from the docs of jquery here. Feel free to check it. You can check for append and other methods also.
I'm trying to create a caching function in angular using RxJS Observable. Originally I've created this method using angularjs $q's deferred promise. Observables and RxJS are new to me and I find this method of working still somewhat confusing.
This is my current implementation of a getOrCreate caching function. Retrieve a single value for a key from storage (this.get()) and if it's not in there you retrieve it elsewhere (fetcher).
Assume fetcher is a slower data source than this.get(). Multiple requests for the same key could fire while we're still retrieving from this.get() so I put in an aggregator: only a single observable is created for multiple requests of the same key.
protected observableCache : {[key: string] : Observable<any>} = {};
get<T>(key : string): Observable<T> { /* Async data retrieval */ }
getOrCreate<T>(key : string, fetcher: () => Observable<T>) : Observable<T> {
const keyHash = this.hash(key);
// Check if an observable for the same key is already in flight
if (this.observableCache[keyHash]) {
return this.observableCache[keyHash];
} else {
let observable : Observable<T>;
this.get(key).subscribe(
// Cache hit
(result) => { observable = Observable.of(result); },
// Cache miss. Retrieving from fetching while creating entry
() => {
fetcher().subscribe((fetchedResult) => {
if(fetchedResult) {
this.put(key, fetchedResult);
}
observable = Observable.of(fetchedResult);
});
}
);
// Register and unregister in-flight observables
this.observableCache[keyHash] = observable;
observable.subscribe(()=> {
delete this.observableCache[this.hash(key)];
});
return observable;
}
}
This is my current version of that code but it doesn't look like I'm properly handling async code:
Observable will be returned before it's instantiated: return observable fires before observable = Observable.of(result);
There's probably a much better pattern of aggregating all requests for the same key while this.get() is still in-flight.
Can someone help with finding the Observer patterns should be used?
I think this might work. Rewritten as:
getOrCreate<T>(key : string, fetcher: () => Observable<T>) : Observable<T> {
const keyHash = this.hash(key);
// Check if an observable for the same key is already in flight
if (this.observableCache[keyHash]) {
return this.observableCache[keyHash];
}
let observable : ConnectableObservable<T> = this.get(key)
.catch(() => { // Catch is for when the source observable throws error: It replaces it with the new Rx.Observable that is returned by it's method
// Cache miss. Retrieving from fetching while creating entry
return this.fetchFromFetcher(key, fetcher);
})
.publish();
// Register and unregister in-flight observables
this.observableCache[keyHash] = observable;
observable.subscribe(()=> {
delete this.observableCache[keyHash];
});
observable.connect();
return observable;
},
fetchFromFetcher(key : string, fetcher: () => Observable<T>) : Observable<T> {
// Here we create a stream that subscribes to fetcher to use `this.put(...)`, returning the original value when done
return Rx.Observable.create(observer => {
fetcher().subscribe(fetchedResult => {
this.put(key, fetchedResult);
observer.next(fetchedResult);
},
err => observer.error(err),
() => observer.complete())
});
}
Explanations:
Observables are very different from promises. They are to work with async stuff, and there are some similarities, but they are quite different
As this.get(...) seems asynchronous, your let observable won't get filled until it yields a value, so when you assign it to your cache it's normal that's null.
A great thing about observables (and the main difference with promises) is that you can define a stream before anything gets executed. In my solution, nothing gets called until I call observable.connect(). This avoids so many .subscriptions
So, in my code I get the this.get(key) stream, and tell it that if it fails (.catch(...)) it must fetch the result, but once that's fetched then put it into your cache (this.put(key, fetchedResult)
Then I publish() this observable: This makes it so it behaves more like promises do, it makes it "hot". This means that all subscribers will get the values from the same stream, instead of creating a new stream that starts from 0 everytime one subscribes to it.
Then I store it in the observable pool, and set to delete it when it finishes.
Finally, I .connect(). This is only done if you publish() it, it's the thing that actually subscribes to the original stream, executing everything you want.
To make it clear, because this is a common error coming from Promises, in angular if you define a stream as:
let myRequest = this.http.get("http://www.example.com/")
.map((result) => result.json());
The request it's not sent yet. And everytime you do myRequest.subscribe(), a new request to the server is made, it won't reuse the "first subscription" result. That's why .publish() is very useful: It makes that when you call .connect() it creates a subscription that triggers the request, and will share the last result received (Observables support streams: Many results) with all incoming subscriptions to the published observable.
I have this piece of code where I listen for newly added items to a collection. When an item is received I want to update it immediately and mark it as received. The initial status is queued.
var invites = Invites.find( {} );
// Watch for new invites
invites.observeChanges( {
added : function ( id, invite ) {
Invites.update( invite._id, {
$set: {
status: 'received'
}
}, function ( error, affected_rows ) {
console.log( error, affected_rows );// prints undefined
} );
}
} );
The problem is that the status never changes. If I refresh the page the status will switch to received so it somehow remains stuck in the process.
I even tried doing the update server side by calling a remote method without any luck.
Btw, this is a mobile app using meteor, angular, ionic.
I'll answer my own. You need to call setTimeout before sending method calls or updates from with the observe block. See this issue for a discussion:
https://github.com/meteor/meteor/issues/907
I have a collection of users (model user)
model has a boolean value: isExport
i have a button that on click supposed to post to the server all the users that isExport=true
Can anyone suggest a suitable solution for this problem?
I know it's possible to wrap the collection as a model and overwrite the toJSON function
but couldn't manage it so far (can someone please give a code example?)
App.User = Backbone.Model.extend({ defaults: {isExport: false}...});
App.Users = Backbone.Collections.extend({model: App.User...});
Thanks!
Roy
Backbone collections by default don't have any write operations to the server, so you'll need to add a new method to your Collection subclass such as doExport, use .where to get the models with isExport=true and their toJSON() to get an array of objects which you can then send to the server with Backbone.sync or $.post.
Backbone comes with RESTful support.
So, if you give each collection a url pointing to the collection rest service, then with a few functions (create,save) you can handle server requests.
App.Models.User = Backbone.Model.extend();
App.Collections.Users = Backbone.Collection.extend({
url: 'users',
model: App.Models.User
});
So, this way, you can:
var users = new App.Collections.Users();
users.fetch(); // This will call yoursite.com/users
// Expecting a json request
And then you can:
users.create({ name: 'John', isExport: true });
This will send a post request to the server, in order to create a new record.
And you can check on server side if it has the flag you want.
App.Views.ExportAll = Backbone.View.extend({
el: '#exportAll',
events: {
'click': 'exportAll'
},
exportAll: function(e){
e.preventDefault();
console.log('exporting all');
console.log(this.collection.toJSON());
var exportModel = new App.Models.Export;
exportModel.set("data", this.collection.toJSON());
console.log(exportModel.toJSON());
exportModel.save();
}
});
I think this is the best solution for the problem
For creating a new model I need to pass just one attribute:
model.save({
name: 'bar'
});
Then the server will add extra attributes like the id for this model.
Actually the response of the server when I create the new model is
this request has no response data available.
What is the best way to get the extra attributes created by the server?
Should I fix the sever part?
Here's my code:
var ListView = Marionette.CompositeView.extend({
// some code
events: {
'click #create-user': 'createUser'
},
createUser: function (e) {
this.collection.create(this.newAttributes(e));
},
newAttributes: function (e) {
return {
name: $(e.currentTarget).val(),
};
}
appendHtml: function (collectionView, itemView) {
collectionView.$el.find('ul.list-users').append(itemView.el);
}
});
This question is not related to Marionette, which only extends views, not models.
Backbone will automatically incorporate any new attributes sent back from the server - there is nothing special that needs to be done on the client side. But for this to work, your server needs to return a JSON object of attribute-value pairs that you want to be set.