File structure on a backbone web application - backbone.js

I am working on a web application that runs through backbone and makes use of the router. On success of certain events I want to redirect users to a static success page.
What is a good naming convention to clearly separate routing url paths from a static pages such as my success page?
the routing object in my route backbone object looks like this:
App.MyRouter = Backbone.Router.extend({
routes: {
"": "index",
"!/search/*search": "search",
"!/profile/*user": "profile",
"!/business/:id": "business"
}
});

Related

Angular 2 for non-SPA (static pages)

All the samples I come across on the web are SPAs, I'm wondering if Angular 2 has a build-in way to handle static pages. Specifically, let's say I use Angular 2 to build a blog site, and I wish users could go directly to a particular post without going through the default home component, (which also incidentally, loads a lot of server side config). I mean, how do I enable user to go to http://server/posts/:id directly, without 404 showing up or configure a ** page for unreachables.
Just need some directions, thanks.
Let's say my folder structure goes like this
/posts
/shared
/users
and my main router goes like this
#RouteConfig([
{ path: './shared/...', name: 'Shared', component: SharedComponent },
{ path: './users/...', name: 'Users', component: UserComponent },
{ path: './posts/...', name: 'Posts', component: PostComponent }
])
and post router goes like this
#RouteConfig([
{ path: '/', name: 'List', component: ListComponent, useAsDefault: true },
{ path: '/:id', name: 'Post', component: PostComponent },
{ path: '/:id/gallery', name: 'Gallery', component: GalleryComponent },
{ path: '/:id/comments', name: 'Comments', component: CommentListComponent },
])
I think I understand your problem. You need to configure your web server software (e.g., Apache) a certain way, this is not an Angular2 configuration issue. You need to configure your web server so that whenever it receives url requests like / or /posts or /posts/123 that it serves your main index.html file. Then Angular will automatically show the right content when it starts up.
Seems like you are looking for routers. Have a look at the docs:
Off. Guide and Router Tutorial. It's used like this:
#Component({ ... })
#RouteConfig([
{path:'/crisis-center', name: 'CrisisCenter', component: CrisisListComponent},
{path:'/heroes', name: 'Heroes', component: HeroListComponent},
{path:'/hero/:id', name: 'HeroDetail', component: HeroDetailComponent}
])
export class AppComponent { }
Its quite hard to tell the perfect answer as you are asking for without going through the default home component(I am not sure what do you mean by that).
AFAIK, in angular2 you can have one component which can define/set routes for other components and so their relevant view.
Let's say after defining routes in a single component, if you go with the HashLocationStrategy like below,
bootstrap(AppComponent, [provide(LocationStrategy,{useClass: HashLocationStrategy}]);
Angular2 will be able to provide you required route and so you don't need to configure server with some extra route setting. Then, you will be able to access required resource at http://server/#/posts/:id
If you go with PathLocationStrategy like below,
bootstrap(AppComponent, [provide(APP_BASE_HREF).toValue(location.pathname)]);
For this configuration angular2 will not be able to provide you required route and so server side routing needs to be configured. Then, you will be able to access required resource at http://server/posts/:id
So In short if required/asking path exits, it will take users to that path.
I know I'm a year late, but your issue is that whatever web-server you're using needs to rewrite urls to the index.html of your web-app. If it did that, then when you went to server/hero/123, the web-server would direct it to the index.html of your web-app, and your web-app would use the router to go to the HeroDetail component, without showing the default home component. Because you don't have the rewrite, the web-server is not even starting the angular app and is instead trying to serve the file server/hero/123, which doesn't exist and therefore it gives you a 404.
FYI this would still be a SPA (single page application).

Can ngRoute use a QueryString parameter instead of the hash?

The ASP MVC app I am working on uses forms authentication with a timeout. This means that when the session has timed out and the user clicks refresh they get redirected to a login page, and after that they get directed back to the original page without the deep-linked client-side # part of the url.
Is there a way to get ngRoute to base its deep-linked client-side url on a querystring instead? For example
http://somesite.com/?p=/home
The first thing to do is to have the ASP MVC server side capture any URLs that should related to your client-side angular routing and return the single-page app HTML. When registering your ASP MVC routes add the following rule
routes.MapRoute(
name: "Angular",
url: "x/{*clientPath}",
Where the "x/" is the base part of your client app, of course you can do without the "x" in the URL if your entire app is a single-page angular app, in which case you will need to add a preceding rule to render your ASP MVC server side account-login page.
Then in your Angular app make sure you use Html5 mode, like so
app.config(["$locationProvider", "$routeProvider", function ($locationProvider, $routeProvider) {
$locationProvider.html5Mode({
enabled: true,
requireBase: false,
rewriteLinks: true
});
$routeProvider.when("/x/docs/upload", {
......
});
$routeProvider.when("/x/docs/view", {
......
});
$routeProvider.otherwise({
redirectTo: "/x/docs/upload"
});
}]);
When you open your browser and navigate to http://mysite/x/what/ever/you/like the server side will use XController.Index to render Views\x\Index.cshtml, which will load your single page app, and then ngRoute will take over and present the relevant view for the "x/what/ever/you/like" part.

Routing in SPA with ASP.NET MVC 6 and AngularJS

I have a sample MVC6 single page app with one view in which I want to load 2 Angular partials using ngRoute. You can have a look at it at GitHub
There are 3 URLs in the app:
localhost - Index.cshtml
localhost/games - Index.cshtml with Angular's gamelist.html partial
localhost/games/2 - Index.cshtml with Angular's game.html partial
The routes config is the following:
MVC:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}");
routes.MapRoute("gamelist", "games", new { controller = "Home", action = "Index"});
routes.MapRoute("gameWithId", "games/2", new { controller = "Home", action = "Index" });
});
Angular:
myApp.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
.when('/games', {
templateUrl: 'partials/gameslist.html',
controller: 'GameController',
controllerAs: 'ctrl'
})
.when('/games/:gameId', {
templateUrl: 'partials/game.html',
controller: 'GameController',
controllerAs: 'ctrl'
});
$locationProvider.html5Mode(true);
}]);
It all works perfectly fine as long as I start the app from the home page '/' and then navigate to the partials using the links on the page. The problem is that the URL #3 (localhost/games/2) does not work if I start the app from it, by typing it in the address bar. The URL #2 (/games/) does work.
The reason why #3 does not work is that MVC removes '/games' part from the URL and what Angular gets is just '/2'. If you run the sample app, you will see that '$location.path = /2'. Of course Angular cannot map using that path and no partial is rendered. So my question is - how to make MVC return the full path to the client so that Angular can map it?
You can get it to work with HTML5 mode, you just need to ensure that every request maps back to your Index.cshtml view. At that point the AngularJS framework loads, client-side routing kicks in and evaluates the request URI and loads the appropriate controller and view.
We've done this with multiple Angular apps inside MVC with different .cshtml pages, though we use attribute routing with the wildcard character, e.g.
[Route("{*anything}")]
public ActionResult Index()
{
return View("Index");
}
The wildcard operator (*) tells the routing engine that the rest of the URI should be matched to the anything parameter.
I haven't had chance to get to grips with MVC6 yet but I think you can do something like this with the "new" version of attribute routing?
[HttpGet("{*anything:regex(^(.*)?$)}"]
public ActionResult Index()
{
return View("Index");
}
To make link #3 work from the browser's address bar, I turned off "html5Mode" in Angular and made links #-based.
kudos to this blog
I think it is a better solution.
His solution is rewriting the request that doesn't fit to any route and doesn't have any extension to the landing page of angular.
Here is the code.
public class Startup
{
public void Configure(IApplicationBuilder app, IApplicationEnvironment environment)
{
// Route all unknown requests to app root
app.Use(async (context, next) =>
{
await next();
// If there's no available file and the request doesn't contain an extension, we're probably trying to access a page.
// Rewrite request to use app root
if (context.Response.StatusCode == 404 && !Path.HasExtension(context.Request.Path.Value))
{
context.Request.Path = "/app/index.html"; // Put your Angular root page here
await next();
}
});
// Serve wwwroot as root
app.UseFileServer();
// Serve /node_modules as a separate root (for packages that use other npm modules client side)
app.UseFileServer(new FileServerOptions()
{
// Set root of file server
FileProvider = new PhysicalFileProvider(Path.Combine(environment.ApplicationBasePath, "node_modules")),
// Only react to requests that match this path
RequestPath = "/node_modules",
// Don't expose file system
EnableDirectoryBrowsing = false
});
}
}

Hot Towel AngularJs Subroute

I am using Hot Towel AngularJs SPA that John Papa created. I wanted to know how can I change the current menu to have sub menus.
[Edited]
The base implementation of HotTowel menu is looping into all routes and show it on the side bar. What I wanted to do is if there are sub-menu for one of the routes then it's content will be grouped in one menu item. similar to dropdowns sub menu
Don't forget that you're working with Models, and Controllers.
The Angular model and controller work in concert with the View John built.
public class HotTowelController : Controller
{
//
// GET: /HotTowel/
public ActionResult Index()
{
return View();
Notice that the Index is the View.
Ok.
look at the url when you run the page.
name
notice that its not loading another page.
That's because it's a SPA framework.
The Index has a Javascript method where we are defining the roles outside of the .NET Framework / MVC
So, we have a modeljs and a controllerjs. Just like MVC we have to use all three. But use those in the APP folder.
The APP Folder
Admin
Common
dashboard
layout
services
viewmodels
views
etc...
So
Go to the Layout Folder
open topnav.html
THen read the viewmodels
shelljs
var routes = [
{ route: '', moduleId: 'home', title: 'Reserve a Seat', nav: 1 },
{ route: 'details', moduleId: 'details', title: 'Poker Details', nav: 2 }];
return router.makeRelative({ moduleId: 'viewmodels' }) // router will look here for viewmodels by convention
.map(routes) // Map the routes
.buildNavigationModel() // Finds all nav routes and readies them
.activate(); // Activate the router
}
Seee what's happening here. We are mapping, knockout and angular have very easy mappings.
http://knockoutjs.com/documentation/plugins-mapping.html
This is an easy way to learn, and the vm is the smae for both only angular your using npm with a much more defined library in open source. both are mit so can't go wrong there.
then look at the config config.route mainjs
What do you see: Are you getting it?
Need more?

Restore backbone state after navigating away and then back

I have a Backbone Marionette app which allows users to search using various criteria. From the search results they can click on a link which navigates them away from the backbone app to a standard static page.
How can I set things up so that when they click back in their browser, the backbone search page is restored back to their previous state (with search criteria and results intact)?
Thanks
You could use backbone router for your search page and save state in location.hash, so all population/rendering will be managed by router or views which will listen to router events:
"route:[name]" (params) — Fired by the router when a specific route is matched.
"route" (route, params) — Fired by the router when any route has been matched.
For example #search/query/nuggets will trigger this route:
'search/query/:query-string': function(query) {
yourCollection.fetch({data: {query: query}})
}
and in your view
initialize: function() {
this.listenTo(yourCollection, 'sync', this.render)
}
so then user click/or hit enter in your search field you just should trigger route change: yourRouter.navigate("search/query/"+yourQuery, {trigger: true})

Resources