SPA Day

Picture yourself at a spa.  You’re relaxing in a nice hot tub, letting the worries of the week soak away into the scalding water.  Your phone rings – a flare of stress and responsibility is fired off into the night.  Do you really want to get out and answer that call?

Now, I’ve never been to a spa, but I have been in a couple hot tubs in my day and the idea of popping out into the cold just to answer a call is not appealing.  But, metaphorically speaking, this is exactly what I’d made our Angular application do.  It never complained, but I still feel bad- it wasn’t very thoughtful of me!

Let me explain- the spa in this case is an acronym – Single Page Application.  You may be familiar with this term- it’s an app that basically lives on the client/browser.  The bulk of the app’s structure and logic/functionality is downloaded up front as a bundle of Javascript, then network calls (think AJAX) are used to get data as needed.  A different model from the traditional request-response website, where each page is fully fetched, data and all, from the server.  You trade off a possibly slower initial load time (though that’s becoming less of an issue with tools like the Angular CLI) for a lightning fast app after that first load because you rarely have to go back to the server for more info- it’s all there in your bundle.

So, the metaphor does kind of work.  I’m the application, the hot tub is your browser, and once it’s loaded, why make it leave?  Of course, sometimes it has to make an important call.  If anything is going to be saved to a database or read from a database you’ll have to make that network call.  But the more you can minimize these, the faster your app will be.

On many views of this SPA, we show a grid of data.  That’s a trip to the database- no way out of that one.  There’s also an “edit” link on each row of the grid so a user can update the details contained within.  Initially, we had these going to the database to get the details of that row.  But that’s the old way of thinking!  That trip isn’t necessary to get anything to be displayed on the screen- it’s just to get data that’s technically already available (in the array of objects that’s fetched to populate the main grid).  All we have to do is pass it along to the individual view.

For our Angular application, I initially thought this might be an option on the Router class.  You can pass params to a child route, but that doesn’t really work in this case.  The data needed by the edit screen (child route) can be quite a few properties long, and passing all that in the query string didn’t seem like a great idea.  But this (sharing data between two components) seems like exactly the kind of thing services were made to do!

In the grid component’s template, there is an “edit” button.  When clicked, it fires a method on the component’s class to navigate to the edit component route and originally just passed the id/key of that item.  Now it’s been updated to pass the key and the object itself- the info from that row in the grid.  This object is passed to a service that has some other utility functions on it, but the one we need in this case is super simple:

setCurrentEditObj(obj) {
    this.currentEditObj = obj;
}

The currentEditObj is a class variable that’s just used to hold the object we’re about to edit.  This simple method updates that object any time the “edit” button is clicked on a grid.

In the edit component’s class, we subscribe to the route’s params observable.  This fires off info when the route resolves and gives back any params passed on the route.  In this case, it’s usually just the id/key of whatever is being edited.  In the original version, that id is then used in a getItem method- which just does an http call to the database to get the relevant info.  We inserted a check in that observable:

ngOnInit() {
    this.subscription = this._route.params.subscribe((params: Params) => {
        const currentObj = this.setupService.currentEditObj;
        if(currentObj) {
            this.countryName = currentObj['country_name'];
            this.id = currentObj['country_key'];
            this.setFormFieldDefaults(currentObj);
        } else {
            this.id = params['id'];
            this.getItem(this.id);
        }
    });
}

We wait for the route to resolve and check if the service has a currentEditObj available.  If so, we set the class variables (countryName and id for this view, which just edits a list of countries), and populate the form (setFormFieldDefaults just loops through the object, checks to be sure the object’s property exists on the form, and sets the initial value).  No database trip- our app gets to stay in the warm hot tub that is your browser!

But there’s one check you should do.  You might have noticed the “else” branch in the code above.  The internet isn’t perfect- your connection might be dropped and a full page refresh might be required.  A user might accidentally refresh the screen as well.  If that happens, it can cause issues for a single page application, because the object we are editing is now stored in the code on your browser.  If that browser refreshes, that info is lost.

As an aside – this kind of relates to the notion of offline availability.  Some really cool new techniques are being explored in this area using service workers– these can sit on your browser and respond to requests as if they were a server when a server isn’t available, but will be much faster (as they don’t actually have to travel anywhere) and be available if you lose your connection.  Very cool stuff, but not widely supported at this point.

Instead, we just include a simple check.  If that currentObj doesn’t exist, fall back to the old method and use the id from the url (which will still be available, as it’s stored in the url string) to go to the database and get the necessary info (getItem just does that, then calls the setFormFieldDefaults method).  The only time this should be called is if there’s a refresh or dropped connection, but it works nicely- the data is still available, and the extra cost of a database call isn’t really felt, as the entire application needs to be re-fetched anyway.

Does it save a lot of time?  Probably not- none of the objects we are fetching are huge.  But it is a saved network call, which definitely conforms to the spirit of a single page application.

Should I have thought of this originally?  Probably- it really makes more sense within the spirit of a single page application.  Anywhere you make an HTTP call should be examined and evaluated.  Does this call need to be made?  Do I already have access to this info?  Am I making my app leave the relaxing waters of your browser to make that call?  Let the app relax!

Advertisements

Servercide

So this thing we’re building is a bit of a mash up of different design paradigms.  Not since the X-files was (originally) on the air have we seen such an alien-human hybrid.  It’s mostly a single page app, but some sections just don’t fit (the sitemap and news sections, in particular).  They need their own, separate pages, reached via the traditional request-response route.

Which is which, though?  I’d say the regular, old site method is the ‘human’.  It’s old, probably on its way out, but still holds most of the power across the landscape.  That makes Angular the alien- fast, strong, looking to first integrate with an unsuspecting population before completely enslaving it.  No server will survive the coming apocalypse!

Anyway, we needed to be able to link from an external page (the footer of the News section, for instance) back to the main app (which uses Angular routing).  Easy enough- it’s a simple link tag with the ‘target=_self’ attribute to make sure the server is contacted.  But another wrinkle appears!  On the main app, there is a section with multiple tabs.  Different content is hidden behind each tab with an ng-if switch.  Click a tab, switch the content.

How to get the proper tab content initialized when popping into the app from an external source?  We found a way, though it’s not pretty.  If the guys at Google saw what we did, they’d probably never stop vomiting.  But the more I work with web development, the more that seems to be the case.  The blog posts and tutorials all have this beautiful, clean, elegant code applied to clearly defined use cases.

But the real web is murkier.  There are specs that can’t be changed (or you don’t have the authority to change them).  People higher up the chain want things done a certain way, and sometimes it’s your job to just make it work.  So I’m trying to stop worrying so much about what other (more skilled) people would think if they read my code, and just making it work (though always striving for that elusive, elegant, efficient solution).

So how did we solve the issue?  Well- using the traditional client-server communication system, you can pass along a querystring in the url (anything after a ‘?’ character in your href string).  When you ‘jump into’ an Angular app, it will wipe that out (via the ‘otherwise’ route, most likely, which ensures the proper view as a default).  However, there is a hook in Angular called $routeChangeSuccess.  When the route changes, this callback fires.  So, in the main controller for the app, we initialize a variable:  var aboutTab = location.search

This will grab that querystring (the location object’s search property) off the url.  Because it’s done before the route officially takes over and redirects us to ‘/’, whatever was passed via the querystring gets saved before getting wiped out by Angular.  Then, within the $routeChangeSuccess callback, you can use this variable to set the proper view on your tabs section.

It’s a little more complicated than that- the tab change function has to be inside a window.ready function, and if your co-worker decided to put spaces in the ng-if variables that change the tabs, you’ll also have to do a little string manipulation.  Also, if you have other sections within your app (like the main footer) that control these tabs, you’ll have to store both the data and methods in a service.  One final caveat- it’s a good idea to completely clear the aboutTab variable when you’re done with it (a simple aboutTab = null will work nicely).  We put a hook on the tab change function that would also scroll the page to the tabs section, but if you don’t clear the variable, it can cause your screen to jump to the tabs section on other, unrelated route changes.

So there are some things to think about, but it can be a cool trick to basically pass variables in javascript between different (full refresh) pages.  And it was a good learning experience- seeing exactly what happens when function scope is abused (not clearing aboutTab) and the lifecycle of a variable.

As an added bonus- once the ‘otherwise’ route (that redirects to ‘/’) takes over, it clears the url.  So you’re not left with a weird looking ‘?tabhere’ appended to the url.  It does its business and goes away.

If anyone actually reads this, feel free to let me know why this is a terrible idea and why it’s on the short list to be banned at the next Geneva Convention.  But also let me know a better way to do it.  And please, not the usual advice we’ve all seen on a certain developer help site: ‘You’re doing it all wrong if you need to use a trick like this- just redesign the entire project’.  This is sometimes not an option.

Getting Routed

Routing confuses me.  But not always.

The app we’re working on lately combines Django and Angular.  Both great frameworks that function similarly in some ways (injecting templates directly into the html, even using the same curly brackets), but very different in others.  Like routing.

Or maybe it’s just me.  I find Angular (1.x) routing really straightforward to use.  You’ve got your routeProvider, can grab GET parameters off the routeParams object, and just set up your .when() calls.  Although, as a short aside, I did find one gotcha with Angular.  I thought I was being so clever- I wanted to set up a default route using .otherwise() to make a 404 type page for the app.  So if a user goes to a url that doesn’t exist, they get a nice message that they’re lost and a link to a sitemap type screen.  However, the default route will show on first display of the app, so that was kind of tricky.  I didn’t want a 404 to be the first thing people see! So I tried to make a .when(‘/’) route, and have .otherwise point to that.  This is not a good idea.  It created an infinite loop that crashed Chrome (unrecoverable- had to ctrl-alt-delete out of it).  So don’t do that!

But in general, Angular’s routes are really nice to use.  I had (have) more trouble with setting up Django’s routing.  Maybe it’s my (rational) fear of regexes, but the urls.py file reads like Greek to me.  I can get simple routes working- but that’s mainly what I was able to tweak after doing the default Django blog tutorial.  The concepts behind Django routing seem to make sense, it’s just the practice of getting them to work (without spending hours of trial and error) that’s taking quite a bit longer to learn.

But in all, both are great ways of handling the request/response of the web.  Set up one section of routing (or two with Django- you need the views.py as well) and let the framework do the heavy lifting!