I have an angular application which looks like this in terms of the directory structure
index.html
libs/
apps/
feature1/
directive.js
view.html
feature2/
feature3/
The application runs in the root of the domain, i.e when you access http://localhost:8080/ the app loads. And other features are accessible in the following url scheme
http://localhost:8080/#/feature1
http://localhost:8080/#/feature2
http://localhost:8080/#/feature3
But now we are going for production setup (which I don't control much) and we also need a landing page. In the new scheme I want the application to load and run at http://production-domain.com/app. Please notice the "app" postfix. The root of the domain will serve only the landing page which will have no business with angular or the application itself. If will just have a link to http://production-domain.com/app. The new url scheme for features should look like this
http://production-domain.com/app/#/feature1
http://production-domain.com/app/#/feature2
http://production-domain.com/app/#/feature3
The application has a lot of view files (templateUrl stuff) and also lot of libs gets loaded through the index.html page. Essentially now the server will be running one level above compared to the current setup.
What is the least intrusive (DRY) way of offsetting the whole angular app to any domain postfix. I don't want to the change the templateUrl path in all the directives.
To do what you are asking, you need to create a sub-folder in the root of your web server called "app". Then move the entire application in the sub-folder. That should be the end of it - meaning you do not have to do anything else and everything should work (given your assets use relative paths to the index.html, otherwise you have to change those).
Related
I am trying to solve a simple problem related to single page apps and angularjs in particular. There surely must be an easy way to do this that I am somehow missing.
Start by considering how a non single page app works. Let's say I have an index.html file in the root and a subfolder called other with other.html inside it. If I launch mysite.com/ in the browser, it will serve up index.html by default and everything works fine. If I launch mysite.com/other/other.html it will also work fine because other.html is a real file and includes all of the supporting resource files etc.
Now consider a single page angular app, let's say using ui-router. I create a route: state=other, url=/other and template=other/other.html with a controller attached
If I launch mysite.com. It loads the angular index.html file and I get the home page as expected. I can then go to the browser and type mysite.com/#/other and it will correctly load the other.html page as expected
The problem comes when I want to launch the app directly into mysite.com/other. The first problem I will encounter is a 404 because even if there is a directory other, there is no index.html there and the other.html file is only a template anyway so wouldn't load the resources needed to run the app.
Somehow the server needs to know that for an angular app, any url without a resource file name at the end it must load index.html and let the angular router provide the actual page.
I even tried this with the server, having it return index.html when you request the url mysite.com/other. This does succeed in loading the angular app but to my surprise, mysite.com/other now shows mysite.com/other#/ in the url. In other words, it now treats other as part of the base url so I have to type /other again (i.e. mysite.com/other#/other) to get it to show the other page.
I have tried various experiments using the base tag and html5mode but am still not able to find a suitable solution.
I appear to have figured out a solution. I set base url to / and turn on html5mode. I also have to configure the server to return index.html for any request with no file at the end (i.e. no extension)
I have recently worked in a pure Scala-Play application and later in a pure AngularJS application. I'm very impressed with both and I'm wondering what is the sweet spot of combining the two frameworks together. Since the two frameworks can be complementary but also overlapping in different areas e.g. MVC and page routing, as far as I know these are some of the possibilities:
Single Page design, use AngularJS MVC-only and use Scala-Play as "dull" service layer backend with no full page refreshes.
Allow page reloads and each page reload becomes a different AngularJS root application. This seems quite flexible e.g. the client side is not bloatted with so much data for larger applications but better partitioned for the different use-cases. The downside is that I'd need different AngularJS MVC applications and I'm not even sure how to organize it as a project. Are there examples of this?
Typical server side Web App, use Play MVC-only and AngularJS for handling UI models for each separate page.
My choice of IDE for these types of architecture would of course be WebStorm but unfortunately I can't have all client-side (AngularJS and JavaScripts) and sever-side (Scala-Play) codes in a single project.
I believe that there is no the ultimate optimal architecture for combining Play and Angular. It depends on the specificity of the project, team etc.
The decision to develop UI part with Angular and the server side back-end with Play is very reasonable. Technically it may be done as following:
Development:
Both parts are developed as detached projects with the preferable IDE.
The client should have some entry point HTML page. It is reasonable to name it index.html, but is may be any other name.
For client-server integration do on the Play side as following:
Select a sub-folder under the play application root, which will serve as the "home" for the client files. The default solution is to use the folder public, since all files under it are automatically deployed.
All client files should be copied under the public folder. The files may be organized in any structure.
Add a route for the default URL as a route to the index.html. The argument path in the route should be the full path of the index.html relatively to the application root.
If index.html is directly in the public folder, the route is like this:
GET /defaultUrl controllers.Assets.at(path="/public", file ="index.html")
Add routing to the client files:
GET /*file controllers.Assets.at(path="/public", file)
Now the distribution package will include all the client files.
Putting of the client files into the public folder should be done automatically, for example by organizing the client directory structure and appropriate configuration of the client IDE.
You can find more examples in this post.
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.
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.
I have a fully developed Angularjs frontend app (with the routes and everything set up) and would like to change the current backend to a Play 2 Java. What is the best approach to display the existing html files from Angular in Play? I have found a few examples of how to connect the routes, but I would rater not create an index.scala.html file as I would like to have the two frameworks separated as much as possible and having Play only working as backend.
If you don't want to dynamically generate views from Play using Twirl and you just want to serve your HTML static files publishing them as assets is the way to go. By default assets are designed to provide resource like CSS or JS files but nothing prevents you from serving ordinary HTML views as well.
Simply put your index.html in the public directory and modify the conf/routes files so it can handle all requests:
GET /*file controllers.Assets.at(path="/public", file)
This way your index.html will be accessible at www.yourdomain.com/index.html. Remember to put this line as the last mapping in the file as it matches all possible URLs. Other services should be declared above it.