Is it possible to add a base url to all routes in an AngularJS app? Essentially changing its location on the server (kind of, if that makes sense... so it would be accessed not via / but via /something/).
To add some context, I am trying to place an existing Angular app behind some authentication such that the app would now be accessed at address say http://mysite/secure after successful login.
The problem is if I was to load the app at http://mysite/secure it works fine (the server will obviously serve up the correct page), but clicking any link would result in a page reload and route to http://mysite/#newpage instead of http://mysite/secure/#newpage.
Without adding /secure/ to all of the routes and link element is this possible? Cheers, sorry if that is not worded well.
The location for the base href must have trailing /. For example:
<base href="location" />
will not work. It must be in this format:
<base href="location/" />
Setting the <base> HTML5 tag might help. From the documentation here:
Relative links
Be sure to check all relative links, images, scripts etc. You must either specify the url base in the head of your main html file (<base href="/my-base">) or you must use absolute urls (starting with /) everywhere because relative urls will be resolved to absolute urls using the initial absolute url of the document, which is often different from the root of the application.
Running Angular apps with the History API enabled from document root is strongly encouraged as it takes care of all relative link issues.
Related
Context
We have 2 distinct React JS applications both deployed behind a proxy.
The proxy serves one app (let's call it the home page) on the root / location. It serves the other on a subpath, e.g. /application-a/. Both are therefore on the same domain, e.g. https://my-site.com/ and https://my-site.com/application-a/
This works great if I open 2 new browser windows and navigate 1 directly to / and the other directly to /application-a/.
The problem comes when I might want to use a HTML a tag inside the root path application (home page) and link this to the application running at /application-a/ subpath using a relative href.
In this case I believe that the router kicks in and wants this to be route in the local app. I want to try to "escape" or "breakout" of the react router.
Possible Solutions
I think if the anchor tag href in the root app has a target attribute value of _blank this works ok - but is undesirable as it forces a new tab to be loaded.
If I use an absolute URL for the href value then I think it will "escape" the SPA and access the new page in the same tab. Sounds great - but now I have to use absolute URL's making the code less portable between dev/staging/live etc.
Explicitly put a matching route in the router for /application-a/ and programatically change the window.location? I could see this might work but feels messy to me.
Question
Isn't there a way I can use good old fashioned anchor tags and maybe provided some type of attribute which tells the react router to "leave me alone" and let me behave like a "regular" anchor in a static HTML/non-SPA/non-React JS page. If not, any other advice appreciated.
Similar unresolved question: Navigating out of a react-router single page application on the same host
Can you use a HashRouter instead? https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/HashRouter.md
URLs won't be "pretty", but it should work.
Alternatively, you could maybe redirect the user to the other app if the current urls doesn't match with any route definition, e.g.:
<Route render={() => (<Redirect to="http:/full-url-to/other-app" />)}/>
after all your other route definitions, so that it matches only when no other route matches.
About portability between different environments (staging, live, ...), consider you could also use Webpack DefinePlugin to set a variable at build time, so you can pass the base URL dynamically.
My setup is as follows:
I have a website for which I would like one of the pages to be handled by angular (javascript heavy one).
I have set base href for this specific page only as <base href="/myangularpage">
This page is being controlled over only by url params. There is only one page like this. No routing. I handle the state using url parameters only. Example url: http://domain.com/myangularpage?param1=2¶m2=3
However this page contains links to some other pages which should be followed and not handled by angular. Like /external-page. By external I mean they are on the same domain but have different base.
However setting up html5mode causes those links to be "push stated".
According to the docs and the answer to Angularjs Normal Links with html5Mode /external-page has a different base so should not be handled by angular.
But the problem is it is!
Tried adding / to the base url but I got a meaningless exception. I don't want the trailing slash in my page url so this isn't a real solution anyway.
EDIT:
So it seems it works when my base url has a trailing slash and I serv the page with an url which contains the slash. But I don't want the slash as it is a single page only.
I'm asking this because a couple of times now, I've tried to play around with the $locationProvider.html5Mode(true) command along with <base href="/"> and ran into a lot of errors calling the scripts/styles/images for my project. I guess there must be something I am doing wrong, but is there a certain folder structure you should follow so you don't run into these errors? Or is there a specific way that the base href works that I'm not quite understanding?
Recently, I thought I'd try it on a very, very small app. It's effectively a static website, but I want to take advantage of Angular's routing to make sure all of the pages can load instantly. So my structure would be something like this:
my-project
css
images
js
angular
app.js
app.routes.js
mainCtrl.js
views
home.html
about.html
contact.html
index.html
So I know that this folder structure isn't great, but I'll only be using Angular in this project for routing, nothing more, so it fits my needs.
I put into the head <base href="/">, put in body ng-app and ng-controller, and inside the body put a <div ng-view> somewhere too.
I added in the $locationProvider.html5Mode(true) and tried the app out. All of my scripts are then being loaded as http://localhost:8888/script.js which is incorrect. The project is located in a folder so that index.html is located in http://localhost:8888/my-project/index.html. So, it should be loading the scripts from http://localhost:8888/my-project/js/angular/app.js for example.
Is there something that I'm not understanding about the base href? Eventually I may host this app somewhere online, so I want the URLs to scripts etc to all be relevant to the file really. Anyone have any ideas?
Alright, so above the base href tag I would have my CSS styles which would be linked as css/style.css and at the bottom of my body tag I would have my scripts loaded as js/init.js or js/angular/app.js for example. This would try to load it as if the js folder is located directly at localhost:8888/js.
The Angular framework is a Single Page Application (SPA) that is able to run in a browser by essentially tricking the browser into running code snippets rather than make server calls, by making use of the "hash" (#) page anchor. Normally, a URL with a # would jump to a specific anchor point in the page; in the case of Angular or other similar SPA frameworks, the # is redirected to a code segment instead.
Ideally, you would like to not have to reference this # in your page URLs. This is where Html5Mode comes into play. Html5Mode is able to hide the #, by using the HTML5 Push State (aka history API).
When Html5Mode is enabled, the normal links on the page are silently replaced by Angular with event listeners. When these events are triggered, the current page is pushed into the browser history, and the new page is loaded. This gives the illusion that you are navigating to a new page, and even allows for the back button to operate.
This is all fine when you are dealing with links which are clicked from within the running application, but relying on event listeners can't work if you navigate to the page from an external source, where Angular isn't loaded into memory yet. To deal with this, you must be loading your pages from a web server which supports URL rewrites. When the server receives a request for a URL that there isn't a physical page for, it rewrites the URL to load the base HTML page, where Angular can be loaded and take over.
When Angular receives a request for a route which has been rewritten in this manner, it must first determine what the intended route was. This is where the Base HTML Tag comes into play. Angular uses the Base reference to help it to determine which part of the URL is on the server, and which part is a client route. Essentially, where the # in the URL would be if Html5Mode was not enabled.
Unfortunately, Base is an HTML Tag that is used by the browser for more than just Angular. The browser also uses this tag to determine the correct location to load scripts and resources using relative paths from, regardless of the path in the location bar. In general, this isn't a problem if all of the scripts and resources are relative to the location of the Index.html file. When Base is omitted, the browser will load scripts from the apparent base path determined by the current URI. However, once you provide it, the browser will use whatever value you have supplied.
In general, unless you are hosting angular on a sub-page of your site and you want your users to expect something specific in the URL string, you should always control the base on your server, and use Base="/" on the client side.
If I migrate a site to use angular and the ui-router. The URLs will now have .../#!/... in the address. Any existing links on social media etc pointing to elements with IDs i.e. www.my-website.com/#help will no longer work. Angular will redirect the URL to the root address.
Its a single page site so retaining the element addressing is important.
Does anyone know how to support the old urls in the new site?
You can use HTML5mode. This lets Angular change the routing and URLs of pages without refreshing the page (and without the '#'). Here's how, briefly:
In your app config function use:
$locationProvider.html5Mode(true); to turn on HTML5 mode.
Add base tag to your HTML head:
<base href="/" />
You server needs to be configured: See How to: Configure your server to work with html5Mode.
When routing in Angular views we add the following. I don't understand the need to add #; if I remove it, I get a 404 Error.
a href="#/AddNewOrder"
Putting # in URL indicates start of the hash part, which is used to address elements inside a single page. In modern single-page web applications, this can be used to address application states.
If you don't put the # there, you're changing the path, which means you're creating a new URL and prompting the browser to load the content at that new URL instead of the current page.
As other posters have suggested, you don't have to use hashes when using html5mode. I left it out, because it brings a few challenges of its own, which I feel to be outside the scope of the question.
enter link description hereYou don't have to. You can configure your URLs to look like normal URLs, but in reality they will still work the same way.
Check https://docs.angularjs.org/guide/$location
And refer to html5mode
It will only work in modern browsers though. Old browsers will still show the hash. But the cool thing is that you can write your URLs the old/normal way.
# or fragment identifier is a way to indicate a specific portion of a single document. Without the #, the url corresponds to a different page.
For example www.yoursite.com/page links to the /page location of your website, while www.yoursite.com/#/location points to the same index page of your website but at a specfic point in the web page #location, or in your case, a different template view.
Angular routing can not load different templates for different server urls. It is specifically designed for single-page applications and any loading of partial views or templates has to happen on the same web-page or location. Hence only the fragment part of the url changes when using angularjs routing.
More information about fragments can be found here: http://en.wikipedia.org/wiki/Fragment_identifier