bscott
bscott

Reputation: 319

Angular ngRoute/UI-Router strange redirect behaviour

I have an angular app which is made up of an index.html page with some partial views inside. Let's say there are 4 partial views: view1, view2, view3, and view4. I want View1 to be the base view that gets loaded into index.html when you first hit my home/root page. I do this by setting my $urlRouterProvider.otherwise() to my view1 partial.

For some reason, this issue is only occurring for a particular view. Let's say it's view3. When I click a button on view3, it's supposed to route to another view, say view4. It always successfully routes the 1st time. However, whenever I return to view3 via any method and try to route to view4 anytime after the 1st, it always force routes back to whatever view is in the $urlRouterProvider.otherwise(): in this case it's view1. It's not just view4 either. It doesn't matter what view I attempt to route to from view3, it will load that view successfully the 1st time, then anytime after it always defaults to the $urlRouterProvider.otherwise() view.

What makes this even more strange is that even though it force routes to otherwise(), I know the controller/view of whatever partial I was attempting to route to is loading. If I set the $urlRouterProvider.otherwise() to a route that doesn't exist within my app, and then reproduce this strange behaviour, the result is that the url shows as 'localhost:8080/routeThatDoesNotExist', but what is actually displayed on the screen is the view I was attempting to route to. Here's a shortened for brevity version of my code:

app.js:

var app = angular.module('app', ['ui.router', 'controllers','services']);

app.config(['$stateProvider', '$urlRouterProvider', '$locationProvider',
                    function($stateProvider, $urlRouterProvider, $locationProvider) {
                      $urlRouterProvider.otherwise("/");
                      $stateProvider.
                        state('root', {
                          url: "/",
                          templateUrl: 'partials/view1.html',
                          controller: 'View1Ctrl',
                        }).
                        state('view2', {
                          url: "/view2",
                          controller: 'View2Ctrl',
                          templateUrl: 'partials/view2.html'
                        }).
                        state('view3', {
                          url: "/view3",
                          controller: 'View3Ctrl',
                          templateUrl: 'partials/view3.html',
                        }).
                        state('view4', {
                          url: "/view4",
                          templateUrl: 'partials/view4.html',
                          controller: 'View4Ctrl',
                        });
                        //more views omitted for brevity...
                        $locationProvider.html5Mode(true);
                    }]);

Index.html

<!doctype html>
<html ng-app="app">
<head>
    <base href="/">
    <title>App</title>
    <link rel="...">
    <style>...</style>
</head>

<body>

<div ng-controller="IndexDotHtmlCtrl">
        <button ui-sref="root">View1</button>
        <button ui-sref="view2">View2</button>
        <button ui-sref="view3">View3</button>
        <div ui-view></div>
</div>
<script src="..."></script>
</body>
</html>

View3.html

<div>
    <button ng-click="stateGo()">Go To Another View</button>
</div>

Controllers.js

var controllers = angular.module('controllers', []);

controllers.controller('IndexDotHtmlCtrl', ['$scope', function($scope) {
    //code omitted...
}]);

controllers.controller('View1Ctrl', ['$scope', '$state', function($scope, $state) {
    //code omitted...
}]);

controllers.controller('View2Ctrl', ['$scope', '$state', function($scope, $state) {
    //code omitted...
}]);

controllers.controller('View3Ctrl', ['$scope', '$state', function($scope, $state) {

   $scope.stateGo = function() {
       $state.go('view4');
   }
   //more code omitted...

}]);

controllers.controller('View4Ctrl', ['$scope', '$state', 'SomeSvc', function($scope, $state, SomeSvc) {

    $scope.RESTful_DATA = SomeSvc.query(); //using ngResource
    //more code omitted...

}]);

Things I've tried to do: I was originally having this issue using Angular's ngRoute, so I moved my entire app over to Angular UI-Router and the issue still persists.

Thought $locationProvider and html5Mode might have had something to do with it. Removed those which added the #'s to my urls. Issue still persists.

In conclusion, the issue is that, whenever I try to route specifically from view3 to any other view in my app, it always works the 1st time. Anytime after, it will call the $urlRouterProvider.otherwise() method and force redirect me to whatever view I have defined in there. I'm not sure what to do at this point. Can anyone help me figure out why I'm getting this strange routing behaviour?

Upvotes: 0

Views: 476

Answers (1)

bscott
bscott

Reputation: 319

After digging around stackoverflow and github, I found that the issue had nothing to do with the ngRoute/Ui-router services themselves. The issue lies in the <uib-tabset> and <uib-accordion> directive templates I was using in my project. They are apart of the Angular Ui-Bootstrap library. It is a known issue that has been brought up a few times on the Angular Ui-bootstrap's github: like here.

If I'm understanding it correctly, essentially what's happening is that these <uib-tabset> and <uib-accordion> get <a> anchor tags appended to them. What this means is that, if you have any clickable element inside of your tabset or accordion (basically, anything other than plain text), angular never receives their click events. They get intercepted by the browser which causes strange redirection behavior to occur. This explains why I was only having the strange routing behavior with a specific view and not all of the views in my app.

The current fix as proposed by the Angular Ui-Bootstrap team here (scroll down a few ticks, look near the bottom right corner of your screen for the 'Known Issues' header and read that section) is to modify the tabset and accordion directive templates themselves, changing where they've inserted the <a> anchor tag to a <div> tag. Doing this will cause you to lose most of the associated CSS styles because they are based off the <a> element. From here, simply go to your bootstrap.css, find all the CSS rules that affect uib-tab and uib-accordion, and update them so they point to div instead of a.

Upvotes: 0

Related Questions