I am having problems with adding new states to the runtime phase of my app using 'UI-Router-Extras'.
I have been trying for quite some time now to get new states attached and loaded AFTER a user has successfully authenticated using the 'UI-Router-Extras' plugin 'ui-router-extras'
Here is a reference to the 'UI-Router-Extras' Examples for the FutureState documentation that i'm using, but I feel like maybe my scenario is either slightly different that what's shown or I'm missing something altogether.
EXAMPLE CODE IN PLUNKER - CLICK LOGIN BUTTON -> http://plnkr.co/edit/PQwNQcLNMyPfpke076yy
Code Below Loads and Works:
I was successful in getting the initial app.config() lazy loaded from an external file. Like the code describes below:
PUBLIC - External Initially loaded Routes using 'UI-Router-Extras' - 'lazyload/states/public-states.json'
[
{
"name": "unauth",
"url": "/",
"controller": "LoginCtrl",
"templateUrl": "app/components/core/login/login.html",
"roles": ["public"]
},
{
"name": "otherwise",
"url": "/",
"roles": ["public"]
}
]
Initial App Load - Successful Lazy Load of Public States To Begin with On a Login:
'use strict';
var app = angular.module('app', ['ui.router', 'ct.ui.router.extras'])
.config(function ($urlRouterProvider, $stateProvider, $futureStateProvider) {
app.stateProvider = $stateProvider; // MIGHT NEED REFERNCE LATER?
app.futurestateProvider = $futureStateProvider; // MIGHT NEED REFERNCE LATER?
// ASYNC LOAD OF ROUTES AVAILABLE - SOON TO BE BY ROLE
var futureStateResolve = ["$http", function($http) {
return $http.get("lazyload/states/public-states.json").then(function(response) {
angular.forEach(response.data, function(state) {
$stateProvider.state(state);
console.log(state.roles);
})
})
}];
$futureStateProvider.addResolve(futureStateResolve);
console.log($futureStateProvider);
});
Code Below Does NOT work
Below is my code for the part that does not work:
PRIVATE - External Initially loaded Routes using 'UI-Router-Extras' - 'lazyload/states/public-states.json'
This json below is meant to be added after user login using lazy loading and FutureStates. So far no luck :(
[
{
"name": "app",
"abstract": true,
"url": "?menu1State",
"templateUrl": "app/components/core/layout/layout.html",
"controller": "LayoutCtrl",
"roles": ["level1"]
},
{
"name": "app.dashboard",
"url": "/app",
"views": {
"feature": {
"templateUrl": "app/components/core/features/features.html",
"controller": "FeatureCtrl"
}
},
"roles": ["level1"]
},
{
"name": "app.associations_beanbags",
"url": "/app/associations/bean-bags?myParam1&myParam2&modalState",
"views": {
"feature": {
"templateUrl": "app/components/core/features/associations/bean-bags.html",
"controller": "BeanbagsCtrl"
}
},
"roles": ["level2"]
}
]
Login button triggering the lazy creation of states after successful authentication:
<a id="btn-fblogin" href="" class="btn btn-primary pull-right" ng-click="callNotify(username, password);">Login</a>
What happens is when a user clicks on a login button it mocks the success and calls '$scope.callNotify' triggering the code you see below. What ends up happening is everything works up until the 'app.futurestateProvider.futureState(newState);' and the trying to call the new state to see if was added '$state.go('app.dashboard');'. All of this results in an error that states the following:
Console Error:
Error: Could not resolve 'app.dashboard' from state 'unauth'
at Object.transitionTo (http://localhost:3000/bower_components/angular-ui-router/release/angular-ui-router.js:2521:17)
at Object.$state.transitionTo (http://localhost:3000/bower_components/ui-router-extras/release/ct-ui-router-extras.js:136:34)
at Object.$state.transitionTo (http://localhost:3000/bower_components/ui-router-extras/release/ct-ui-router-extras.js:874:55)
at Object.$state.transitionTo (http://localhost:3000/bower_components/ui-router-extras/release/ct-ui-router-extras.js:1301:48)
at Object.go (http://localhost:3000/bower_components/angular-ui-router/release/angular-ui-router.js:2454:21)
at http://localhost:3000/app/components/core/auth/auth-service.js:58:13
at http://localhost:3000/bower_components/angular/angular.js:8113:11
at wrappedCallback (http://localhost:3000/bower_components/angular/angular.js:11573:81)
at wrappedCallback (http://localhost:3000/bower_components/angular/angular.js:11573:81)
at http://localhost:3000/bower_components/angular/angular.js:11659:26
So what this looks like to me is that the states never really got added when we expected, thus causing an error saying something to the likes of "Sorry, i don't see the route you are looking for."
'use strict';
app.controller('AuthServiceTestCtrl', ['$window','$scope','$http', '$state', function (win, $scope, $http, $state) {
$scope.callNotify = function(username,password, $stateProvider, $futureStateProvider) {
//notify(username, password); // CALL SERVICE AND GET A RETURN VALUE / ACTION
var loadedAgain = $http.get("lazyload/states/private-states.json").success(function(response) {
if(username == "testuser@example.com" && password == "abc123"){
console.log('Valid Credentials. Logging In.');
// NOW THAT USER IS LOGGED IN REGISTER NEW PRIVATE ROUTE / STATES - PREFERABLY FROM THE SECURED SERVER FILE ('private-states.json') ABOVE
var adminModuleFutureStates = [
{
"name": "app",
"abstract": true,
"url": "?menu1State",
"templateUrl": "app/components/core/layout/layout.html",
"controller": "LayoutCtrl",
"roles": ["level1"]
},
{
"name": "app.dashboard",
"url": "/app",
"views": {
"feature": {
"templateUrl": "app/components/core/features/features.html",
"controller": "FeatureCtrl"
}
},
"roles": ["level1"]
},
{
"name": "app.associations_bean-bags",
"url": "/app/associations/bean-bags?myParam1&myParam2&modalState",
"views": {
"feature": {
"templateUrl": "app/components/core/features/associations/bean-bags.html",
"controller": "BeanBagsCtrl"
}
},
"roles": ["level2"]
}
];
angular.forEach(adminModuleFutureStates, function(newState) {
console.log(newState);
app.futurestateProvider.futureState(newState); // WAS SAVED AS A VAR IN APP CONFIG FOR LATER REFERENCE
});
// FINALLY GO TO ONE OF THE NEWLY ADDED PRIVATE STATES WE JUST ADDED WHICH IS A DASHBOARD
$state.go('app.dashboard');
}
});
};
}]);
I'm very sorry for not having a working example ready, I'm on it right now, but i figured i'd post this now to show what I do have and possibly get a discussion or solution as to how I can load states to ui-router at runtime via my controller listed above after the application has already loaded the config etc..
What I'm trying to ultimately do here is this:
I really need to only expose two safe public routes to begin with on login. Then once a user logs in the previous public routes stay and i'm trying to add or decorate the existing routes with new ones that now allow the user to only have access to the routes their role provides. Security for us is extremely important and I do not see any benefit whatsoever loading every possible route upfront on a login page letting someone know what out api or server routs are without at least being logged in.
I'm very sorry for rambling but I've come to the conclusion that I'm just flat doing it wrong and need some extra eyes to maybe catch why i can't add new states post load.
Seriously thank you so much!
EXAMPLE CODE IN PLUNKER - CLICK LOGIN BUTTON -> http://plnkr.co/edit/PQwNQcLNMyPfpke076yy