Home » Angularjs » Strange behavior of $routeChangeSuccess: Not triggering on first load (but not throwing any error)

Strange behavior of $routeChangeSuccess: Not triggering on first load (but not throwing any error)

Posted by: admin November 30, 2017 Leave a comment

Questions:

I have no clue how to even begin explaining this situation but I’ll try my best. I have a simple Spanish-English-Spanish dictionary lookup page with a text box, a Lookup button, and a div to show the results. When you enter a word to lookup in the text box and hit Lookup, the results are shown in the div below.

In the results some words are hyperlinked so when you click on them, you get the search result for the clicked word in the div. That’s just like any online dictionary service functions. It works perfect except that the second functionality doesn’t seem to work on the first click after a typed search. For example:

You type pedir in the input box and hit Lookup. The div below now shows the detailed meaning of pedir including hyperlinked words like ask, English for pedir. Now, you click ask which should refresh the div and show you the Spanish meanings of ask including words like pedir. However, it just refreshes the div and shows the same content as if you looked up pedir a second time. But when you click on ask a second time now, it works fine as expected. It must be noted that the words are hyperlinked appropriately and there’s no mis-linking going on here. Not only that, other links (such as the ones on the navigation tab on top) also don’t seem to work on first click. This happens every time a new word is looked up.

Hope the above example illustrates the problem well enough; at least that’s what I have tried. My routing and controllers look like this:

var asApp = angular.module('asApp', ['ngRoute']);
asApp.config(function($routeProvider) {
    $routeProvider
.when('/', {
            title: 'Home of thesite – Radical Spanish learning tips and tricks for the adventurous learner',
            templateUrl : 'pages/home.html',
            controller  : 'mainController'
        })
// route for dictionary
        .when('/dictionary', {
            title: 'The dictionary',
            templateUrl : 'pages/dictionary.html',
            controller  : 'mainController'
        })

        // route for dictionary term
        .when('/dictionary/:word2lookup', {
            title: 'The thesite dictionary',
            templateUrl : 'pages/dictionary.html',
            controller  : 'dictController'
        })

        // route otherwise
        .otherwise({
            title: 'thesite – Radical Spanish learning tips and tricks for the adventurous learner',
            templateUrl : 'pages/home.html',
            controller  : 'mainController'
        });

});

function HeaderController($scope, $location) 
{ 
    $scope.isActive = function (viewLocation) { 
        return viewLocation === $location.path();
    };
}

asApp.run(['$rootScope', '$route', '$location', function($rootScope, $route, $location) {
$rootScope.$on('$routeChangeSuccess', function (event, current, previous) {
    document.title = 'Translation of ' + $route.current.params['word2lookup'] + ' | ' + $route.current.title;
});
}]);

asApp.controller('mainController', function($scope) {});
asApp.controller('dictController', function($scope, $routeParams){});

I don’t even know if I can reproduce the entire situation in a fiddle since it involves some significant server-side scripting.

Please let me know if there’s anything I could explain in order for someone to identify the gremlin breaking down my code’s functionality.

P.S.: This issue only affects the first click (on any link on the page) after a new search has been performed, i.e. a word is entered in the input box and the Lookup button is clicked.

Update: In response to @gr3g’s request, here’s the code for the functions lookup_check() and lookup_word():

function lookup_check(lookupterm){
    close_kb();
    if(lookupterm != ""){
        lookup_word(lookupterm);
    }
    else{
        var lookup_box = $('#word');
        lookup_box.addClass('empty');
        setTimeout(function(){ lookup_box.removeClass('empty'); },500);
    }
}
// Query dictionary and populate meaning div
function lookup_word(lookupword){
    var mean = document.getElementById('meaning');
    var waittext = '<div class="preloader-image"><br /><br />';
    var hr = createXMLHTTPRequestObject();
    var url = 'bootstrap/php/dictengine.php';
    var vars = "lookup_word=" + lookupword;
    document.getElementById('word').value = lookupword;
    hr.open("POST", url, true);
    hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    hr.onreadystatechange = function(){
        if(hr.readyState == 4 && hr.status == 200){
            var return_data = hr.responseText;
            mean.innerHTML = return_data;
            if ($(".el")[0]){ hist_mean = $('.el:first').text(); }
            else { hist_mean = ""; }
            add2local(lookupword, hist_mean);
            $(".tab-container").addClass("hide-tabs");
            if($("#dict_span").length != 0) {
                $(".tab-container").removeClass("hide-tabs");
                // logic to seggregate spanish and english results
                $("#dict_eng").addClass("hide-div");
                $("#sp_tab").addClass("active");
                $("#en_tab").removeClass("active");
            }
            document.title = 'Translation of ' + lookupword + ' | The thesite dictionary';
            $("<hr class='dict-divider'>").insertAfter(".gram_cat");
            $("<hr class='dict-divider'>").insertAfter(".quickdef");
            $("<hr class='dict-divider'>").insertBefore(".dict_source");
            $('div.entry_pos').wrap('<div class="pos"></div>');
            $('a.dictionary-neodict-first-part-of-speech').wrap('<div class="pos"></div>');
            // update url
            var loc = window.location.href;
            var lastpart = loc.substring(loc.lastIndexOf('/') + 1);
            if(lastpart == 'dictionary'){ window.location.replace(window.location.href + "/" + encodeURI(lookupword)); }
            if((lastpart != 'dictionary') && (lastpart != encodeURI(lookupword))){
                var addr = window.location.href;
                var addrtemp = addr.substring(addr.lastIndexOf('/') + 1);
                addr = addr.replace(addrtemp, encodeURI(lookupword));
                if(!!(window.history && history.pushState)){ history.pushState(null, null, addr); }
                else{ window.location.replace(addr); }
            }
        }
        //else { setTimeout('lookup_word(lookupword)', 1000); }
    }
    hr.send(vars);
    mean.innerHTML = waittext;
}

Update 2: To further facilitate @gr3g, here’s dictionary.html:

<!-- dictionary.html -->
<script>
    var loc = window.location.href;
    var lastpart = loc.substring(loc.lastIndexOf('/') + 1);
    if(lastpart != 'dictionary'){ lookup_check(decodeURI(lastpart)); }

    // populate search history if available
    var recent = document.getElementById('recent-lookups');
    var value = localStorage.getItem('w');
    if (value) {
        value = JSON.parse(value);
        var len = value.length - 1;
        var str = "";
        for (a=len; a>=0; a--){
            term = value[a].substr(0, value[a].indexOf('$'));
            term_meaning = value[a].substr(value[a].indexOf("$") + 1);
            if(term_meaning != "") {
            str = str + "<p><strong><a href='/a-s/#/dictionary/" + encodeURI(term) + "'>" + term + "</a></strong>&nbsp;&nbsp;<i class='fa fa-chevron-right' style='color: #a5a5a5;font-size: 80%;'></i>&nbsp;&nbsp;<span class='recent_meanings'>" + term_meaning + "</span></p>";
        }
            else { str = str + "<p><em>" + term + "</em></p>"; }
        }
        recent.innerHTML = str;
    }
    else { recent.innerHTML = "<p>No historical data to show right now. Words will start appearing here as you begin your lookups.</p>"; }

    // populate word of the day on pageload
    wotd();

</script>

<!-- top-image start -->
<div class="page-header-line-div">
</div>
<!-- top-image end -->
<br>
<br>

<div class="container-fluid" ng-controller="luController as luCtrl">
    <div class="row row-padding">
        <form class="form-horizontal" role="form" name="lookup-form" id="lookup-form" action="" method="">
            <div class="input-group col-md-6">
                <input  id="word" type="textbox" placeholder="Enter a Spanish or English word here..." class="form-control input-lg lookup-field lookup-field-single" onMouseOver="$(this).focus();" required ng-model="luCtrl.lookuptrm">
                <i class="fa fa-times fa-lg delete-icon" onfocus="clearword();" onclick="clearword();" data-toggle="tooltip" data-placement="top" title="Click to clear entered text"></i>
              <i class="fa fa-keyboard-o fa-2x kb-icon" onfocus="toggler('virtualkeypad', this);" onclick="toggler('virtualkeypad', this);" data-toggle="tooltip" data-placement="top" title="Click to enter accented characters"></i>
                <div class="input-group-btn">
                    <button class="btn btn-lg btn-primary lookup-submit" type="submit" id="lookup" ng-click="luCtrl.handlelookup(luCtrl.lookuptrm)">Lookup</button>
                </div>
            </div>
            <div id="virtualkeypad" class="btn-group vkb-hide"><!--col-md-offset-4-->
              <button class="btn btn-lg first-btn" type="button" onClick="spl_character('á');">á</button>
              <button class="btn btn-lg" type="button" onClick="spl_character('é');">é</button>
              <button class="btn btn-lg" type="button" onClick="spl_character('í');">í</button>
              <button class="btn btn-lg" type="button" onClick="spl_character('ó');">ó</button>
              <button class="btn btn-lg" type="button" onClick="spl_character('ú');">ú</button>
              <button class="btn btn-lg" type="button" onClick="spl_character('ü');">ü</button>
              <button class="btn btn-lg last-btn" type="button" onClick="spl_character('ñ');">ñ</button>
            </div>
        </form>

        <!-- tabbed view for bilingual words -->
        <div class="col col-md-8 bi">
            <ul class="nav nav-tabs tab-container hide-tabs lang-tabs" role="tablist">
                <li class="nav active" id="sp_tab" onClick="$(this).addClass('active'); $('#en_tab').removeClass('active'); $('#dict_eng').addClass('hide-div'); $('#dict_span').removeClass('hide-div');"><a href="" data-toggle="tab">Spanish</a></li>
                <li class="nav" id="en_tab" onClick="$(this).addClass('active'); $('#sp_tab').removeClass('active'); $('#dict_span').addClass('hide-div'); $('#dict_eng').removeClass('hide-div');"><a href="" data-toggle="tab">English</a></li>
            </ul>
            <div class="dictionary-result" id="meaning">
                <p class="box-text">This bilingual dictionary is an actively growing resource accumulating new words each day. Currently drawing from the best names in the world of Spanish/English dictionary, such as <strong>Collins</strong><sup>®</sup> and <strong>Harrap</strong><sup>®</sup>, it continues to improve with every lookup you perform. It includes regionalism, colloquialism, and other non-standard quirkiness from over a dozen Spanish dialects ranging from Peninsular to Mexican and Argentinean to Cuban. This dictionary also includes a growing number of specialty terms specific to niches such as medicine, economics, politics, etc.</p>
                <p class="box-text">Please use this page only for dictionary lookups and not comprehensive translations. You can enter either English or Spanish terms and the dictionary will automatically guess the language it belongs to. Keep your inputs to within 20 characters (that should be long enough to handle any English or Spanish word you might want to look up).</p>
            </div>
        </div>



        <!-- sidebar -->
        <div class="col col-md-4">
            <!-- history panel -->
            <div class="panel panel-default panel-box card-effect">
              <div class="panel-heading panel-title">Recent Lookups</div>
              <div id="recent-lookups" class="panel-body panel-text">
                No historical data to show right now. Words will start appearing here as you begin your lookups.
              </div>
            </div>
            <!-- WOTD panel -->
            <div class="panel panel-default panel-box card-effect">
              <div class="panel-heading panel-title">Word of the Day</div>
              <div id="wotd" class="panel-body panel-text">
                Word of the day not currently available.
              </div>
            </div>
        </div>
    </div>
</div>
Answers:

Finally I got it to work!! The offending code was in the lookup_word() function:

if(!!(window.history && history.pushState)){ history.pushState(null, null, addr); }
                else{ window.location.replace(addr); }

I just removed the if block and replaced it with history.pushState(null, null, addr); window.location.replace(addr);. Don’t know why or how but this resolved the problem.

Questions:
Answers:

This :

.otherwise({
            title: 'TheSite – Radical Spanish learning tips and tricks for the adventurous learner',
            templateUrl : 'pages/home.html',
            controller  : 'mainController'
        });

Can be replaced by this :

.otherwise("/");

In your HTML:
This should be avoided :

onclick="$('#word').blur(); lookup_check($('#word').val());"

You may put some JQuery events, but the values can’t be passed from JQuery.
It should look something like that :

onclick="$('#word').blur(); lookup_check(variableValueBindToInput)"

Could you show the lookup_check function?
And also show how you make the call to the look_up function from the link?

Here is a Plunker, using your scripts, in the angular way.
http://plnkr.co/edit/EP8y7DrTmzr0WdRkQSew?p=preview

Look here for binding html.

Does

    var loc = window.location.href;
    var lastpart = loc.substring(loc.lastIndexOf('/') + 1);
    if(lastpart != 'dictionary'){ lookup_check(decodeURI(lastpart)); }

Makes the XHR request on page load when the link is clicked (which is setting the request words in the url)?

If this is the case, can’t you use:

str = str + "<a ng-click='lookup_checkFromLink(request)'>";

And don’t check on page load?
Because, in AngularJs everything inside the app (#) does not reload the whole script when changing the route, which is a core concept of Single Page Applications : not reload all content when only a part of it needs to be changed.