Simple Start
The start of the week focused on finishing the import page and testing the search page. This is the third week of development meaning by next week development should be complete, if no more unexpected errors show up.
Errors Everywhere
Development this week has been the slowest, a lot of my time was spent debugging the code. Unexpected errors will always happen within software development, this week two days were spent fixing bugs. Below details the two major bugs I was experiencing and how I fixed them.
Furthermore, the predicted development time has been pushed back by two days which takes two days off the extra features development time. Luckily development should still easily be completed within the 6 weeks of development time
Third Party Troubles
When building the Single Page Web Application (SPA), I have been using multiple third-party libraries when needed, for example, Angular Materials. Using third-party libraries can save a lot of time allowing me to focus on the application login but it means that my code is tied down to the third-party libraries.
To implement the autocomplete as well as the UI chips, represents a small set of information, I used the Angular jsTag library. The jsTag library relies on angular-typeahead which is a wrapper for twitter-typeahead, I followed the official tutorial to implement all three libraries. Once jsTag was implemented I noticed it didn’t work 100%, when selecting an item using the arrow and return keys the item would not select correctly, only the first character of the selected item would be viewed.
To diagnose the problem I first re-read the official tutorial and documentation double checking my implementation. After confirming I implemented the library correctly, I checked the Issues list on the GitHub repository. The issues contained multiple records of other developers experiencing similar problems but with no solution. Instead of switching to a different a library I attempted to solve the problem myself initially looking at a jsTag demo. When testing the demo application I couldn’t reproduce the same issue but the source code for the application wasn’t available.
Luckily the developer used Github Pages, a service which serves a website from a GitHub repository, to host the demo. I compared the demos source code with mine which again confirmed I implemented the library correctly.
My final step was to replicate the demo exactly from the ground up, testing every time a new part was added. Whilst importing each library I noticed that I was using a different version of the angular-typeahead library. Throughout the documentation and tutorial, there was no mention of version compatibility, the jsTags relies on an earlier version of angular-typeahead. Using the correct angular-typeahead version fixed all the issues I was experiencing.
The demo source code can be found here and the compatible angular-typeahead v0.2.1 can be found here. Additional the issue fix can be found here
Back Button Breaks
The Single Page Web Application (SPA) built with AngularJS, SPAs function similar to desktop applications rather than traditional websites. The advantage SPA have over traditional websites is they have relatively quicker response times, the disadvantage is they can easily break expected features. Whilst building the import page the browser back button provided with unexpected results. The import page contains a simple folder browser when pressing the back button the user would expect the folder browser path to go up, instead pressing the back button went to the previous page.
The unexpected result happens because navigating folders using the folder browser dynamically updates the DOM instead of directing the user to a different URL thus nothing added to the back button’s history.
To fix the back button problem I first researched the problem collating articles on stack overflow and on UI-router. The stack overflow provided many examples explaining how to detect a back button event and how to handle the event but they all relied on the ‘$stateChange’ events which are official depreciated since ui-router 1.0. To migrate ‘$stateChange’ to ui-router 1.0 the Transition Hook API is used instead, after reading the APIs documentation and tutorials I converted the stack overflow example to use the API.
To detect when a transition start runs the onStart hook can be used. This hook can be used to perform tasks before the transition is complete, this hook is also invoked when the user presses the back button. The example below logs to the console ‘Transition onStart Invoked’ when the user switches pages.
$transitions.onStart({},function(transition){ console.log('Transition onStart Invoked'); })
The problem with using the onStart hook is it doesn’t tell us what invoked the transition change for example by clicking on a link or pressing the back button.I first had to understand what else happens when a transition changes to detect if the used pressed the back button. I discovered that the ‘locationChangeSucess’ event fires followed by the ‘before’ event, which invokes the onStart hook, when the user presses the back button, otherwise the before event fires initially, followed by the ‘locationChangeSucess’.
Knowing the locationChangeSucces event fires first when the the users pressed the back button, I could hook on to this event. The example below sets a new scope property ‘current location’ to the user’s location when the hook is invoked.
$scope.$on('$locationChangeSucess',function(){ $scope.currentLocation = $location.url(); })
I checked if the currentLocation property was equal to the URL transitioning to, by using the ‘.to‘ method. If both properties are equal, the user has pressed the back button, if not the user has transitioned using a different method. Attaching the hooks to the root scope rather than a local scope will detect the events globally.
The example below logs to the console ‘back button pressed’ and cancels the transition, using the ‘.abort‘ method, when pressing the back button
$rootScope.$on('$locationChangeSuccess', function() { $rootScope.currentLocation = $location.url(); });
$transitions.onStart({}, function(transition) { //extracts the URL and params from transitioning var newLocation = parseUrl(transition.to()); if(newLocation === $rootScope.currentLocation){ console.log('back button pressed') //stops the transition from happening transition.abort(); } });
The example above uses Angular services to detect a back button click, the same results can be achieved just using pure Javascript. The history object which ”exposes useful methods and properties that let you … manipulate the contents of the history stack”. Using the same hook as before ‘locationChangeStart’ we can use the history object to manipulate the transition. The example below illustrates how to redirect the user back to the original page
$scope.$on('$locationChangeStart', function () { //as if the user clicked the Forward button history.forward(); });
The disadvantage with using the history object is that support is unknown for Internet Explorer, Opera, and Safari. Doing a quick test (03/08/2018), Chrome, Safari, Opera and Firefox all support the history object.
Conclusion
This week mostly consisted of debugging but was still no less enjoyable. Seeing the SPA moving slowly closer to completion is immensely gratifying as well as getting feedback from the clients. Next week the SPA should be close to completion if everything sticks to the plan.