mykepwnage mykepwnage - 12 days ago 4
Javascript Question

Angular UI-Router $urlRouterProvider .when not working *anymore*

Question: Why won't the

.when()
in my
$urlRouterProvider
work anymore?

I've picked up a fairly robust angular app. The most recent issue I've been trying to fix with it has been the 'user credentials lost on page refresh' issue.

The project's auth service didn't make much sense when I picked it up and after digging around I learned it was put together from a few random pages of code. It also wasn't working completely, so I looked into the different sources and decided to fully implement one of them (angular-client-side-auth)

Now, the auth service/controller I've implemented are basically the same as what's in angular-client-side-auth, however the site I'm working uses token based authentication. The token functionality was bascially in place already, so I didn't have any trouble getting it to work.

At this point, I've got the user staying logged in on refresh, routes are working along with their authentication (was previously using bulky code to disable elements in view) However, now the
$urlRouterProvider .when()
s are broken. They were working perfectly fine before, so I know something I've done is to fault - but I can't simply undo what I've implemented!

So this is $urlRouterProvider code I'm using. Worth mentioning is that I've included/required the
.when()
, whereas angular-client-side-auth doesn't seem to use them. I expected they could have been implemented with that code, however maybe there is something in it that voids the functionality of
.when()
? I didn't think so. Might as well also clarify that I'm not using the
.rule
or
$httpProvider.interceptors
there either yet. I've tried using the site with these implemented, but they didn't seem to make a difference.

$urlRouterProvider
.when('/myState/group', ['$state', 'myService', function ($state, myService) {
$state.go('app.myState.group.id', {id: myService.Params.id});
}])
.otherwise('/');


So basically, if the user or site directs the user to
/myState/group
, they should actually end up at url for state
app.myState.group.id
right? They don't though, and I can't figure out why. There isn't much out there regarding issues using urlRouterProvider.

What does happen? Well the view for
/myState/group
loads fine, and within it's
<ui-view>
is a generated list of the
id
's. You can click on an ID, which calls a function which sets the ID and then performs
$state.go('app.myState.group.id', {id: group.id, std: $scope.std1});
That state of course replaces the 'generated list' that the user clicked.

Now, for the
.state()
's...

.state('app.myState', {
abstract: true,
url: '/myState',
templateUrl: 'views/myState.html',
data: {
access: access.user
},
controller: 'MyCtrl',
resolve: {
// bunch of stuff here, I'll include it if it's relevant
},
redirectMap: {
'409': 'app.error'
}
})
.state('app.myState.group', {
url: '/group',
templateUrl: 'views/myState.group.html',
data: {
access: access.user
}
})
.state('app.myState.group.id', {
url: '/:id',
templateUrl: 'views/myState.group.id.html',
data: {
access: access.user
},
resolve: {
'': function (myService) {
myService.stuff.get(false, false, 7, 0).then(function (data) {
});
}
},
controller: 'MygroupidCtrl'
})


Finally,
index.html


<li ng-class="{active: $state.includes('app.myState')}">
<a ui-sref="app.myState.group">My State</a>
</li>


Somehow I feel like there's a better way to structure this. From what I can tell, the
app.myState.group
is never actually needed by itself, it's simply part of the nesting.

I've attempted to simply change the
ui-sref
to
="app.myState.group.id"
. When that didn't work I also changed
$state.includes('app.myState')}
to
$state.includes('app.myState.group')}
. Nope. Is this viable? I know I'd have to pass the ID in somehow, but it doesn't even seem to be directing.

Ideally, I'd like to limit the amount of unnecessary states (and controllers.. and everything). Since the user is suppose to be taken directly to
/myState/group/id
when they click the link, I feel like that should just happen. Not this linking to another state and then a redirect!

That said, I care more about simply getting this to work at the moment, and I can worry about cleaning things up later.

EDIT

So while I was writing the question I was getting some more ideas of things to try. One of them actually worked!

.state('app.myState.group', {
url: '/group',
templateUrl: 'views/myState.group.html',
data: {
access: access.user
},
onEnter: ['$state', '$timeout', 'myService', function ($state, $timeout, myService) {
$timeout(function () {
$state.go('app.myState.group.id', {id: myService.Params.id})
}, 1000);
}]


I like it, though I still wonder if there are other ways of doing this. I'm also still curious why the $urlRouterProvider stopped working!

Answer

EDIT: version 0.2.13 - breaking change

The original post was working with the UI-Router 0.2.12-. A fix in 0.2.13 disables this solution. Please follow the workaround here:

Angular UI-Router $urlRouterProvider .when not working when I click <a ui-sref="...">

ORIGINAL:

There is one issue, I found, causing similar issue. There is a working example with full code

Firstly, let's have two functions to configure

  • $urlRouterProvider and
  • $stateProvider

The snippet of these defintions:

// when config for $urlRouterProvider
var whenConfig = ['$urlRouterProvider', function($urlRouterProvider) {

    $urlRouterProvider
      .when('/app/list', ['$state', 'myService', function ($state, myService) {
            $state.go('app.list.detail', {id: myService.Params.id});
    }])
    .otherwise('/app');
}];

// state config for $stateProvider
var stateConfig = ['$stateProvider', function($stateProvider) {

  $stateProvider
    .state('app', {
        url: '/app',
        ...
}];

Having this, the reported behaviour will hapen if we will call them like this:

angular.module('MyApp', [
  'ui.router'
])
// I. firstly the states
.config(stateConfig)
// II. then the WHEN
.config(whenConfig) 

The above call will not work. When will not trigger the detail state when list is visited.

But this will work as expected:

angular.module('MyApp', [
  'ui.router'
])
// I. firstly WHEN
.config(whenConfig) 
// II. only then call state config
.config(stateConfig)

Check it here. See the app.js file...

SUMMARY: We simply have to firstly configure the $urlRouterProvider. Only then we can start to fill in our states via $stateProvider. This approach will lead to expected behaviour.

Comments