I am creating a new ember app. I want to use the newest version of ember-data. (ember-data 2.0). I want it to be a mobile webapp. Therefore it must handle variable network access and even offline.
I want it to store all data locally and use that data when it goes offline so the user gets the same experience regardless of the network connectivity.
Is ember-data 2.0 capable of handling the offline case? Do I just make an adapter that detects offline/online and then do....?
Or do I have to make my own in-between layer to hide the offline handling from ember-data?
Are there any libraries out there that has this problem solved? I have found some, but are there any that is up to date with the latest version of ember-data?
If device will go offline and user will try to transition to route, for which model is not loaded yet, you will have an error. You need to handle these situations yourself. For example, you may create a nice page with error message and a refresh button. To do this, you need:
First, In application route, create error action (it will catch errors during model hook), and when error occurs, save transition in memory. Do not try to use local storage for this task, it will save only properties, while we need an actual transition object. Use either window.failedTransition or inject in controllers and routes a simple object, which will contain a failed transition.
actions: {
error: function (error, transition) {
transition.abort();
/**
* You need to correct this line, as you don't have memoryStorage
* injected. Use window.failedTransition, or create a simple
* storage, Iy's up to you.
*/
this.get('memoryStorage').set('failedTransition', transition);
return true; //This line is important, or the whole idea will not work
}
}
Second, Create an error controller and template. In error controller define an action, retry:
actions: {
retry: function () {
/**
* Correct this line too
*/
var transition = this.get('memoryStorage').getAndRemove('failedTransition');
if (transition !== undefined) {
transition.retry();
}
}
}
Finally, In error template display a status and an error text (if any available) and a button with that action to retry a transition.
This is a simple solution for simple case (device gone offline just for few seconds), maybe you will need something way more complex. If you want your application to fully work without a network access, than you may want to use local storage (there is an addon https://github.com/funkensturm/ember-local-storage) for all data and sync it with server from time to time (i.e sync data every 10 sec in background). Unfortunately I didn't try such things, but I think it is possible.
Related
I'm building an ember app, and I keep running into the same problem where I make a call to the store, but the function keeps compiling before the store has retrieved the data from the backend. Specifically I'm having the problem with a findRecord. I've implemented it both ways:
var admin = this.store.findRecord('admin', 1);
console.log(admin.get('season'));
console.log('Are we here?');
and
this.store.findRecord('admin', 1).then(function(admin) {
console.log(admin.get('season'));
});
console.log('Are we here?');
In both cases, the Are we here? is logged BEFORE the season. Obviously the console logs are just for the example, and it creates an actual problem with what I'm trying to get done. Does anybody know a simple fix for this delay?
Thanks.
Of course it is. It's an asynchronous behavior. It takes some time to solve promise which is returned from findRecord(), thus the consequence is:
this.store.findRecord(); //synchronous
console.log('we are here'); //synchronous
in the meantime a promise returned from findRecord() gets resolved (asynchronous behavior)
console.log(admin.get('season'));
An asynchronous call will not stop your code from progressing, that´s the purpose of it. Else it would block UI updates and user interaction while loading data.
My backend always responds with all available data and it took a considerably amount of time. So I'm reloading store periodically and I plan to use peekAll() and peekRecord().
My code is:
model: function() {
return Ember.RSVP.hash({
'clusters': this.store.peekAll('cluster'),
'single': this.store.peekRecord('cluster', 'cluster::My')
});
When code is executed, at first I can see that both of these items do not contain content. After few seconds data are loaded to store and I can see content 'clusters' on template as expected. But 'single' is still completely without content ({{model.single}} does not return nothing in template). But when I have a button with action:
alert(this.store.peekRecord('cluster', 'cluster::My'));
I can see that the record was found. Records are also available via Ember Inspector. What am I doing wrong that only peekAll() works in model for me.
The semantics of both methods are:
store.peekAll returns a live array that is updated as the store is updated.
store.peekRecord returns the corresponding object in the current cache, or null, and it does not update.
So the behaviour you're observing is the expected one. If you want to use the peek methods, my advise is to make sure that the initial request has finished loading before fetching any data from the store.
I am looking into how to show proper deletion error message in ember when there is an error coming back from the server. I looked at this topic and followed its suggestion:
Ember Data delete fails, how to rollback
My code is just like it, I return a 400 and my catch fires and logs, but nothing happens, when I pause it in the debugger though and try to rollback, I get Error: Assertion Failed: calling set on destroyed object
So A) I cannot rollback B) the error is eaten normally.
Here is my code
visitor.destroyRecord().then(function() {
console.log('SUCCESS');
}).catch(function(response) {
console.log('failed to remove', response);
visitor.rollback();
});
In case it's relevant, my model does have multiple relationships. What am I doing wrong? Ember-data version is 1.0.0.8 beta (previous one from the release a few days ago).
Thanks in advance.
EDIT
I discovered now that the record actually is restored currently inside the cache according to ember inspector, but the object will not reappear in the rendering of the visitors. I need some way to force it to reload into the template...
After destroyRecord, the record is gone and the deletion cannot be rolled back. The catch clause will just catch a server error. If you want the record back, and think it's still on the server, you'll have to reload it.
See the following comment on deleteRecord from the Ember Data source:
Marks the record as deleted but does not save it. You must call
`save` afterwards if you want to persist it. You might use this
method if you want to allow the user to still `rollback()` a
delete after it was made.
This implies that a rollback after save is not possible. There is also no sign anywhere I can see in the Ember Data code for somehow reverting a record deletion when the DELETE request fails.
In theory you might be able to muck with the isDeleted flag, or override various internal hooks, but I'd recommend against that unless you really know how things work.
Try reloading the model after the rollback. It will reload from the server but it was the only way around this that I could find.
visitor.destroyRecord().then(function() {
console.log('SUCCESS');
}).catch(function(response) {
console.log('failed to remove', response);
visitor.rollback();
visitor.reload().then(function(vis)
{
console.log('visitor.reload :: ' + JSON.stringify(vis));
});
});
Hope that helps.
We are building some plugins in Microsoft Dynamics CRM by inheriting from IPlugin. We have these configured so they fire whenever an Account is updated.
The problem is the plugins are calling our services, which causes our service to respond with an update. We are doing some pretty hacky things right now to prevent these cyclical updates from happening.
We were wondering if there was a way to pass a value to the IOrganizationService service (the web service) that a plugin can look at. Our other system could send a flag ("hey, don't bothing sending an update!") and the plugin could skip calling back.
Can we pass parameters from web service to the plugins?
Good idea could be usage of custom flag-field. For example you add bit field and call it CallFromExternalSystem. So when you make an update from your external system through IOranizationService you just fill this flag with true field and in plugin you can check condition that this field is present in fields list so you have no need to call external system endpoint again.
We decided the correct solution was to use the value found in IPluginExecutionContext.InputParameters["Target"]. In the case of an Update, this returns an Entity containing attributes for all the attributes that were updated.
We basically have a list of attribute names we cared about. We loop through names and see if any of them appear in the entity attribute list. If so, we send an update to our other system. The good news is, Dynamics CRM ignores updates where the values don't actually change, so trying to update a value to itself is no-op.
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = serviceProvider.GetService(typeof(IPluginExecutionContext));
Entity entity = (Entity)context.InputParameters["Target"];
string[] fields = new string[] { "name", "statecode", "address1_line1" };
bool hasUpdates = fields.Where(f => entity.Attributes.Contains(f)).Any();
if (!hasUpdates)
{
return;
}
}
I have set up some basic routing in my app by using the examples at http://emberjs.com/guides/outlets/#toc_the-router
Within the root I have some events that trigger from view actions e.g:
gotoStepOne: Ember.Route.transitionTo('stepOne'),
gotoStepTwo: Ember.Route.transitionTo('stepTwo'),
gotoStepThree: Ember.Route.transitionTo('stepThree'),
gotoStepFour: Ember.Route.transitionTo('stepFour'),
gotoStepFive: Ember.Route.transitionTo('stepFive'),
Full example router code at http://jsfiddle.net/hellosmithy/WdjXT/
This all works fine at the moment. The problem is that I'd like to add other code into these events. For example:
gotoStepOne: function() {
if (someCondition) {
Ember.Route.transitionTo('stepOne');
}
someOtherFunction();
}
However doing this breaks the routing without throwing any errors. It just no longer transitions.
Specifically I only want transitions to happen if a certain state is met - something has been selected or input by the user at each stage before they can proceed. Is there a workaround for this, or should I be abstracting this functionality elsewhere?
The way I understand the router is, that it is the representation of the application's state.
Specifically I only want transitions to happen if a certain state is met - something has been selected or input by the user at each stage before they can proceed.
So the user inputting or selecting something puts your application in a certain state which is reflected by the router.
IMHO it should be something like this in a view (or controller):
userDidSomething: function(condition) {
if (condition) {
App.get('router').send('stepOne');
}else{
someOtherFunction();
}
}