Sunday, March 29, 2015

Using events to remove some duplication from an Angular controller

In this post, we'll show how we're dealing with a controller which is less simple than the TrackDetailsPageController shown in previous posts.

This was the code of that controller in which we had already used the techniques specified in the two previous posts (extending Angular resources with behavior and moving logic to simple JavaScript widgets):

Even though most of the logic in this controller had been removed by using four widgets, (activities, dateRange, categoryList and pagination), there was still duplication in the functions that were doing the coordination among widgets (see functions: loadMonthActivities, loadWeekActivities, loadYearActivities, loadCurrentDateRangeActivities, loadNextDateRangeActivities, loadPreviousDateRangeActivities and selectCategory).

A way of getting rid of this code was using events.

We started by publishing a 'DateRange:Changed' event using the Angular's $rootScope.$broadcast from the updateRange function inside the dateRange widget and then added a listener for that event in the controller which loaded new activities every time that event was published. We also added some new convenience helpers to the dateRange widget in order to make it easier to use from the partial.

This was the resulting code of TracksIndexPageController after these changes:

Notice how the functions loadMonthActivities, loadWeekActivities, loadYearActivities, loadCurrentDateRangeActivities, loadNextDateRangeActivities and loadPreviousDateRangeActivities have dissappeared and how this piece of code

was listening to the 'DateRange:Changed' event to reload the activities every time the date range changes.

Next, we did the same for the categoryList widget getting this new version of TracksIndexPageController:

in which selectCategory was gone.

The duplication that we highlighted in the first version of the controller had been removed but there were still some things we didn't like in this code.
  1. We were using strings to identify the events in different parts of the code (the widgets and the controller). This was a case of the magic numbers smell.
  2. Now our widgets weren't simple JavaScript objects anymore because they were using Angular's $rootScope.$broadcast inside. This made their testing a bit more difficult.
We solved the first problem by putting the events identification strings in just one place:

To avoid the second problem and make our code easier to test we created a wrapper on Angular's $rootScope.$broadcast:

This is a fragment of the current tests for the dateRange widget where we spy on eventPublisher to check that its publish method gets called whenever a function that updates the date range is called:

After all these changes this is the current version of TrackDetailsPageController:

No comments:

Post a Comment