Update to AngularJS 1.5.9 broke Angular Route - angularjs

I have an Angular app that I had to update to 1.5.9 recently.
In my app it's necessary for someone to get an URL (eg.: http://somedomain/#/dashboard/1) and have a direct access to that screen, which is quite common.
In the latest version this worked perfectly, but now with 1.5.9 angular always redirects this request to the root path (http://somedomain/#/), causing all links to be broken.
How do I fix this? Is this some new rule, or a bug with this version, or some configuration I have to change?
--- UPDATE ---
My router code is a little confusing because I have several modules, and each one defines an object like this:
portal._routes = {
'/': {
controller: 'HomeController',
controllerUrl: 'portal/layout/controller/home',
templateUrl: 'app/portal/layout/template/home.html'
},
'/application': {
controller: 'HomeController',
controllerUrl: 'portal/layout/controller/home',
templateUrl: 'app/portal/layout/template/home.html'
},
'/dashboard': {
controller: 'DashboardListController',
controllerUrl: 'portal/dashboard/controller/dashboard-list',
templateUrl: 'app/portal/dashboard/template/dashboard-list.html'
},
'/dashboard/new': {
controller: 'DashboardFormController',
controllerUrl: 'portal/dashboard/controller/dashboard-form',
templateUrl: 'app/portal/dashboard/template/dashboard-form.html'
},
'/dashboard/:id': {
controller: 'DashboardViewController',
controllerUrl: 'portal/dashboard/controller/dashboard-view',
templateUrl: 'app/portal/dashboard/template/dashboard-view.html'
},
'/dashboard/public/:id': {
controller: 'PublicDashboardViewController',
controllerUrl: 'portal/dashboard/controller/dashboard-view-public',
templateUrl: 'app/portal/dashboard/template/dashboard-view-public.html'
},
'/dashboard/:id/edit': {
controller: 'DashboardFormController',
controllerUrl: 'portal/dashboard/controller/dashboard-form',
templateUrl: 'app/portal/dashboard/template/dashboard-form.html'
},
'/user/form': {
controller: 'UserFormController',
controllerUrl: 'portal/user/controller/user-form',
templateUrl: 'app/portal/user/template/user-form.html'
},
'/user/new': {
controller: 'UserFormController',
controllerUrl: 'portal/user/controller/user-form',
templateUrl: 'app/portal/user/template/user-form.html'
}
};
Then I have this in the main angular module file:
connecta.config(function ($controllerProvider, $compileProvider, $provide, $filterProvider, $translateProvider, $routeProvider, $httpProvider, $sceProvider, applications) {
configureLazyProviders($controllerProvider, $compileProvider, $provide, $filterProvider);
configureTranslations($translateProvider, window.navigator);
configureRoutes($routeProvider);
configureRequestInterceptors($httpProvider, applications);
configureHTTPWhitelist($compileProvider, $sceProvider);
configureAuthenticationListener($httpProvider, $routeProvider);
});
Which calls this configureRoutes function:
function configureRoutes($routeProvider) {
var allRoutes = buildRoutes(portal, presenter, maps);
angular.forEach(allRoutes, function (route, url) {
if (route.controllerUrl) {
if (!route.resolve) {
route.resolve = {};
}
if (!route.resolve.load) {
route.resolve.load = function ($q, $rootScope) {
var deferred = $q.defer();
require([route.controllerUrl], function () {
deferred.resolve();
$rootScope.$apply();
});
return deferred.promise;
};
}
}
$routeProvider.when(url, route);
});
$routeProvider.otherwise({
template: '<h1>Not Found</h1>'
});
}
Which calls this auxiliary buildRoutes function:
function buildRoutes() {
var finalRouteObject = {};
angular.forEach(arguments, function (module) {
// Coloca a referĂȘncia
angular.forEach(module._routes, function (value) {
value.module = module.name;
});
$.extend(true, finalRouteObject, module._routes);
});
return finalRouteObject;
}
This code was working and has not been changed recently, only Angular has been updated and it does this weird redirect to the root path.

Related

Angular JS - go to otherwise state without changing the URL

Hi I have been using stateprovider for a long time and this time I want to implement a functionality in which without changing the url, I want to go not found state but not on url.
This is my code.
(function () {
angular.module('mean').config(aosOfferConfig);
function aosOfferConfig($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/link/404_not_found");
$stateProvider
.state('offers', {
url: '/offers/packages',
templateUrl: 'views/aosOffers/aosoffers.html',
controller: 'offersController',
controllerAs: 'packages'
})
.state('offersignup', {
url: '/offers/signup',
templateUrl: 'views/aosOffers/offerssignup.html',
controller: 'offersSignupController',
controllerAs: 'offerssignup'
})
.state('thankyou', {
url: '/offers/thankyou',
templateUrl: 'views/aosOffers/offersthankyou.html'
});
}
})();
Now in my otherwise part I am taking the user to /link/404_not_found. I don't want this. Instead, I want that it will remain in current url but the state should be changed. Just like github does.
How can I do this ?
do it this way
(function () {
angular.module('mean').config(aosOfferConfig);
function aosOfferConfig($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise(function ($injector, $location) {
var state = $injector.get('$state');
state.go('404_not_found', {}, { location: false });
return $location.path();
});
$stateProvider
.state('offers', {
url: '/offers/packages',
templateUrl: 'views/aosOffers/aosoffers.html',
controller: 'offersController',
controllerAs: 'packages'
})
.state('offersignup', {
url: '/offers/signup',
templateUrl: 'views/aosOffers/offerssignup.html',
controller: 'offersSignupController',
controllerAs: 'offerssignup'
})
.state('thankyou', {
url: '/offers/thankyou',
templateUrl: 'views/aosOffers/offersthankyou.html'
})
.state('404_not_found', {
template: '<h1>Notfound</h1>'
});
}
})();

Reading property resolve from angular $routeProvider error

I am trying to restrict access to some pages within the public directory using Angular routing.
.when('/adminprofile', {
templateUrl: '../partials/adminprofile.html',
access: {restricted: true}
})
I am trying to read what I believe is a property called access which I declared, called access
myApp.run(function ($rootScope, $location, $route, AuthService) {
$rootScope.$on('$routeChangeStart',
function (event, next, current) {
AuthService.getUserStatus()
.then(function(){
if (next.access.restricted && !AuthService.isLoggedIn()){
$location.path('/login');
$route.reload();
}
});
});
});
But what I get is this error:
TypeError: next.access is undefined
How can I read the property or how can i achieve this in a better way?, Thanks
EDIT :
According to suggestion, I have changed the code and it looks as it follows:
var myApp = angular.module('myApp', ['ngRoute']);
myApp.config(function ($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl: '../partials/home.html',
resolve: {
access: false
}
})
.when('/home', {
templateUrl: '../partials/home.html',
resolve: {
access: false
}
})
.when('/login', {
templateUrl: '../partials/login.html',
controller: 'loginController',
resolve: {
access: false
}
})
.when('/logout', {
controller: 'logoutController',
resolve: {
access: false
}
})
.when('/register', {
templateUrl: '../partials/register.html',
controller: 'registerController',
resolve: {
access: false
}
})
.when('/adminprofile', {
templateUrl: '../partials/adminprofile.html',
resolve: {
access: true
}
})
.otherwise({
redirectTo: '/'
});
});
And I have my run function:
myApp.run(function ($rootScope, $location, $route, AuthService) {
debugger;
$rootScope.$on('$routeChangeStart',
function (event, next, current) {
AuthService.getUserStatus()
.then(function(){
if (next.resolve.access && !AuthService.isLoggedIn()){
$location.path('/login');
$route.reload();
} else{
$location.path($route.current.originalPath);
}
});
});
});
Now I can see the value of access:
next.resolve.access
but is not displaying anything, I run in debug and can see that actually is going through the $routeChangeStart callback , whyyyyy?? helppppp!
ok fellas, is done, this is the right way to do it:
var myApp = angular.module('myApp', ['ngRoute']);
myApp.config(function ($routeProvider, $locationProvider) {
$routeProvider
.when('/', {
templateUrl: '../partials/home.html',
resolve: {
access: function() {
return false;
}
}
})
.when('/home', {
templateUrl: '../partials/home.html',
resolve: {
access: function() {
return false;
}
}
})
.when('/login', {
templateUrl: '../partials/login.html',
controller: 'loginController',
resolve: {
access: function() {
return false;
}
}
})
.when('/logout', {
controller: 'logoutController',
resolve: {
access: function() {
return false;
}
}
})
.when('/register', {
templateUrl: '../partials/register.html',
controller: 'registerController',
resolve: {
access: function() {
return false;
}
}
})
.when('/adminprofile', {
templateUrl: '../partials/adminprofile.html',
resolve: {
access: function() {
return true;
}
}
})
.otherwise({
redirectTo: '/'
});
});
myApp.run(function ($rootScope, $location, $route, AuthService) {
debugger;
$rootScope.$on('$routeChangeStart',
function (event, next, current) {
AuthService.getUserStatus()
.then(function(){
if (next.resolve.access && !AuthService.isLoggedIn()){
$location.path('/login');
$route.reload();
}
});
});
});
resolve has to be a function
Resolve will open your route with a resolved variable (that can be injected into controller) as soon as it gets value (instantly or when returned promise will be resolved)
as it was not returning a value, the router seems to be stuck waiting for a value to resolve.

AngularJS UI router: Block view

Right now i am making an AngularJS+UI router install application. But i have a problem, the problem is, that i want to disable access to the views, associated with the install application. I want to do it in resolve in the state config.
But the problem is i need to get the data from a RESTful API, whether the application is installed or not. I tried making the function, but it loaded the state before the $http.get request was finished.
Here was my code for the resolve function:
(function() {
var app = angular.module('states', []);
app.run(['$rootScope', '$http', function($rootScope, $http) {
$rootScope.$on('$stateChangeStart', function() {
$http.get('/api/v1/getSetupStatus').success(function(res) {
$rootScope.setupdb = res.db_setup;
$rootScope.setupuser = res.user_setup;
});
});
}]);
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/404");
$stateProvider.state('db-install', {
url: "/install/db",
templateUrl: 'admin/js/partials/db-install.html',
controller: 'DBController',
resolve: {
data: function($q, $state, $timeout, $rootScope) {
var setupStatus = $rootScope.setupdb;
var deferred = $q.defer();
$timeout(function() {
if (setupStatus === true) {
$state.go('setup-done');
deferred.reject();
} else {
deferred.resolve();
}
});
return deferred.promise;
}
}
})
.state('user-registration', {
url: "/install/user-registration",
templateUrl: "admin/js/partials/user-registration.html",
controller: "RegisterController"
})
.state('setup-done', {
url: "/install/setup-done",
templateUrl: "admin/js/partials/setup-done.html"
})
.state('404', {
url: "/404",
templateUrl: "admin/js/partials/404.html"
});
}]);
})();
EDIT:
Here is what my ajax call returns:
Try this way:
$stateProvider.state('db-install', {
url: "/install/db",
templateUrl: 'admin/js/partials/db-install.html',
controller: 'DBController',
resolve: {
setupStatus: function($q, $state, $http) {
return $http.get('/api/v1/getSetupStatus').then(function(res) {
if (res.db_setup === true) {
$state.go('setup-done');
return $q.reject();
}
return res;
});
}
}
})
Then inject setupStatus in controller:
.state('setup-done', {
url: "/install/setup-done",
templateUrl: "admin/js/partials/setup-done.html",
controller: ['$scope', 'setupStatus', function ($scope, setupStatus) {
$scope.setupdb = setupStatus.db_setup;
$scope.setupuser = setupStatus.user_setup;
}]
})

Separate nav from ng-view

I'm brand new to Angularjs and am trying to set up a new site but I'm confused as to the set up. I have a module and am using $route to successfully navigate but I'm lost as to what to do with my nav. When I load the module I want to read my database for a list of links that the user is allowed to access then spit them out in the nav. I don't want to repeat this in every view because I don't need to. So I'm trying to figure out how to run the ajax call once and then keep changing the view (I'd also like to add a class .selected to whatever view they're on). How would I go about doing that, with a directive?
(function () {
var app = angular.module('manage', ['ngRoute', 'manageControllers']);
/*
I've tried this but obviously $http isn't injected. Can I even do that?
var thisApp = this;
$http.get('/test/angular/php/angular.php', {params: {'function': 'nav'}}).then(function successCallback(response) {
});
*/
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/', {
templateUrl: 'templates/dash.html',
controller: 'DashCtrl'
}).
when('/inventory/', {
templateUrl: 'templates/inventory.html',
controller: 'InventoryCtrl'
}).
when('/inventory/:mvKey', {
templateUrl: 'templates/inventory.html',
controller: 'InventoryCtrl'
}).
when('/inventory/:mvKey/:tab', {
templateUrl: 'templates/inventory.html',
controller: 'InventoryCtrl'
}).
/* etc...*/
}
]);
})();
EDIT:
My attempt at getting the nav to run once
controllers.js
var manageControllers = angular.module('manageControllers', []);
var thisApp = this;
nav = null;
navSelected = '/';
manageControllers.controller('NavCtrl', ['$scope', '$http', function($scope, $http) {
if (thisApp.nav === null) {
$http.get('php/angular.php', {params: {'function': 'nav'}}).then(function successCallback(response) {
console.log(response.data);
thisApp.nav = response.data;
$scope.nav = thisApp.nav;
$scope.select = thisApp.navSelected;
});
} else {
$scope.nav = thisApp.nav;
$scope.select = thisApp.navSelected;
}
}]);
manageControllers.controller('DashCtrl', ['$scope', function($scope) {
thisApp.navSelected = '/';
}]);
I would swith to UI Router (https://github.com/angular-ui/ui-router) instead of $route. It allows you being much more flexible with your routing.
A Small example:
app.config(['$stateProvider',
function($stateProvider) {
$stateProvider.
state('/', {
url: '/',
views: {
'': {
templateUrl: 'templates/dash.html',
controller: 'DashCtrl'
},
'nav#': {
templateUrl: 'path/to/nav.html',
controller: 'NavCtrl'
},
}
}).
state('/inventory/', {
url: '/',
views: {
'': {
templateUrl: 'templates/dash.html',
controller: 'DashCtrl'
},
'nav#': {
templateUrl: 'path/to/nav.html',
controller: 'NavCtrl'
},
}
}).
// ...
and in your index.html
<div ui-view="nav"></div>
<div ui-view ></div>
Take a closer look at UI Router's doc, there's much more you can do with it!

AngularJS+ Firebase can't get data preloaded on route change

var testApp = angular.module('testApp', ['firebase'])
.config(['$routeProvider','$locationProvider',function
($routeProvider,$locationProvider)
{
$routeProvider
.when('/', {
templateUrl: '/views/main.html',
controller: 'MainCtrl'
})
.when('/test', {
templateUrl: '/views/test.html',
controller: testCrtl,
resolve:
{
firedata: function($q,angularFire){
var deffered = $q.defer();
var ref = new Firebase('https://shadowfax.firebaseio.com/items');
ref.on('value', function(result){
deffered.resolve(result.val());
});
return deffered.promise;
}
}
})
.otherwise({
redirectTo: '/'
});
// $locationProvider.html5Mode( true );
}]);
angular.module('testApp')
.controller('MainCtrl', ['$scope','$routeParams','$rootScope', function ($scope,$routeParams,$rootScope) {
$scope.load = function(){ return false;}
$rootScope.$on('$routeChangeStart', function(next, current) {
$scope.load = function(){ return true;}
});
}]);
testApp.controller('TestCtrl',['$scope','$timeout','Fire','firedata','testCrtl']);
var testCrtl = function ($scope,$timeout,Fire,firedata) {
$scope.items=firedata;
};
In the code above, why is the value of $scope.items=firedata; null? Please explain how can I perform a Google-like route change to preload data for the controller? This example works like John Lindquist explains, but when I use Firebase's native JS library, I can't get the data preloaded.
Also, using the Firebase angularFire library doesn't help, because it uses $scope as a parameter and it's not possible to pass $scope to the resolve function.
You should be able to use angularFireCollection to preload data:
.when('/test', {
templateUrl: '/views/test.html',
controller: testCrtl,
resolve: {
firedata: function(angularFireCollection){
return angularFireCollection('https://shadowfax.firebaseio.com/items');
}
}
})

Resources