AngularJs Routes and Refreshing - javascript

Routes is what I am using in AngularJs to load dynamic content through an ajax calls asynchronously. This is how I do that:
var $app = angular.module('app', ['ngRoute']);
$app.config(["$routeProvider", "$locationProvider", function ($routeProvider, $locationProvider) {
$routeProvider
.when("/pop", {controller: "popCtrl", templateUrl: "partials/pop_test.html"})
.when("/dashboard", {controller: "dashBoardCtrl", templateUrl: "partials/dashboard.html"})
.otherwise({redirectTo: "/"})
$locationProvider.html5Mode(true);
}])
I also have a mini nav set up so that when I click on my links it loads those partials through the templatrUrl parameter
<ul>
<li>Home</li>
<li>Pop</li>
<li>Dashboard</li>
</ul>
This is all working great, when I click on my links it loaded the content I have set up in pop_test.html and dashboard.html
Now the problem that I am having.
When you click on the link for lets say dashboard the content loads just fine through ng-view and the routes work great BUT if the user reloads or refreshes the page and you're on lets say localhost/dashboard it will actually try to go to that link and locate a file named after whatever you have after / which in this case is a directory called dashboard.
So how can I make it so when the user refreshes or reloads the page he doesn't actually reload the /dashboard page therefor not getting a server error page not found.

As Alberto mentioned, the server needs to know what to do. In Angular's case, everything needs to be routed through the index.html when using $locationProvider.html5Mode(true); otherwise you'll run into what you are now.
To fix this you need to add a Server Rewrite rule which you can find an explanation of it, along with the different server versions, in ui-router's docs here.
You can also add a .htaccess file with the rewrite rules for index.html like so:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^(.*) /index.html [NC,L]

Related

How can I prevent angular from removing query parameters on production mode?

I'm building a web app for my job that uses angular 10 as frontend and Spring boot 2.2 for backend.
It's a very simple app with just one route, but this is route can receive a query parameter and show a different HTMLitem depending on the parameter
These are my routes:
const routes: Routes = [
{path:'mainapp', component: HomeComponent},
{path: '', redirectTo: '/mainapp', pathMatch:'full'}
]
The app works great in development mode, I mean when I run my war file with java and then run ng serve, all is good
However, when I generate the production build, in the /resources/static folder so Spring boot can serve my angular app as a static site, there are some issues.
The first issue is when I try to navigate to localhost:8080:/?report=weekly, it redirects me to localhost:8080:/mainapp and removes my query parameter.
The second issue is when I try to navigate to either localhost:8080:/mainapp or localhost:8080:/mainapp?report=weekly it shows a Whitelabel error page with a 404 error code. Even when I refresh the site
I encountered a similar issue before when I tried to deploy an Angular app to a VPS, and at that time it worked for me to add a .htaccess file with a few commands, so I tried this as well here, this is the file I added:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^(.*) /index.html [NC,L]
But apparently, this did not work. This file was added at the same level where my angular.json file is.
This is my project folder structure
I have the angular app in the Ui folder
I am not sure what else could I should you, but if you need any other information about my project let me know.
Also since this is a work-related project I can't upload the code directly, however, I'm trying to replicate the basic architecture to upload it to Github
Until then, can somebody point me in the right direction? How can I fix this route?
Thanks!
So after a couple of try and error seasons, I managed to make this work by adding a controller that redirects to my index
#Controller
public class ViewController {
#RequestMapping("/mainapp")
public String index(){
return "forward:/index.html";
}
}
Just in case someone else is having the same issue

How to have single page application without # inside the URL using Angularjs?

I am trying to build an single page application with angular js.
But i don't want # inside the url when the template is included.
I have used the
$locationProvider.html5Mode(true)
app.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider.
when('/dashboardnew', {
templateUrl: './dashboard_tmp',
controller: 'CategoryCtrl'
}).
otherwise({
redirectTo: '/dashboardnew'
});
// use the HTML5 History API
$locationProvider.html5Mode(true);
}]);
but this didn't work when the page is reloaded.
How should I solve this problem?
Thanks
You cannot, that's the compromise. The html5 mode only works if angular is loaded, which is why it will not work if the page is reloaded or if paths are bookmarked.
Stick with the #, people are starting to get used to seeing them
ofcourse you can, use html5 mode in angular and rewrite every (non-api) request to your angular home page on server side. angular will pick up url and go to url location
You need to edit your .htaccess file
RewriteEngine on
# Don't rewrite files or directories
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Rewrite everything else to index.html to allow html5 state links
RewriteRule ^ index.php [L]
RewriteRule: index.php or index.html

How do I implement `$locationProvider.html5Mode(true);

From what I understand doesn't $locationProvider.html5Mode(true); remove the hash from your URL?
While it does that for me only the home page seems to be ok when you refresh the page.
Isn't that what redirectTo: supposed to handle?
var rustyApp = angular.module('rustyApp', ['ngRoute','viewController',
'mm.foundation','angular-flexslider','ui.router'],
function($routeProvider, $locationProvider) {
$routeProvider.when('/', {
templateUrl: '/partials/home.html',
controller: 'HomeController'
});
// When you put /home, it also automatically handles /home/ as well
$routeProvider.when('/work', {
templateUrl: '/partials/work.html',
controller: 'WorkController'
});
$routeProvider.when('/contact', {
templateUrl: '/partials/contact.html',
controller: 'ContactController'
});
$routeProvider.otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(true).hashPrefix('!');
});
And this is my html:
This is in the head:
<base href="/"/>
This is the naviagation...
<section class="top-bar-section uk-navbar-flip">
<ul class="uk-navbar-nav ">
<li my-active-link="/"><i class="uk-icon-home uk-icon-medium "> </i>home</li>
<li my-active-link="/#work"><i class="uk-icon-photo uk-icon-medium "></i> work</li>
<li my-active-link="/#contact"><i class="uk-icon-envelope-o uk-icon-medium "></i> contact
</ul>
</section>
UPDATE
I changed this in my app.js file.
$locationProvider.html5Mode(true).hashPrefix('!'); //changed this
to
$locationProvider.html5Mode(true);
And added this to a .htaccess file.
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^(.*) /index.html [NC,L]
</IfModule>
However, this seemed to work when I was running a server via apache.
I suppose my problem was the static server which gulp spawns was not configured to handle that request. I am sure there is some add-on to gulp-open etc...
This is because the webserver will handle the request first, so if you don't have URL Rewriting in any form set up it will just look for a folder in the webserver with the name you have put in your 'route'.
If you're using a Node Express webserver, you can set the rewrites like this:
app.all('/*', function(req, res) {
res.sendfile(__dirname + '/index.html'); // Or whatever your public folder is
});
For Apache servers you could use a .htaccess, with something like this:
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*) /index.php/#/$1 // Might have to fiddle with the hash and/or other prefix you want to use
</IfModule>
From the docs on $location, it says that you need to have server-side rewriting for HTML5 mode to work with page refreshes. This page details the different server configurations.

Forcing a specific page to use HTTPS with angularjs

In our application we have a payment page that we want to use SSL on because we are handling credit card information. We've already put in place rewrite rules for apache to redirect a request to the specific page to HTTPS -- which takes care of any direct requests to the payment page ( http://oursite.com/pay ).
However most navigation in our site is done via relative urls and states using ui-router in angularjs and we have found that apache does not catch these requests and so serves the page without SSL.
EX If a user clicks a link with ui-sref='pay' ui-router loads the template and refreshes the state -- at no point is a request made to the server for a new uri so apache can't redirect to https
Is there a way to force ui-router(or angular in general) to force a state to use HTTPS without having to change all links to reload the entire site?
Of course this may also be a shortcoming in our rewrite rules...Here's what we have so far
RewriteEngine on
RewriteCond %{HTTPS} !=on
RewriteCond %{REQUEST_URI} /pay
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
RewriteRule ^ index.html [L]
The second set of rules is to enforce html5mode for our app.
RewriteCond %{REQUEST_FILENAME} !-f is in place so that angular can fetch the payment template for the state without needing SSL. Is this okay?
I had a similar problem, although was using $routeProvider in a SPA application. What I did was to enforce a redirect inside the controller:
var forceSSL = function () {
if ($location.protocol() !== 'https') {
$window.location.href = $location.absUrl().replace('http', 'https');
}
};
forceSSL();
This though does reload all resources. However, this happens only once when switching to SSL mode.
Note, the function is actually in a service so can be called from anywhere.
I hope this helps.

Enabling HTML 5 Mode in AngularJS 1.2

I'm working on an app that needs to use HTML 5 mode. Due to the fact that I am migrating an existing site to use AngularJS 1.2, I cannot have '#' tags in my URL. Currently, I have the following:
angular.module('myApp', ['ngRoute']).
config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider
.when("/home", {templateUrl:'home.html', controller:'homeController'})
// other routes are defined here..
.otherwise({redirectTo: '/home'});
}]);
Unfortunately, when I visit 'http://myServer/home', my app does not work when html 5 mode is enabled. I get a 404. However, when I visit http://myServer/ it works. Its like deep-linking doesn't work when I have html5 mode enabled. If I comment out the line that says "$locationProvider.html5mode(true);", the site functions. However, once again, I cannot have hash tags in my URL due to the fact I'm migrating an existing site.
Am I misunderstanding how html5mode(true) works? Or, am I doing something wrong?
Thank you
If you're using html5mode you need make changes on the server side to return your entry point index.html (main app page) on any URL request. Then angular app will parse URL and load needed page.
Here's a list of solutions for most common web servers: Apache, nginx, IIS and express.
https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#wiki-how-to-configure-your-server-to-work-with-html5mode
The link is in ui-router's wiki, but it has nothing to do with it, this answer is valid with ngRoute.
I had a similar problem while setting up my app. I was trying to implement pretty URLs as the # in the URL was very disturbing. This is how I solved the problem.
Step 1: Inject the location provider in your app module and set html5mode to true
$locationProvider.html5Mode(true);
Step 2: Insert the base tag in your index.html
Some people say this is not required but I got an error when I tried removing the tag and implementing the same. It says locationProvider requires a base tag.
<base href="/specify-app-directory-here/">
If it is in the root, the following line will suffice
<base href="/">
Step 3: You will need to setup URL rewritting on your server. I, myself am a beginner but found it quite simple. If you are using an Apache just this is how you do it.
Create an .htaccess and place it in the same directory as your index.html, or you can even use an existing one. Add the following lines to the .htaccess
Options +FollowSymLinks
RewriteEngine On
RewriteBase /base-as-specified-in-base-tag/
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^([a-zA-Z]+)$ #/$1 [NC,L]
The last line is what is very important. Basically it tells your server if it finds any links of the form "/home" it will redirect it to "/#/home" which angularJS can then handle appropriately. In doing so, it does not actually change the url displayed in the address bar and you have a pretty url. If you try reloading the page also, you will not get a 404 error.
Hope this helps.
first enable $locationProvider.html5Mode(true);
after enabling html 5 mode true you can insert
this code in .htaccess
BEGIN
Options +FollowSymLinks
RewriteEngine on
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^(.*) your file root /index.php [NC,L]
END

Categories