What can be done about ember actions that may be fired after the user has transitioned to a new route? - ember.js

I am having a problem with route-level actions that occur asynchronously occasionally not getting handled.
In my Ember app I sometimes receive the error:
Nothing handled the action 'X'. If you did handle the action, this error
can be caused by returning true from an action handler in a controller,
causing the action to bubble.
Where X is the name of some action. The problem is not that the action is bubbling or that the handler is not defined, because it works almost all the time.
The problem, rather, relates to to fact the this.send('X') is called in a promise callback, e.g., after saving a model:
model.save().then(() => {
this.send('X');
});
If the save encounters some network congestion or a delay on my server then the user might have navigated to another route by the time the promise is resolved. And that route might not have an action X defined (or worse perhaps it's the wrong action of the same name).
Is using actions here a fundamental design flaw?
If so, this would seem to severely limit the utility of actions, because they couldn't be used in any asynchronous context.
Or is there some way that transitions out of a route can delayed until any pending async stuff is resolved? (though this might create it's own undesirable symptoms)

IMHO there is not a design flaw in the case you have described. This is the nature of the promises. The same thing can happen within components and there might occur some errors related with trying to set properties on destroyed Ember components. There is a great addon I use to handle such cases: Ember Concurrency
If you take a look at this addon; you will see that there are similar cases explained, which are very similar to the one you have described. I would suggest benefiting from a popular, already heavily used library like this one instead of trying to tackle it by your own. My best regards.

For the simplicity of coding, I would suggest to move such actions to Application Controller and inject it wherever needed.
Application Controller can be injected as
import Ember from 'ember';
export default Ember.Controller.extend({
appController: Ember.inject.controller('application')
});
To use it in Template:
<button {{action 'X' target=appController}}> Login </button>
To invoke it from your code
this.get('appController').send('X');
You can also use Service to achieve this.

You can use isDestroyed guard check before calling send. But I would encourage you to use ember-concurrency addon for this use case. If you can go through How to do you check for isDestroyed? discussion forum, you will get the idea.

Related

unit testing in redux

So the docs suggest using a mock store, but it's just recording all the actions and is not connected to any reducer. I basically just want to unit a test a component, and see that given an action has been dispatched, it changed- something like(in the most general way to describe):
expect(counter.props).to.equal(1)
dispatch(increment())
expect(counter.props).to.equal(2)
any ideas? thanks
There's a couple factors involved here.
First, even in normal rendering and usage, dispatching an action does not immediately update a component's props. The wrapper components generated by connect are immediately notified after the action is dispatched, but the actual re-rendering of the wrapped component generally gets batched up and queued by React. So, dispatching an action on one line will not be reflected in the props on the next line.
Second, ideally the "plain" component shouldn't actually know anything about Redux itself. It just knows that it's getting some data as props, and when an event like a button click occurs, calls some function it was given as a prop. So, testing the component should be independent from testing anything Redux-related.
If it helps, I have a whole bunch of articles on React and Redux-related testing as part of my React/Redux links list. Some of those articles might help give you some ideas.

Can an ember(-cli) app trigger window.onerror?

Is there any kind of javascript error that is not handled by onerror in an ember(-cli) application? In other terms, is there any need to add a handler to window.onerror?
In my tests I could not come up with any error that would no be handled by Ember's onerror. Syntax errors didn't get past ember-cli's build process.
EmberJS does an excellent job of containing errors that happen within its boundaries. Errors caused by your Ember code should all get funneled into the Ember-specific handler.
However, browser JavaScript is a complicated beast. Any JavaScript errors that happen outside of Ember would not be captured internally and may only be exposed by the global window.onerror. Some examples of these could include:
Unbounded callbacks into native functions like setTimeout and addEventListener
Non-ember JavaScript libraries and integrations, such as Stripe, Twitter, or Advertisements
Browser-extension script that can interact with the DOM in unpredictable ways.
To get a complete picture of what is happening with your visitors, you should probably attach to both Ember.onerror and window.onerror and send the reports back to your logs. Even if you are not actively developing, browser changes can cause sudden bugs to appear in your application. Like this webkit bug that caused ember to throw errors. There are good options to record errors automatically from Ember apps, like TrackJS.
Disclosure: I am one of the founding developers of TrackJS, so I've debugged a lot of errors :)
In my experience, I would not advise implementing a window.onerror, unless you have a strong reason to do so and you know what you may be doing.
To my knowledge all of Ember's onerror methods piggy-back on to window.onerror and overriding it has potential to override/disable all the error in your app.
Case in point, I had an experience recently where i noticed that all my js errors stopped logging to console altogether. The reason was that another developer in my team had bootstrapped into my index.html to inject a <script> tag which utilized window.onerror for some error reporting script. The result was that overriding window litterally disabled all of ember's onerror becuase they work in a bubbling fashion.
I think if you do want to override window.onerror you need to ensure that it always returns false so that it does not prevent default error handling on the browser.
See: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror
I hope that helps clarify to some degree.
Ember has a built in error event you can use in your routes
actions: {
error(error, transition) {
if (error && error.status === 400) {
//handle error
}
}
}
More Info: https://guides.emberjs.com/v2.0.0/routing/loading-and-error-substates/

Ember.js Acceptance Testing not waiting for asynchronous data operations

When using the Emberfire (Firebase) adapter, I'm getting various errors that indicate the tests are not waiting for data operations to complete.
eg: Error: Assertion Failed: You can only unload a record which is not inFlight. when I try to create, check, and then delete a record
Also:
FIREBASE WARNING: Exception was thrown by user callback. Error: Called stop() outside of a test context at Object.extend.stop (http://localhost:4200/assets/test-support.js:3000:10) at exports.default._emberTestingAdaptersAdapter.default.extend.asyncStart
These errors do not occur while manually navigating through my app, nor when using the standard ember data adapter.
What is causing these errors and how do I avoid them?
Edit: although the symptoms are a bit different (no error thrown), it sounds like this problem may have the same root cause as the errors I've been seeing.
tl;dr
To work around the issue, I've been using a custom test waiter. You can install it with ember install ember-cli-test-model-waiter (works with Ember v2.0+)
Longer answer:
The root cause of these problems is that the ember testing system doesn't know how to handle Firebase's asynchronicity. With most adapters, this isn't a problem, because the testing system instruments AJAX calls and ensures they have completed before proceeding, but this doesn't work with Firebase's websockets communication.
So although these errors don't occur when interacting manually, I believe they technically could if it were possible to click fast enough.
These problems are known to occur with ember-pouch and will likely also occur with other non-AJAX adapters (eg. localstorage adapters (1, 2), or any other websockets-based adapter. It may occur with the fixture adapter, but that may return results immediately and so not trigger this problem). It also happens with other asynchronous processes, like liquid-fire animations (fixed in a similar way)
The custom test waiter I mentioned in the tl;dr works by waiting for all models to resolve before proceeding with the test, and so should work with all of these non-AJAX adapters.
For more background on how ember's testing deals with asynchronicity under the hood, Cory Forsyth has a helpful blog post, and this gist gives another more flexible solution approach but that requires more manual book-keeping.

Updating a controller via polling a REST call after it is initialized with Ember's connectOutlet

Just digging into Ember JS and read through a great guide at http://trek.github.com/ regarding the use of the routers and structuring your ember app. The framework is great and has come a long way based on some of the early tutorials.
My question is, does any one have any strong opinions/good examples/best practices for updating a instance of a controller that was initialized by the router and the connectOutlet method:
connectOutlets: function(router,context){
router.get('applicationController').connectOutlet(
'attlist',
'smartSensor',
App.SmartSensor.getSensorData(context)
);
}
The SmartSensor model's getSensorData() function calls out to a back-end REST API. I would like to then periodically poll the server to see if the attributes of the controller have been updated and if needed update the controller. I can think of a few ways I could force it, but nothing that seems clean. Any ideas or am I overthinking it?
Thanks!
I would either:
a) Create a new poller object and give it a reference to the smartSensorController. The router would call startPolling() on the poller in your connectOutlets method, and call stopPolling() when leaving the state where it is needed (assuming it ever becomes unneeded in your app)
...or...
b) Let the smartSensorController take responsibility for polling. After your call to connectOutlet, do router.get('smartSensorController').startPolling() and a corresponding stopPolling() at the appropriate spot in the router.
Hope that helps!

Ember.js: Where is the "start" button?

I'm used to thinking about a single-page application startup happening like this: 1. Bootstrap some data into critical models, 2. Instantiate a master controller, and 3. Call it's render() method to kick things off.
How is this accomplished with Ember? Following the (meager, sigh) examples in the documentation, it seems like things sort of kick off on their own when the page loads -- templates are compiled, views render like magic when the page loads. I feel like I am missing something fundamental. It there an example online of a more complex app, say something with tabbed or dynamically loaded views?
Lightbulb, going off it is not.
I've started a blog series about getting up and running with Ember on Rails. Here's Part 1:
http://www.cerebris.com/blog/2012/01/24/beginning-ember-js-on-rails-part-1/
I hope you'll find it useful, even if you're not planning to use Ember with Rails. Most of the interesting details are client-side and thus server-independent. The posts so far cover creating an Ember.Application object, loading data dynamically through a REST interface, and then rendering an Ember view on a page in handlebars. I hope it's enough to get you started.
When you extend an ember Application object you can provide a ready function which will be called when the application starts. You have to make sure to call this._super() or else it will break your application. Check out my sample sproucore 2.0 application (ember is the new name of sproutcore 2.0).
The way that ember works is that it sets up a run loop which responds to events. Whenever an event fires, the run loop basically calls the necessary handlers and runs any bindings that need to be updated. Since everything typically happens in the run loop you often don't really write any code to update things. Instead you write bindings which are fired when needed.
Another thing I've done is use an Em.StateManager to bootstrap.
App.Loader = Em.StateManager.create({
start: Em.State.create({
enter: function(mgmt, ctx) {
// this code will execute right away, automatically
}
})
});
Since you use create instead of extend, the object will be instantiated immediately. If you define a state called start, it will be recognized as the default initial state (or you can specify another one by name). So the new StateManager object will immediately enter the initial state, and when the StateManager enters a new state, it will always look for a method of that state called enter and fire it if present.
A state manager is the natural place to initialize your app because the object provides ways for you to micromanage execution order during an async loading process without entangling yourself in too many callbacks.