Using an unclosed element for your ng-view in Angular

Last week I had one of those problems that make absolutely no sense. The sort of problem that breaks down everything you think you know about yourself and your ability. "Am I really any good? Do I even know what I'm doing any more? How could this even be?" Up was down, left was get it.

It all started out pretty innocuously. A simple SPA, served from an nginx server, and a django instance for the API with CORS enabled. I've done this more times than I can count. With time running out on this sprint I was hoping I could wrap up the set up and have a few welcome screens ready. But, as I've learned through the years, things never go as planned.

While nginx and django were being set up, I worked with a temporary node server to get the files served. I set up all of the front end using bower, and utilized wiredep to manage all those assets and write them into the default index.html page. That was all working fine—even the Gruntfile we've made that pushes the limits of what Grunt is supposed to do. Everything was operating perfectly. THEN we switched to nginx to serve the SPA and all hell broke loose.

The page would load and pull down all of the JS assets. Angular would fire off requests for all of my templates and load them into my view. That was fine. Then I noticed some errors. I opened up my console and found they appeared to be caused by attempting to re-initialize scripts. "WARNING: Tried to load angular more than once," Angular complained. "Uncaught TypeError: Cannot redefine property: chunk," barfed another error. Ok, it looks like it's calling the scripts again. No big deal, I thought.

I looked at the network tab and found that I was right. But it wasn't all of the page just doing a refresh and everything being reloaded: only the javascript files were being re-requested.

 Then I noticed the line that was re-requesting those scripts:

And here was where I made my fatal mistake.

I started digging into the scripts, trying to figure out why it worked when they were being served from node but not nginx. Was it CORS? It had to be; it was one of the few differences between the first set up and the second set up. I dug into line 8625 of jQuery:

// Do send the request (this may raise an exception)
    xhr.send( options.hasContent && || null );

This appeared in the function ajaxTransport, which appeared to only happen when CORS was enabled. From line 8549:

    // Cross domain only allowed if supported through XMLHttpRequest
    if ( support.cors || xhrSupported && !options.crossDomain ) {

"Ohhh, ok...I think I know what's happening," I foolishly thought. Because CORS is enabled on the server, jQuery is somehow being tricked into thinking that everything needs to be requested in a cross-domain way. Somehow it is confused and attempting to retrieve the scripts through CORS. Yes, this must be what it was.

I looked at that if statement. "Wait a second," I thought, "if CORS is supported, OR xhr is supported and it is not a cross domain request, then try to do these requests?" That doesn't make sense. Why does it say "!options.crossDomain"? Don't they mean "if it is a cross domain request then attempt to make these calls?"

I doubled down. I changed the line to:

if ( support.cors || xhrSupported && options.crossDomain ) {

and everything worked. BLAM. I found a bug in jQuery. I kept reading through the rest of their code, and none of it made sense...why add preflight requests if it is not a cross domain request?! That doesn't make sense. I foolishly opened a ticket, oblivious to my glaring, obvious mistake. One that I've learned time and time again in this business.

My mistake was my approach. I had believed that the issue was solely due to scripts, and because of that, I only checked the scripts. It was only days—literally days—later that I tried another approach: checking the HTML.


The culprit: 

<main class="container" ng-view>

Adding a closing tag to that element fixed the problem. Whoops.

As with everything in life, things pop up that defy explanation. Typically, following your tried and true methods for solving problems will help you find a solution. But every once in a while you lose perspective—maybe it's from being sick and off your mojo; maybe it's because your wife has an illness that stumps medical professionals from Harvard to Yale. When you have that little nagging voice in the back of your head, you're never able to truly focus on the task at hand.

In buddhism they always remind you to come back to your breath. That, they say, is the only true thing you can control, and the starting point from which you can solve all of your problems. Through your practice of being still and focusing on your breath, problems like these will be solved—but only if you can remember to do it. It's easy to say, but tough to do. And the simple truth of it is that only through constant repetition of any task will it become second nature. Sure, I am quick to pat myself on the back about the 20 minute meditation I put in a day. But when you're confronted with situations like these, the reality hits you that you really, truly, have so much further to go

Posted on April 3, 2015 .