Search code examples
angularjsroutesangular-routingurldecoderoute-provider

Angular - how to get encoded url to be recognised in $routeProvider - when a hash is changed to %23


Some email clients (apparently) encode the hash in a url to %23, (for example #/path#sectionId becomes #/path%23sectionId or %23/path%23sectionId).

When accessing such a url angular uses the otherwise propriety of $routeProvider and redirects you to your default page. (I think)

How could I decode the url before $routeProvider.when redirects you accordingly? Is there any work-around to this you are aware of? Ideally other than not using the hash to scroll to a section on the page.

Thanks.


Solution

  • A work around I found so far: adding a $location dependency to my main controller reveals that $location.hash return '' and $location.path returns '/path#sectionId'.

    Based on this, we can add something like the following at the top of the controller:

        // when has in url is encoded to %23, location.hash does not recognize it, and it  gets cleared from the path in $RouteProvider
        var holdHash = $location.path();
        holdHash = holdHash.split('#');
        if (holdHash.length > 1) { // if it has an item after the hash in the array
            $location.hash(holdHash.pop()); //take that item and use it as a hash
        } 
    

    BUT you lose the values in $location.search

    OR you could do this:

    $location.url(decodeURIComponent($location.url()))
    

    which keeps your search too, but replaces '?' with '%3F' and then returns it in $location.ash not $location.search.

    They are a tad hacky a solution somehow, and don't fully work (see problems with the $location.search)

    ***** EDIT 2 SOLUTION ****

    As a final solution to my problem, I've updated the service that stored the id, and trigger the hash detection first thing in my main controller. Also updated the has detection to keep in mind all the variations in which that can be stored:

    /**
         * tries to get hash from $location
         * if unavailable tries to look for encoded hash
         * if unavailable returns null
         * @return {string or null} [a string with the id of the section or null]
         */
        function getHashID() {
            var fromLoc = $location.hash(),
            fromEncodedUrl,
            inPath;
    
            if (fromLoc.length) {
                return fromLoc;
            }
    
            // iphone Chrome encodes the pound sign on redirect
            fromEncodedUrl = $location.url();
            fromEncodedUrl = fromEncodedUrl.split('%23');
    
            if (fromEncodedUrl.length > 1) {
                return fromEncodedUrl.pop();
            }
    
            inPath = $location.path();
            inPath = inPath.split('#');
    
            if (inPath.length > 1) {
                return inPath.pop();
            }
    
            return null;
        }
    

    I later trigger the animation with the value stored in the service object, or getting a new value, and after the animation is done I clear the value in the service.