In my app, a user can create a message and send it. When the user sends the message, the message is created with createRecord and the server replies with 201 Created if successful.
Also, the user can get messages from other users through a websocket. When it receives a message, I push it into the store with pushPayload.
var parsedData = JSON.parse(data);
this.store.pushPayload('message', parsedData);
The problem is, when a user sends a message and saves it, they also get it back from the websocket, and even though both objects have the same id, the store ends up with duplicate messages.
How can I tell the store than when I push or save something with the same id of an already existing element, it should override it?
Simply perform a check to see whether the model is already in the store before adding it:
var parsedData = JSON.parse(data);
if(this.store.hasRecordForId ('typeOfYourRecord', parsedData.id)){
// logic you want to run when the model is already in the store
var existingItem = this.store.find('typeOfYourRecord', parsedData.id);
// perform updates using returned data here
} else {
this.store.pushPayload('message', parsedData);
}
The only method I found to avoid this problem is to run my update in a new runloop. If the delay in ms in long enough, the problem won't occur.
It seems that receiving the update from the websocket and the request at nearly the same time creates a race condition in Ember Data.
Related
So I am trying to replicate the Google Docs functionality wherein every time you edit, the document will be saved. Will I be putting an onchange function on every input in the form then sending the data through ajax? How should I approach this or is this even feasible?
Note: I am just asking for some sort of pseudocode or simply the flow-chart of how I should do things.
I think that something like this should works:
jQuery(function($){
function changeFn(){
// make ajax save.
}
var timer;
$("#doc").bind("keyup", function(){
clearTimeout(timer);
timer = setTimeout(changeFn, 2000)
});
});
This will wait 2 seconds(you can try with 3 - 5 seconds) after the user press the last key, then will call the ajax function to save the content. I think that is better than save all the time when the user press a key. Note that if the user press a key before that time, the timeout will be interrupted and will initiate a new count.
I have a list of items handling by Ember.ArrayController. I'm doing some PATCH action on records, which updates existing items and adding a new ones, if it is needed from the context. All changes I'm sending back from the server and I'm pushing it to the store by using Store.pushPayload() method.
I do something like that:
All changes in existing records are automatically updated - so observers of particular items are run.
Unfortunately when I have a new items in payload - they do not appear on the list - observes of ArrayController.content are not called.
I also tried to manually notify ArrayController about the changes by doing:
_this.store.pushPayload(response);
var tasksController = _this.get('controllers.tasks');
tasksController.contentDidChangedManually();
And in controller:
contentDidChangedManually: function() {
this.set('contentChangedManually', new Date().getTime());
},
filteredContent: function() { // my content filters... }.property('arrangedContent', 'contentChangedManually')
But it does not work, because contentDidChangedManually() is run before pushing a payload is done. Unfortunately, Store.pushPayload() does not return a promise, so I can't run it when new records are ready.
Does anyone have a solution for this? Thanks in advance.
It sounds as if you are using find to load your model data, you likely want to use DS.Store.filter instead as that returns a live updating record array:
http://emberjs.com/api/data/classes/DS.Store.html#method_filter
I need to make custom validation of instance before saving it to MySQL DB.
So I perform (async) check inside beforeValidate model hook.
MyModel.beforeValidate = function(next){
// async check that finally calls next() or next(new Error('fail'))
}
But when check fails and I pass Error obj to next function, the execution continues anyway.
Is there any way to stop execution and response to client with error?
This is a known bug in the framework, see https://github.com/strongloop/loopback/issues/614
I am working on a new hook implementation that will not have issues like the one you have experienced, see loopback-datasource-juggler#367 and the pull request loopback-datasource-juggler#403
I'm submitting an object to our API via a POST and then transitioning to a route that displays that object. The API modifies one or more fields in the object in the POST and returns the updated info in the request response.
The data displayed is the original data from before the POST to our API. I can see from the console that ember-data is receiving back the updated information from our API. How can I force ember to "refresh" the object so that it displays the correct info?
Matt. Yehuda Katz posted a reply to a user which provides this functionality:
https://stackoverflow.com/a/14183507/506230
Basically you create a record, apply it, save it, then reload it.
saveMessage: function(text){
var acct = Social.Account.find(this.get("id")),
msg = Social.store.createRecord(
Social.Message,
{
text: text,
account: acct,
created: new Date()
}
);
acct.get("messages").addObject(msg);
Social.store.commit();
var timeoutID = window.setTimeout(function(){
__msg.reload();__
console.log('reloading');
}, 250);
}
It turns out ember was actually behaving properly and no additional work was necessary. The problem is that I was setting a variable on the controller with the same name as a computed property on my model. The value of the variable on the controller was being displayed rather than the computed property. Changing the name of the controller variable resolved the issue without any additional code.
I have a list of clients displayed through a ClientsController, its content is set to the Client.find() i.e. a RecordArray. User creates a new client through a ClientController whose content is set to Client.createRecord() in the route handler.
All works fine, however, while the user fills up the client's creation form, the clients list gets updated with the new client record, the one created in the route handler.
What's the best way to make RecordArray/Store only aware of the new record until the record is saved ?
UPDATE:
I ended up filtering the list based on the object status
{{#unless item.isNew}} Display the list {{/unless}}
UPDATE - 2
Here's an alternative way using filter, however the store has to be loaded first through the find method, App.Client.find().filter() doesn't seem to behave the way the two methods behave when called separately.
// Load the store first
App.Client.find();
var clients = App.Client.filter(function(client){
console.info(client.get('name') + ' ' + client.get('isNew'));
return !client.get('isNew');
});
controller.set('content',clients);
Few ways to go about this:
First, it's very messy for a route/state that deals with a list of clients to have to go out of its way to filter out junk left over from another unrelated state (i.e. the newClient state). I think it'd be way better for you to delete the junk record before leaving the newClient state, a la
if(client.get("isNew")) {
client.deleteRecord();
}
This will make sure it doesn't creep into the clientIndex route, or any other client list route that shouldn't have to put in extra work to filter out junk records. This code would ideally sit in the exit function of your newClient route so it can delete the record before the router transitions to another state that'll called Client.find()
But there's an even better, idiomatic solution: https://gist.github.com/4512271
(not sure which version of the router you're using but this is applicable to both)
The solution is to use transactions: instead of calling createRecord() directly on Client, call createRecord() on the transaction, so that the new client record is associated with that transaction, and then all you need to do is call transaction.rollback() in exit -- you don't even need to call isNew on anything, if the client record was saved, it obviously won't be rolled back.
This is also a useful pattern for editing records: 1) create a transaction on enter state and add the record to it, e.g.
enter: function(router, client) {
this.tx = router.get("store").transaction();
this.tx.add(client);
},
then the same sort of thing on the exit state:
exit: function(router, client) {
this.tx.rollback();
},
This way, if the user completes the form and submits to the server, rollback will correctly/conveniently do nothing. And if the user edits some of the form fields but then backs out halfway through, your exit callback will revert the unsaved changes, so that you don't end up with some dirty zombie client popping up in your clientIndex routes display it's unsaved changes.
Not 100% sure, could you try to set the content of ClientsController with
Client.filter(function(client){
return !client.get('isNew'));
});
EDIT: In order to make this work, you have to first load the store with Client.find().