How to serve page with Backbone Controller - backbone.js

Backbone define routes in its Controller in the following fashion. Does this mean every page of the site must have a copy of it? Or that every script must be load when the user reach the first page to make it work?
var Workspace = Backbone.Controller.extend({
routes: {
"help": "help", // #help
"search/:query": "search", // #search/kiwis
"search/:query/p:page": "search" // #search/kiwis/p7
},
help: function() {
...
},
search: function(query, page) {
...
}
});

It's a hashbang router, those's aren't real pages. The urls look like:
mysite/!#help
mysite/!#search/kiwis
etc.
It's used to route single page web apps. So you only serve one page and then render other pages by getting your data from a JSON web service.
Backbone.js allows you to route to sub pages on the client inside a page. This means you can change your URL to a book markable state and when you reload the page, backbone will reload that "section" of the page.
This routing should only be used inside a page and should not span across multiple pages.
You should be using your serverside MVC framework for that.
CodeIgniter for PHP
Express for node.js
Rails for Ruby/Groovy
MVC for ASP.NET
Django for Python
etc.

Related

Asp.net MVC Routes With Angular

I have an angular application (backed by .Net 4.6 and MVC5) with routes such as
/admin
/admin/manageusers
/admin/export
I can reach these via client side routing if my asp.net application sends me to /Admin.
However, if I refresh the page the MVC routing engine doesn't know where to send me and I end up with a 404.
I've tried as many variations of this as I can think of with no success.
context.MapRoute(
"Admin_root",
"Admin/{*url}",
new { area = "Admin", controller = "Admin", action = "Index" });
Is there a way to tell MVC to ignore what would traditionally be the controller and action and just continue with the Index action?
Is there a way to build a catch all route that will ignore what would typically be the controller and action?
The ASP.NET Core Angular project template includes a call to MapSpaFallbackRoute for exactly this purpose. You can see a detailed explanation of this in Github.
Here's the code example:
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" });
});
Here's part of the explanation:
Then, since MapSpaFallbackRoute is last, any other requests that don't appear to be for static files will be served by invoking the Index action on HomeController. This action's view should serve your client-side application code, allowing the client-side routing system to handle whatever URL has been requested.
You'll need a reference to Microsoft.AspNetCore.SpaServices if you don't already have it.
EDIT: Since you've specified that you're not using ASP.NET Core, take a look at this asnwer: How to use ASP.NET MVC and AngularJS routing?. It's a similar approach to what I described but for ASP.NET MVC 5. The solution suggested there does seem to basically be the same as what you've described, however.

MVC and AngularJS Single Page Application

I have an MVC Razor app that I have just converted to a Single Page Application using AngularJS and Angular UI Router.
I have a problem in that when I go to a URL (via a refresh) such as /settings/options MVC attempts to look for the Options() method on the Settings controller. Once the page has finished loading the Angular routing takes over and shows the correct page.
Because my layout page HAS to have a RenderBody() call (else an exception is thrown) I end up with my page looking correct but having a 404 page at the bottom. The 404 is rendered into RenderBody because it cannot find the Options() method on the Settings controller
How can I fix this? I presume using some kind of MVC Routing but not sure where to start. Here is my current route
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
This question has a similar problem except they use WebAPI and I have my endpoints within a normal controller. If I use the solution here all my AJAX requests get redirected too
I feel like this question (How to use ASP.NET MVC and AngularJS routing?) might help you. They're using different "Areas" of the site, but it's the same concept for the routing.
You could also look into the URL Rewriter for IIS config and use that to make rules to always route your SPA requests to the angular app...
How do I configure IIS for URL Rewriting an AngularJS application in HTML5 mode?

Will search engines index an html fragment if it doesn't have an html and body root element?

I've developed a single page app and it's working well, but I want to split the content into multiple urls. The main page loads html fragments using angular, I was wondering if I serve the same fragments up to a search engine if the missing <html> and <body> elements will impact search ranking in a negative way. I have searched on both google and stack overflow, but if someone has asked this before it's been drowned out by other similar questions.
I think I've figured out a working solution. Using the $locationProvider service, map the urls (like /about.html and /faq.html) to the html fragments (/about-fragment.html and /faq-fragment.html) with the templateUrl argument:
module.config(["$routeProvider", function($routeProvider) {
$routeProvider
// about page
.when("/about.html", {
"templateUrl" : "about-fragment.html"
})
// faq page
.when("faq.html", {
"templateUrl" : "faq-fragment.html"
});
}]);
Put just the content fragment into the <file>-fragment.html, then copy the content into the full html wrapper at <file>.html. This way, users will see the same content as a crawler would, while still interacting with the site as a single-page app. A nice last step would be to embed a javascript redirect in each full-content page to navigate back to the url for the single page app, then use angular to switch the url back to the original - this way a user who bookmarked /about.html or /faq.html and then navigated directly to that page would still get the same single-page app experience.

expressjs static content and angular app

I am trying to load a yeoman angular app only once the user hits a specific page inside of express (e.g. '/dashboad').
The end goal is to have multiple static front pages on express without using angular. Then I would use the angular routes/ application once a user has logged in.
I have successfully followed Yeoman inside ExpressJS
but this boots up angular right from the start and does not allow routing to any express html pages.
We already chatted about this in person, but the answer here is to not have your Angular app be the base route in Express. You would have separate routes in Express for each of the static pages and finally one route for the Angular page. For example:
/ => /index.html
/dashboard/ => /dashboard.html (this is the Angular app)
/faq/ => faq.html
As long as the Angular app script is not loaded outside the one Angular route, everything will work as you expect.

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