How can I handle a browser refresh in an AngularJS SPA application? - angularjs

My AngularJS application uses
ui-router
an index.html file
all calls for login and data go to an ASP.NET Web Controller with a URL that starts with /api/xxx.
When a user enters myapp.com then the server index.html which is what I want. When the user now selects links on that page then ui-router shows the appropriate templates inside the index.html and the browser URL bar correctly displays addresses like these:
myapp.com/home
myapp.com/home/overview
myapp.com/city
myapp.com/city/london
Note that my configuration is set up like this:
.config([
'$httpProvider', '$locationProvider', '$sceProvider', '$stateProvider',
function (
$httpProvider, $locationProvider, $sceProvider, $stateProvider) {
$sceProvider.enabled(false);
$locationProvider.html5Mode(true);
If a user now clicks on refresh then the browser forgets that it was on index.html and tries to find the web page: myapp.com/city/london etc which of course does not exist. It then shows an error page saying the page does not exist.
How can I handle this situation? I would like a user clicking on myapp.com/city to refresh and ideally go back to myapp.com/city

EDIT
When you use HTML5 modle ruting with angular you must tell your server how to handle the request If you arrive at one of those routes, for example if you try to go to myapp.com/city you will get an error because the server is going to look for a city.html page at the root of the server which won't be there. If you navigate to that route from within your app it will work fine, but if you copy and paste that into your browser address bar you'll get a 404 because the server does not understand how to get to city.html. An easy fix would be to avoid using HTML5 routing mode. According to the angular docs you need to do some URL rewriting to make your server understand how to route to those pages:
From the docs:
Using HTML5 mode requires URL rewriting on server side, basically you
have to rewrite all your links to entry point of your application
(e.g. index.html)
You can also have a look here, this answer discusses how to get HTML5 routing working with your server.

when the user refreshes the application, you can lead them to login page or default page. which can be done by the following code.
app.run(['$location', function ($location) {
$location.path('/login');
}]);

Related

I am receiving an error on page reload after removing '#' from the URL in AngularJS

I was trying to remove '#' from URL.
I looked through a lot of example doing so. Almost all examples followed same 2 steps which I followed.
Step 1: Enable HTML5 mode
.config(function ($routeProvider,$locationProvider){
$routeProvider
.when('/', {
templateUrl: 'views/main.html',
controller: 'MainCtrl',
controllerAs: 'main'
})
.when('/products/',{
templateUrl:'views/HomeMain/products.html',
controller:'MainCtrl',
controllerAs:'main'
});
$locationProvider.html5Mode(true);
})
Step 2: Add basetag
<head>
<base href="/">
</head>
After following above mentioned steps I successfully removed '#' from the URL.
Whenever I navigate to 'products' page URL looks like http://localhost:9001/products/ which is exactly what I want.
But, whenever I reload products page, this error is displayed in the browser window:
Cannot GET /products/
Why am I receiving this error? How do I handle this error?
When using # you are browsing in client side and urls are never sent to server. However when you use html5Mode, you should also configure server side, to rewrite to urls and tell client will handle it
For asp.net see this
You had a URL like http://localhost:9001/#/products/ before enabling the HTML5 mode. Please note that the contents of the hash are never submitted to the server. So what was requested from the server was http://localhost:9001 only.
As soon as server returns response, and angular takes over the control, it sees the /products/ fragment of the hash, and reacts accordingly.
After enabling HTML5 mode, your URL becomes http://localhost:9001 for your home page. Then you visit the route for products, which makes your URL http://localhost:9001/products/. Please note that because HTML5 mode uses browser's history API, your page is not refreshed entirely, even if the URL (not the hash fragment) is changed.
Now, if you refresh your page, what server receives is http://localhost:9001/products/, and since this route is not handled on the server, it returns a 404 error.
For making this to work, you need to implement logic to return appropriate contents when this route is visited. For Node based apps, you can follow this answer.
This is because the web server receiving the request looks for a resource matching the full url on the server, which doesn't exist because the angular portion of the url refers to a route in your angular application and needs to be handled in the client browser.
You can do a URL Rewrite if using NodeJS/ExpressJS/IIS. Refer this blog for the solution:
http://jasonwatmore.com/post/2016/07/26/angularjs-enable-html5-mode-page-refresh-without-404-errors-in-nodejs-and-iis

AngularJS 1.x redirect to app url scheme

I want to redirect users back to the mobile app from the controller (as opposed to a href link).
I've tried the following:
$location.url(`appname://app/verify/${this.access_code}`);
$location.path(`appname://app/verify/${this.access_code}`);
$window.location.href =`appname://app/verify/${this.access_code}`;
Because there is my appname:// instead of http://, it's treating it as an unassigned route and routing to my 404 page.
If I have a button on the page linked to appname://app/verify/${this.access_code} it works but I'd like to programmatically redirect the user.
Just wondering if anyone with a similar issue was able to solve it.
It seems adding the app scheme to the $compileProvider href whitelist fixed the issue with $window.location.href
angular.module('app', []).config(($compileProvider) => {
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|appname):/);
}

How to reload angular single page subpages and don't lose content

I'm using Spring Boot 1.4.0.M2, Angular 1.5.8, Angular UI Router 0.3.1, and Thymeleaf templating.
What I want to do is to remove hash # from my url I want to something like this:
http://localhost:8080/#/contact
look like this
http://localhost:8080/contact
What I did:
added to my angular configuration.js file something like this:
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix('!');
added to my userIndex.html head
base href="/userIndex.html"/>
This userIndex.html contains all js libraries, this is my single page index page it contains imports like this:
<script type="text/javascript" th:src="#{/js/lib/jquery.min.js}" />
and UI router:
<div ui-view="" class="ui-container"></div>
After this hash # disappear from URL, but!
I open my web page http://localhost:8080/ than click link to for example "/contact" and go to sub page URL look perfect http://localhost:8080/contact no hash and page look like this.
And that when I press F5 to reload web page http://localhost:8080/contact and than content look like this:
Starting from http://localhost:8080/ and clicking link to /contact make everything ok, trying to enter URL http://localhost:8080/contact present raw html without css, js etc. userIndex.html is not loaded is it possible to load it to sub page like /contact?
This is how my project and template config looks like
Is any body here who could help me to fix this reload thing? Maybe it is something wrong with my spring boot template config?
The difference between the two ways is that in the first case you download the userIndex.html file and afterwards angular is intercepting your location change on client side to render the contact state.
In the second case, you are requesting the contact path from the server directly. This is where your viewController configuration comes into play and returns the probably not wanted html instead of the index page which would be required to run your angular app.
For node.js there is a concept called historyApiFallback that registers a 404 error handler to return the index.html page (e.g. this express module: https://github.com/bripkens/connect-history-api-fallback). In your case you need to avoid the clash between your registered viewControllers and the routing names used in the angular app. See this question for a similar case: How can I use Angular2 PathLocationStrategy in a Spring Boot application?
All in all it's not that convenient to use the PathLocationStrategy / html5Mode even if it is essential if you later want to have the possibility to also do server side rendering.
You need to configure server side routing. So when you removed the hash and you could go to the contact and it worked, but when you refreshed it lost the content because after refreshing the page your server tried to find contact.html and if it find in your case it served only that without the css and other stuff. Depend's on your server you can configure your server to fallback to index.html all the time so when it can not find the requested page. and set base to / otherwise server will go to the root of directory and find the contact.html and serve it to you and it will be raw without css. I hope i could explain it to you. You can search for the url-re-writer for your server.

How do I get the browser's back button to take me to my previous URL? (angular ui-router)

I am doing my frontend routing with angular ui router, and I am seemingly unable to get my browser's back button to take me to most recent URL.
Let me demonstrate.
Let's say I start here
/#/myapp/
And after some clicks, I have dynamically adjusted my URL to become
/#/myapp/12345
Let's say I click a datepicker while remaining on the page
/#/myapp/12345?start=2016-01-21&end=2016-01-28
I am able to do this by using $scope'd functions in tandem with $location.url
Awesome. So far, so good.
Let's say I navigate away from this by clicking
My App
Which, due to the way I have set up my routing
myApp.config([
'$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider.state('myapp',
url : '/myapp/*id?start&end',
templateUrl : '<path to my template>',
controller : '<name of my controller>'
);
// more routes
$urlRouterProvider.otherwise('/myapp/');
}
]);
will default back to the initial state
/#/myapp
Ok, still so far, so good. However, when I click my browser's back button, I am taken back to
/#/myapp
instead of what I would expect
/#/myapp/12345?start=2016-01-21&end=2016-01-28
I can only guess that the intermediate urls that I have generated while navigating around my page were not captured by my browser's history... which I guess makes sense. How do I get my browser to recognize these intermediate steps so that I can backtrace properly?
Please take a look at the following SO answer
there are four steps to take
Unique Urls
A session service
A state history
And a location service
You may use window.history.back(); to go to back page.

How to use $stateprovider without # in URL?

How to use $stateprovider without # in URL?
If we use $locationprovider.html5mode(true) then first time it was loading correctly but after refreshing the page the state is not loading and error is displaying.
I will explain you the scenario. We are using ASP.NET web api as back end server and front end is purely HTML application with angular.JS . we are not facing any issues with backend server. we don't have issues with api calls. I will explain with a example: www.test.com/#/main/home is redirecting to www.test.com/main/home using $state.go and rendering the view perfectly. But after redirecting to www.test.com/main/home if we refresh the page we are getting 404 error. Please help out
I am looking for to provide URL as per use choice. So I need to eliminate the # for that.
The problem with html5mode(true) and page reload is that in HTML 5 mode the browser sends the full URL to the server when a page reload is triggered. And if the resource of the full URL does not exist on the server, an error is returned.
So to fix your problem, you need to add a rule on the server to always send the same .html file no matter what URL is called.
The $stateprovider will then initialize the client side state accordingly.
You need to re-write the url with .htaccess
Check this link out:
https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-configure-your-server-to-work-with-html5mode

Resources