AngularJS with MVC 6 - angularjs

This is an MVC 6/WebApi application. I'm attempting to use WebApi on the backend with AngularJS on the frontend.
I have two static files: index.html and login.html
(There will eventually be more static files.)
My Angular app is contained in the index.html while views (such as /login) are loaded from static files (i.e. login.html).
If I go to my root URL, index.html is loaded just fine. However, if I go to /login, I receive a 404 page not found.
Now, let it be said, I DO NOT want to bog down my application with a bunch of controllers and views as it's unnecessary to simply serve static files. That's overkill.
I have my routes setup to serve API calls (i.e. ~/api/whatever). How I get MVC 6 to ignore all other routes?
FYI, it also appears that MapPageRoute is deprecated in MVC 6?
EDIT:
One thing I've done to actually get it working is add my own middleware to intercept the request. The code below is added after all of my default middleware in the Configure method:
app.Use(next => async context =>
{
if (context.Request.Path.Value.StartsWith("/api"))
await next.Invoke(context);
else
await context.Response.WriteAsync(System.IO.File.ReadAllText("index.html"));
});
This seems a bit much and it's just a hack. All it does is allows any request that begins with "/api" to go through (which is then picked up by the MVC middleware), but any other call is served with the contents of the index.html. So, if my requested URL is "/login", the index.html file is served, then Angular looks at the route and loads the login.html file into view.
Any other suggestions?

Okay, so since something didn't exist in the MVC 6/ASP.NET 5 framework, I've created my own middleware that provides a lot more flexibility. It has been added to GitHub and is available through NuGet.
The project page is: https://github.com/a11smiles/AngularMiddleware

I was able do what you are asking here. Basically, I added a catch all route to my index action:
[Route("{*path}")]
meaning if no MVC action does not exist, call my Index action and angular routing will take from there

Related

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.

Single page application using AngularJS

I am working at a SPA using Flask(with jinja) and AngularJS. Everything works fine, but when the application is in a given state and I try to refresh the page in browser, the server responds with 404 response: "error": "Not found". Is there a way to make this work in a proper way when trying to access a page of the SPA application through the URL?
This may depend on how your dev http server is setup:
It should be set to always load the default page which is index.html such that the angular engine will load and run and only then it will serve the other routes (states) like localhost/state, otherwise the angular router would not be able to resolve the url since it is not loaded (letting the http server handle the request, serving a 404 Page Not Found)
The http servers sometimes serve only one level deep url's like localhost/state, not localhost/state/param and you need to change some settings to make it work, but I think this is beyond the scope of your question :)
You can use a hasher in SPA. You need to parse hash string before initializing view model and based upon the value of hash set your current visible page and then bind the view mode.
You need to have a rule to always return your SPA index.html page for any routes that match your angular routes. I'm not sure what your web server is, apache or nginx or whatever but you should be able to find instructions on how to match those requests back to index.html.
You must have enabled html5 mode to true in in your angular js app. Set that to false will solve your problem.

How to render templates with express?

My current setup is as follows:
Starting the server and going to localhost takes me to home.ejs as stated here (in my front end routing):
However, when I go to localhost:3000/posts, the posts template is infact not being injected into my index file (where my ui-view is). Ive been following the mean stack guide from thinkster, but have been making a few changes (not using inline templates)
My question is, how do I setup my routing such that localhost:3000/posts will actually take me to the posts page?
You are dealing with two types of routing, client side and server side. Client side is located in your app.config function in your angular code. That function should be calling html files, which are located in your public directory. These files are not rendered server side via express and they wouldn't not be able to read the ejs format.
Typically with MEAN stack applications, you just render your index file when the user logs in, and from there the Angular router takes over with html files and you handle your routing from there. If you don't want Angular routing, you have to set up your index.js file to render pages as they are called with res.render('posts')
You can change your posts.ejs file into an html file, and just call it via Angular when the user navigates to localhost/#/posts (depending on your version and configuration of Angular).
Your Express server side routing will handle your API calls as you have defined in index.js. In order to call those APIs, you will make GET or POST requests via Angular's $http method, through a service or factory.
Hope that helps, if not, let me know and I can elaborate or provide examples.
To solve this, I used Eric Hartmanns solution. By changing posts.ejs to posts.html, and letting the angular router take over on client side, the page was being rendered. However, when typing in localhost:3000/posts in the address bar, the page wasnt being rendered and the server was being hit for some reason. To get around that I simply made a new get route in express and re-rendered index whenever that route was hit like so:
router.get('/', function(req, res) {
res.render('index');
});

Removing the hashtag from AngularJS when working with SailsJS

I'm getting really frustrated with configuring the Routing on our app, which is using sailsJS and angularJS.
The problem is, that the browser doesn't know about angular, so any request like /login returns a 404 Error from sails. I need a solution, to keep the sails routes from the angular ones,
One solution would be to disable html5Mode, but i really don't like the look of URLs with the /#/ which is typical for angular.
I have researched a lot on this and haven't yet found a good answer or maybe a working project for this.
Is what I am trying to do even possible right now?
If you're using HTML5 mode with Angular, then you need to configure your web server (in this case SailsJS) to respond with your index.html file for requests to /login or any arbitrary routes.
If you navigate directly to http://localhost:3000/login in your web browser (assuming you're running Sails on localhost:3000), Sails needs to respond with your index.html so that your Angular app can bootstrap and then display the appropriate route. Then, subsequent links that the user clicks on in your app will be intercepted directly by the Angular router instead of Sails directly.
Angular has documentation about making HTML5 mode work correctly here.
Using this 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). Requiring a <base> tag is also important for this case, as it allows Angular to differentiate between the part of the url that is the application base and the path that should be handeled by the application.

How to handle non-root URLs in a singlepage app?

I try to make a single page app with Rails 3.2 and Backbone.js with pushState option but faced with something that I do not understand.
If I load the root URL of the app (/), everything goes right: Rails return an HTML-layout with JS which bootstraps Backbone which makes some XHRs for JSON-entities and renders the content.
But if I start using app from non-root URL (e.g. by manually typing it in the browser's address bar) then Rails will try to handle this request using theirs routing rules from routes.rb - that's wrong, cause it's a "Backbone's" route. How do I load the page and bootstrap Backbone for handling this URL in that case?
Finally I found the solution.
I put the following code into my routes.rb
class XHRConstraint
def matches?(request)
!request.xhr? && !(request.url =~ /\.json$/ && ::Rails.env == 'development')
end
end
match '(*url)' => 'home#index', :constraints => XHRConstraint.new
With this matcher all non-XHR requests are routed to HomeController which returns an HTML page. And XHR requests will be handled by other controllers which return JSON responses.
Also I left requests ending with ".json" as valid in development environment for debugging.
This is a somewhat tricky issue, but basically in a nutshell, you need to respond to all valid (HTML) requests in rails with the same (root) page, from there backbone will take over and route to the correct route handler (in your bakckbone router).
I've discussed this issue in more detail here: rails and backbone working together
Basically what I do is to create actions for every page that I want to handle, and blank views. I use respond_with to return the page (which is the same in each case) and because I handle GET actions only for HTML requests, I add this line at the top of the controller:
respond_to :html, :only => [ :show, :new ]
JSON requests are handled with respond_with as well, but unlike the HTML requests actually return the requested resource (and perform the requested action in the case of PUT, POST and DELETE).
Backbone will not be informed of your url change if you do it manually. This change will be catch by the browser and it will do its job sending the request to the server as usual.
Same if you click in a normal link, it will follow its href without inform Backbone.
If you want Backbone being in charge of a url change you have to do it through the Backbone tools you have available and this is the own Router.
So if you want to make an URL change in the Backbone way you have to do it explicitly, something like:
app.router.navigate("my/route", {trigger: true});

Resources