Angular.js: ngRouter: Multiple controller initialization when search changed

Created on 18 Jul 2018  路  4Comments  路  Source: angular/angular.js

I'm submitting a ...

  • [x] bug report
  • [ ] feature request
  • [ ] other

Current behavior:

Controller is initializing multiple times while search changed with ngRouter.

Expected / new behavior:

Single search change should only trigger controller initialization once.

Minimal reproduction of the problem with instructions:

index.htm

<!DOCTYPE html>
<html lang="en" ng-app="app">
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <base href="/" />
    <title>ngRoute + links with #</title>
    <style>
        ul, p   { font-size: 200%;  }
        h2      { margin-bottom: 0; }
        ul, pre { margin-top: 0;    }
    </style>

    <script src="/angular/1.6.9/angular.min.js"></script>
    <script src="/angular/1.6.9/angular-route.min.js"></script>
    <script src="app.js"></script>
</head>

<body>
    <p>Click on <b>main/#a1</b> and <b>main/#a2</b> links a few times and notice that every time when address is changing 'Ctrl.init' record added to the log once.
    <br />Then click on <b>main/</b> or <b>main/#</b> link once and notice that the single click just added 3 records to the log (rouring occured 3 times and every time the controller is initialized).
    <br />There are no such issue with AngularJS 1.6.1 and below: every click to the link initializes the controller only once.</p>

    <h2>Links</h2>
    <ul>
        <li><a href="main/">main/</a></li>
        <li><a href="main/#">main/#</a></li>
        <li><a href="main/#a1">main/#a1</a></li>
        <li><a href="main/#a2">main/#a2</a></li>
    </ul>

    <h2>View</h2>
    <section id="view" ng-view=""></section>

    <h2>Log <button type="button" ng-click="$root.log.length = 0">Clear log</button></h2>
    <pre class="log" ng-bind="$root.log|json"></pre>
</body>
</html>

app.js

var log = [];

angular
    .module('app', ['ngRoute'])
    .config(Config)
    .controller('Ctrl', Ctrl)
    .run(Run)
;

Run.$inject = ['$rootScope', '$location'];
function Run($rootScope, $location){
    log.push('Run');
    $rootScope.log = log;
}

Config.$inject = ['$locationProvider', '$routeProvider'];
function Config($locationProvider, $routeProvider){
    log.push('Config');
    $locationProvider.html5Mode(true);
    $routeProvider
        .when('/main/', {
              template: '{{ctrl.url}}'
            , controller: 'Ctrl as ctrl'
        })
        .otherwise({
            redirectTo: '/main/'
        })
    ;
}

Ctrl.$inject = ['$location'];
function Ctrl($location){
    log.push('Ctrl.init');
    var vm = this;
    vm.url  = $location.url();
}

http://plnkr.co/ZjIq1gb6hKgqPrNjsKZ0

AngularJS version: 1.6.2 and above (checked with 1.6.2, 1.6.9, 1.7.1, 1.7.2). The last version that is not affected -- 1.6.1

Browser: [all]

$location regression bug

All 4 comments

I think it is b60761834 馃槆 Interesting that nobody complained for almost 1.5 years. This is clearly broken :grin:

Should be fixed by #16636. In the meantime, you can hack around the issue like this:

myApp.decorator('$browser', $delegate => {
  const originalUrl = $delegate.url;
  $delegate.url = (...args) => {
    const result = originalUrl.apply($delegate, args);
    return angular.isString(result) ? result.replace(/#$/, '') : result;
  };
  return $delegate;
});

DISCLAIMER: This is a total hack, relies on private APIs and is not recommended. Use at your own risk :grin:

Many thanks for prompt fix. Have not expected this.
I should have reported it half a year ago when first discovered %-)

You definitely should have 馃槧
I usually feel the urge to fix things that I have broken myself :grin:

This one was quite serious (combined with routing and non-trivial route initialization logic).
Thx for reporting it.

Was this page helpful?
0 / 5 - 0 ratings