Currently I have $routeProvider.otherwise configured to go to a page behind the login, and if that page does not resolve, they go to the login screen. I would like an additional redirection to an error page when an XHR fails that is meant to redirect the user to a success page. I have found that if I register a listener on routeChangeError to redirect to /error instead of the defaults, the .otherwise is overridden and all mistyped links take the user to the error page. Does angular provide anything that allows for this? Or maybe I missed something in my code? I can write it if angular doesn't provide it, but I'm just wondering if there is a feature native to the framework.
$rootScope.$on("$routeChangeError", function () {
$location.path("/login")
})
app.config(['$routeProvider', function ($routeProvider, $route) {
$routeProvider.
when('/error', {
templateUrl: 'error.html',
controller: 'Error',
controllerAs: 'error'
}).
when('/status-info', {
templateUrl: 'status-page.html',
controller: 'StatusInfoController',
resolve: {
auth: function (SessionService) {
return SessionService.resolve()
}
}
}).
when('/', {
redirectTo: '/status-info'
}).
otherwise({
redirectTo: '/'
})
Additional info
The desired outcome is a website that responds to incorrect URLs in the usual way - defaulting to a specific page behind login or login if not authenticated, but with the extra twist of having a special error page for rejected $route.resolve() promises.
I would love to write a plunkr for you, but I can't get to it tonight. We're launching the beta this weekend! I don't necessarily need code if you can just set me in the right direction. Any help you can offer would be great.
Related
I am new to AngularJS, and I am a little confused of how I can use angularjs ui-router in the following scenario:
It consists of two sections. The first section is the Homepage with its login and sign up views, and the second section is the Dashboard (after a successful login).
When I logged in success need to navigate from login form to "Home page".
When I tapped a registration button I need to navigate to "Registration page" from login page
Similarly I also need a "forgot password" screen
My current router is below. How can I do this functionality? (Please help with some HTML code and related controllers)
app.js:
'use strict';
//Define Routing for app
angular.module('myApp', []).config(['$routeProvider', '$locationProvider',
function($routeProvider,$locationProvider) {
$routeProvider
.when('/login', {
templateUrl: 'login.html',
controller: 'LoginController'
})
.when('/register', {
templateUrl: 'register.html',
controller: 'RegisterController'
})
.when('/forgotPassword', {
templateUrl: 'forgotpassword.html',
controller: 'forgotController'
})
.when('/home', {
templateUrl: 'views/dashBoard.html',
controller: 'dashBordController'
})
.otherwise({
redirectTo: '/login'
});
}]);
});
Firstly, nobody will design a website with your requirements / functionalities for you. stackoverflow is for specific problems, your questions is too broad, more about it - How to Ask. But to help you with a conversion from ngRoute to ui.router I can describe what the syntax should look like so you can adopt it for your website.
Converting to ui.router
Your config doesn't change that much. you need to replace .when with .state, use the right providers, and have the right syntax. Here is an example with just few states:
app.config(config);
/* your preferred way of injecting */
config.$inject = ['$stateProvider', '$urlRouterProvider'];
function config($stateProvider, $urlRouterProvider) {
$stateProvider.
state("HomepageState", {
url: "/home",
templateUrl: 'views/dashBoard.html',
controller: 'dashBordController'
}).
state("RegisterState", {
url: "/register",
templateUrl: 'register.html',
controller: 'RegisterController'
})
/*
more can be added here...
*/
$urlRouterProvider.otherwise('/login');
}
Navigation
You should be using states for navigation at all times. So replace href="url" with ui-sref="state". Here are some examples of anchor links:
<a ui-sref="HomepageState">Home page</a>
<a ui-sref="RegisterState">Register</a>
Don't forget to replace your ng-view with ui-view. (For older browser support it's better to have <div ui-view></div> instead of <ui-view></ui-view>)
Redirection
After filling in a login form, the user will press something like:
<button ng-click="login()">Sign in</button>
which will call a function login() that will validate / verify if the user can be logged in. (You can also have <form ng-submit="login()"> with <button type="submit">...) Then, if everything is fine and the user got his session / cookie, you can have a redirection to another page with:
$state.go("HomepageState");
(Don't forget to inject $state into your controller)
Advanced navigations
In the future if you have user profiles that are listed by their index. Your routing can be improved with $stateParams. Their job is to check any additional parameters in the URL. For example: a URL: /profile/721 can have a state with url:"/profile/:id". Then you can extract that id with $stateParams.id and use it in your controllers. And your redirection would look like:
$state.go("ProfileState", { "id": 721});
In my app there's a certain wizard with several steps the user must finish before using the app. Each step certain data is begin added to an array. How can I use this in order to determine how many steps the user finished before getting to the final page? Can I force redirect him to the step he skipped from? E.g typed the url to the final page.
This is my route config code:
app.config(function ($routeProvider) {
$routeProvider
.when("/", {
templateUrl: "homePage.html",
controller: "homeController"
})
.when("/summary", {
templateUrl: "summary.html",
controller: "summaryController"
})
.when("/step/:index", {
templateUrl: "/step/stepPage.html",
controller: "stepController"
})
.otherwise({
redirectTo: "/"
})
});
In the summary page I tried something like this to determine how did the user get to this page. If length of array is 5 then he went through all steps, if less, redirect him back to where he left.
if(dataArray.length<5){
$location.path( "/step/"+dataArray.length );
}
This approach always gets me back to the root URL "/". Why?
I have a MEAN stack app generated with the Yeoman Generator Angular Fullstack generator. You should only have access to the site by logging in.
The repo for ensureLoggedIn is here
While logged out, if I try to navigate to '/products' I'm redirected to '/login' just fine. However, I'm having an issue redirecting users who aren't logged in when the url is '/' or even 'localhost:9000' without the slash.
If I'm not logged in, and at the login screen, and I modify '/login' to just '/' or '' I'm sent to "main" in AngularJS and treated as logged in(I'm assuming because it recognizes the session?) and able to click links through to '/products' or '/users'.
My current routes.js looks like this:
/**
* Main application routes
*/
'use strict';
var errors = require('./components/errors');
var auth = require('./controllers/auth');
var passport = require('passport');
var ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn;
module.exports = function(app) {
// Insert routes below
// All undefined asset or api routes should return a 404
app.route('/:url(api|auth|components|app|bower_components|assets)/*').get(errors[404]);
app.route('/login').get(auth.login).post(auth.loginUser);
app.route('/logout').get(auth.logout);
// All other routes should redirect to the index.html
app.all('*', ensureLoggedIn('/login'));
app.route('/*').get(function(req, res) {
res.sendfile(app.get('appPath') + '/index.html');
});
};
I've also tried this with the routes:
app.route('/*').get(function(req, res) {
res.sendfile(app.get('appPath') + '/index.html');
});
Which seems to have the same behavior as placing ensureLoggedIn in app.all.
Here's a snippet of my routing on the Angular side, which uses ui-router:
.config ($stateProvider, $urlRouterProvider, $locationProvider) ->
$httpProvider.interceptors.push('httpInterceptor')
$urlRouterProvider
.otherwise '/'
$stateProvider
.state 'main',
url: '/'
templateUrl: 'app/views/main/main.html'
controller: 'MainCtrl'
.state 'users',
url: '/users'
templateUrl: 'app/views/users/index.html'
controller: 'UsersController'
As I said, the redirect works fine on '/users'. I'm not sure if this is a routing issue or auth issue. Auth should be fine, since clicking logout does take you to login screen and restricts access, but doesn't restrict access to the '/' route.
For the views, the login.jade is actually on the server side and the form is processed there. Except for a 404.jade, all other views are on the client-side and served using ui-router.
I feel like I'm overlooking something basic. Or just don't fully understand how this is working together. Any help would be great.
EDIT:
One thing I tried was changing the routing before app.route('login'):
app.route('/')
.get(function(req, res) {
res.render('login');
});
And changing ui-router url for main from '/' to '/main'.
This still grabbed index.html from angular and logged me in, so it didn't work. I also tried res.redirect to login in routes.js and it didn't redirect.
This is the code I use for handling authentication. It is a hack but I didn't find a better way when I needed to code it. Also the routes defined in the system varied user to user so I couldn't define them in the normal config stage. This may help with your issue though.
$routeProvider
.when("/login", { templateUrl: "/view/account/login.html", controller: Login })
.when("/forgottenpassword", { templateUrl: "/view/account/forgottenpassword.html", controller: ForgottenPassword })
.otherwise({ redirectTo: "login" });
This basically only allows access to 2 views. Once someone authenticates successfully I rebuild the routing table with the new valid views. Any invalid navigation goes to the login view.
I do this through a hack though so it might not be the best implementation angularjs wise. I do this by keeping a reference to $routeProvider on the window object then use $routeProvider as normal when you have a successful logon.
The original $routeProvider provided in angular also needs a public method to clear the existing routes before adding new ones.
After
var routes = {};
Add
this.ClearRoutes = function ()
{
routes = {};
}
Example usage after successful logon
$routeProvider.ClearRoutes();
$routeProvider
.when("/home", { templateUrl: "/view/home.html", controller: Home })
.when("/logoff", { templateUrl: "/view/account/logoff.html", controller: Logoff })
.otherwise({ redirectTo: "home" });
If the user has become logged out, I want to send the user to the login page, but it fails. I have this configuration:
webShopApp.config(function($routeProvider) {
$routeProvider.when('/login', {
templateUrl: 'partials/login.html',
controller: 'LoginController'
});
$routeProvider.when('/:id?/?products', {
templateUrl: 'partials/products.html',
controller: 'MenuController'
});
});
Note the "/:id?/?" inte the code for products. Let's say the url is 'index.html#/45/products'. If the controller discovers that the user is logged out, it calls
$location.path('/login');
If I console.log the $location.path(), it says '/login' as expected, but the url in the browser is 'index.html#/45/login'. Why is the '45' (the id) still there, and how do I get rid of it, ie, redirect to 'index.html#/login' without the id from the previous path?
I guess you should use window.location = 'yourUrl'; instead of $location.path('/login'); in this case. But in this case, the page is reloaded.
see if you need to workout in pure angularjs, so please avoid javascript terms and jquery terms i.e. windows.location and $location.path; Better option is always to route the user to login page whenever the user gets logout from the App.
for this you can specify it at the routing level i.e.
$routeProvider.otherwise({redirectTo: "/login"});
or you can say a logout link on clicking of which user will be redirected to login page
$routeProvider.when('/logout', {
templateUrl: 'partials/login.html',
controller: 'LoginController'
});
I hope this may help.
I'm working on a web page that is using Angular, jQuery Mobile, and the jQuery Mobile Angular adapter by tigbro. I have everything up an running and it works great except for one issue and that is if at any point if you refresh the page using the browser's refresh button it will give a 404 error as if it doesn't understand the route anymore. I'm not sure where the issue might be since it gets a little confusing with the two frameworks and the adapter working together and I'm new to all of these technologies.
IE happens to be the only browser this doesn't happen in and the difference seems to be in the url. Here is what it looks like when you browse to a page in IE:
http://site.com/SalesManagement/SalesManagementIndex.aspx#!/ViewNotes/4
Here is what it looks like when you browse to the same page in another browser like Chrome:
http://site.com/SalesManagement/ViewNotes/4
If you go to the first url in Chrome it will load the page and then rewrite the url to the 2nd one.
Below is my routing configuration:
var SalesManagementApp = angular.module('SalesManagementApp', [])
SalesManagementApp.config(['$routeProvider', '$compileProvider', function ($routeProvider, $compileProvider) {
$routeProvider
.when('/Search', { templateUrl: 'Views/GrowerSearchView.aspx' })
.when('/SearchResults', { templateUrl: 'Views/GrowerResultsView.aspx' })
.when('/ViewNotes/:growerIndexKey', { templateUrl: 'Views/NotesHistoryView.aspx' })
.when('/EditNote/:growerIndexKey/:noteIndexKey', { templateUrl: 'Views/UpsertNoteView.aspx' })
.when('/AddNote/:growerIndexKey', { templateUrl: 'Views/UpsertNoteView.aspx' })
.when('/', { templateUrl: 'Views/GrowerSearchView.aspx' })
.otherwise({ templateUrl: 'Views/GrowerSearchView.aspx' });
} ]);
I've read some about html5 mode verse hashbang mode but setting html5 mode to off or on in the configuration just made my routing not work at all. Any help would be much appreciated.
I figured this out thanks to a similar question on the github site for the adapter: https://github.com/opitzconsulting/jquery-mobile-angular-adapter/issues/163
The fix for this is to disable html5Mode in angular and prefix your links with the # character. This makes your urls a little uglier as you are no longer using the html5 history API but in my case that doesn't matter. Optionally you can specify a hash prefix (by default it seems to be !) but I set mine to empty string. I couldn't find any documentation telling me why this is useful but its important to know what the prefix is so you can properly set your links.
Below is my updated routing configuration. Notice I now inject the $locationProvider.
var SalesManagementApp = angular.module('SalesManagementApp', [])
SalesManagementApp.config(['$routeProvider', '$compileProvider', '$locationProvider', function ($routeProvider, $compileProvider, $locationProvider) {
$locationProvider.html5Mode(false).hashPrefix("");
$routeProvider
.when('/Search', { templateUrl: 'Views/GrowerSearchView.aspx' })
.when('/SearchResults', { templateUrl: 'Views/GrowerResultsView.aspx' })
.when('/ViewNotes/:growerIndexKey', { templateUrl: 'Views/NotesHistoryView.aspx' })
.when('/EditNote/:growerIndexKey/:noteIndexKey', { templateUrl: 'Views/UpsertNoteView.aspx' })
.when('/AddNote/:growerIndexKey', { templateUrl: 'Views/UpsertNoteView.aspx' })
.when('/', { templateUrl: 'Views/GrowerSearchView.aspx' })
.otherwise({ templateUrl: 'Views/GrowerSearchView.aspx' }); // jQuery Mobile seems to ignore the / and just use .otherwise.
$compileProvider.urlSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|tel):/);
if (!localStorage.SessionInfo)
window.location = '/Login.aspx';
} ]);
My links now look like: #/ViewNotes/{{growerIndexKey}}