My standard workflow for debugging
I'll admit that I have a lot of experience with things not going well in Ember.
Over time I've started to get a general workflow in place that I'd like to share.
When I have to debug it's commonly performed from two points of view. Either the page didn't render at all due to a javascript error,
or the page rendered, just not in the way that I was expecting. From there I find myself following the flow in the chart below.
<sodipodi:namedview pagecolor="#ffffff" bordercolor="#666666" borderopacity="1" objecttolerance="10" gridtolerance="10" guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-width="2462" inkscape:window-height="1328" id="namedview4244" showgrid="false" inkscape:zoom="1.1972936" inkscape:cx="387.8658" inkscape:cy="1045.9024" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="0" inkscape:current-layer="g3832" inkscape:snap-global="true" inkscape:snap-midpoints="true" inkscape:snap-bbox="true" inkscape:object-nodes="true" inkscape:bbox-nodes="true"/>
Produced by OmniGraffle 6.5.2 <dc:date>2016-07-29 19:32:49 +0000</dc:date>
<rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/><dc:title/></cc:Work></rdf:RDF>
Canvas 1
Layer 1
<xlink:use xlink:href="#id1_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id168_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id19_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id23_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id163_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id47_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id59_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id3_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id25_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id31_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id68_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id34_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id96_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id98_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id110_Graphic" filter="url(#Shadow)"/>
<xlink:use xlink:href="#id173_Graphic" filter="url(#Shadow)"/>
I don’t see
what I was
expecting
There was a
javascript
error
Maybe the routes aren’t running
correctly
.
LOG_TRANSITIONS_INTERNAL: true
Check the rendered state of the
template using the inspector
W
as there a javascript
error?
Not really
Y
es
Did the page render
at all?
- Reload and see if the transition to the
page fi
nished successfully.
If a
transition failed go debug from there
- Make sure any ajax requests
completed successfully
Are the values in the template/
components what you expected?
Obvs No
How is that particular value
supposed to be set?
- Check dependent keys
- Set a breakpoint and check if the observer
is fi
ring as intended
- Make sure you’ve added .on(‘init’) if
needed
Observer
- Check dependent keys
- Make sure the property isn’t getting
overwritten
Computed Property
- Set a breakpoint and make sure the
helper is recomputing as needed
Helper
- Make sure the value isn’t getting set from
somewhere else
-
T
emporarily add an observer that watches
the value and set a breakpoint there
Binding
- Set a breakpoint and check that the action
is fi
ring as intended
- For closure actions make sure params are
curried correctly
Action
- Make sure the hook is fi
ring, especially if the
problem is occurring after a value change or re-
render
- If the rerender is from a route transition, ember
might reuse an instance of a given component and
not call the Init() hook every time.
- Make sure to call this._super(…arguments)
Component Lifecycle Hook
Does the CP
depend on something async?
- DS.hasMany(‘foo’, {async: true})
- PromiseProxy
, PromiseArray
, etc.
Make sure the CP
is still valid when the async
dependency is in an unfulfi
lled state
-If there’
s a consistent problem in a runloop use
Ember.run.backburner.DEBUG = true
- If using chrome make sure the ‘async’
check box on the inspector is enabled
- If the browser stack trace isn’t useful
take a look the error
’
s ‘stack’
property
- Check server responses
- Set a breakpoint and check if the observer
is fi
ring as intended
- Make sure you’ve added .on(‘init’) if
needed
Ember Data Model
A small companion guide
-
Start at the template. The template is the source of truth for what goes on the screen, and Ember's handy inspector makes it
quick to confirm the state of all those bound values. If the page rendered incorrectly it means a bound value somewhere isn't what I was expecting.
From there I go to each incorrect value and try to determine how it was set, essentially recursing back through the flowchart. -
Blank screens and no error? Start logging. In some apps the way I've (poorly) set up my routes and their error handling will lead to a route swallowing
an error during a transition. If I come across a blank page with no error I'll normally setLOG_TRANSITIONS_INTERNAL: true
like the debugging guide
demonstrates. Then tracking down the failed route hook is a snap. -
Check computed property dependent keys I've shot myself in the foot countless times from typos or plain mistakes in computed property keys. If I write a property whose keys don't
correspond to theget()
calls inside, I try to write a comment saying why. -
I <3 developer tools I couldn't get through my working day if I didn't have the Chrome developer tools. Half of the items on my flowchart involve setting a breakpoint somewhere
in my app's source code or in the ember source code. I often find myself setting a breakpoint at an obvious place, then navigating up the stack and setting more breakpoints closer to
what I think is the source of the problem. -
Use error.stack When the browser pauses on an Ember error, there are many cases where the browser stack trace at that moment won't be very useful. In that case, I check the thrown
error'sstack
property. It often has a more informative list of places for me to check. -
Debug two-way bindings with observers Sometimes I get plain stuck trying to figure out how a value is being changed in a component. Observers to the rescue! Here's a quick example:
Ember.Component.extend({
heisenValue: "foo",
// If heisenValue changes for any reason, the observer will fire synchronously
// and show me where the change is coming from.
forDebug: Ember.observer('heisenValue', function() {
debugger;
})
});
- Check the server response If my Ember Data models aren't showing the right information I'll always take a quick look at the network response
to make sure the data is what I'd expect before diving in further on the client side. It's saved me a ton of time, especially when working on a new feature.
Take heart!
Debugging can be frustrating, but I've found having a consistent set of steps has made finding run of the mill problems much less taxing.