Category
Software Engineering

Published
Dec 13, 2015

Read
3 min

Tweet
Share
Comment

Deep Linking Dynamic Anchors in AngularJS

Article Purpose

The purpose of this article is to showcase an approach for deep linking to dynamically generated page anchors using AngularJS.

Problem

In working on a very large Angular app, the need arose to deep link to specific body headings and tabs within pages. The implementation of this type of deep linking is straightforward using a simple bookmark link. Here is a refresher:

What isn’t as straightforward is the fact that our hundreds of pages are comprised of a small set of HTML templates (partials). This means that each page inflates dynamically when data is bound. As a result, we can’t simply hard code an id string into the HTML like the example above. Thankfully there is a simple solution when using Angular’s built-in filter mechanism.

Solution

We have a style guide that informs the structure of our page templates. This guide has resulted in the mapping of body headings and tab headings to the <h3> element. Using this fact in conjunction with our page model enables us to bind the id attribute of our <h3> elements to the same model property used to bind to the <h3> content itself. Here is an example:

This approach gets us most of the way there. It is an incomplete solution because the id attribute will be invalid if its value contains spaces (which is extremely common for our body headings). This is where the custom filter comes in. Here is the updated example:

Below is the custom filter. Simply update this filter with the required logic if you need to do any additional processing of the model data prior to binding.


function() {
    'use strict';

    angular.module('common').filter('anchorFilter', anchorFilter);

    function anchorFilter() {
        return function(text) {
            if (text) {
                text = text.replace(/,/g, ""); //clear out commas
                text = text.replace(/\s+/g, "-"); //spaces to dashes
                text = text.toLowerCase(); //return cleaned string
            }
            return text;
        }
    }
};

You could do the same thing a little cleaner too by leveraging a service. Here is an example that injects a service that is minification safe:


function() {
    'use strict';

    angular.module('common').filter('anchorFilter', anchorFilter);

    anchorFilter.$inject = ['stringUtilsService'];

    function anchorFilter(stringUtilsService) {
        return function(text) {
            if (text) {
                text = stringUtilsService.anchorify(text); //return cleaned string
            }
            return text;
        }
    }
};