mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-24 02:30:13 +00:00
Migrate from hasher.js to history.js to support HTML5-style navigation. Also clean up the HMR support.
This commit is contained in:
@@ -2,22 +2,18 @@ import 'bootstrap';
|
||||
import 'bootstrap/dist/css/bootstrap.css';
|
||||
import './css/site.css';
|
||||
import * as ko from 'knockout';
|
||||
import appLayout from './components/app-layout/app-layout';
|
||||
import { createHistory } from 'history';
|
||||
|
||||
ko.components.register('app-layout', appLayout);
|
||||
ko.applyBindings();
|
||||
// Load and register the <app-root> component
|
||||
ko.components.register('app-root', require('./components/app-root/app-root').default);
|
||||
|
||||
// Tell Knockout to start up an instance of your application
|
||||
ko.applyBindings({ history: createHistory() });
|
||||
|
||||
// Basic hot reloading support. Automatically reloads and restarts the Knockout app each time
|
||||
// you modify source files. This will not preserve any application state other than the URL.
|
||||
declare var module: any;
|
||||
if (module.hot) {
|
||||
module.hot.dispose(() => {
|
||||
ko.cleanNode(document.body);
|
||||
|
||||
// TODO: Need a better API for this
|
||||
Object.getOwnPropertyNames((<any>ko).components._allRegisteredComponents).forEach(componentName => {
|
||||
ko.components.unregister(componentName);
|
||||
});
|
||||
});
|
||||
module.hot.accept();
|
||||
module.hot.accept();
|
||||
module.hot.dispose(() => ko.cleanNode(document.body));
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
import * as ko from 'knockout';
|
||||
import * as router from '../../router';
|
||||
|
||||
class AppLayoutViewModel {
|
||||
public route = router.instance().currentRoute;
|
||||
}
|
||||
|
||||
export default { viewModel: AppLayoutViewModel, template: require('./app-layout.html') };
|
||||
@@ -0,0 +1,39 @@
|
||||
import * as ko from 'knockout';
|
||||
import { Route, Router } from '../../router';
|
||||
|
||||
// Declare the client-side routing configuration
|
||||
const routes: Route[] = [
|
||||
{ url: '', params: { page: 'home-page' } },
|
||||
{ url: 'counter', params: { page: 'counter-example' } },
|
||||
{ url: 'fetch-data', params: { page: 'fetch-data' } }
|
||||
];
|
||||
|
||||
class AppRootViewModel {
|
||||
public route: KnockoutObservable<Route>;
|
||||
private _router: Router;
|
||||
|
||||
constructor(params: { history: HistoryModule.History }) {
|
||||
// Activate the client-side router
|
||||
this._router = new Router(params.history, routes)
|
||||
this.route = this._router.currentRoute;
|
||||
|
||||
// Load and register all the KO components needed to handle the routes
|
||||
ko.components.register('nav-menu', require('../nav-menu/nav-menu').default);
|
||||
ko.components.register('home-page', require('../home-page/home-page').default);
|
||||
ko.components.register('counter-example', require('../counter-example/counter-example').default);
|
||||
ko.components.register('fetch-data', require('../fetch-data/fetch-data').default);
|
||||
}
|
||||
|
||||
// To support hot module replacement, this method unregisters the router and KO components.
|
||||
// In production scenarios where hot module replacement is disabled, this would not be invoked.
|
||||
public dispose() {
|
||||
this._router.dispose();
|
||||
|
||||
// TODO: Need a better API for this
|
||||
Object.getOwnPropertyNames((<any>ko).components._allRegisteredComponents).forEach(componentName => {
|
||||
ko.components.unregister(componentName);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default { viewModel: AppRootViewModel, template: require('./app-root.html') };
|
||||
@@ -7,23 +7,23 @@
|
||||
<span class='icon-bar'></span>
|
||||
<span class='icon-bar'></span>
|
||||
</button>
|
||||
<a class='navbar-brand' href='#'>WebApplicationBasic</a>
|
||||
<a class='navbar-brand' href='/'>WebApplicationBasic</a>
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
<div class='navbar-collapse collapse'>
|
||||
<ul class='nav navbar-nav'>
|
||||
<li>
|
||||
<a class='navbar-brand' href='#' data-bind='css: { active: route().page === "home-page" }'>
|
||||
<a class='navbar-brand' href='/' data-bind='css: { active: route().page === "home-page" }'>
|
||||
<span class='glyphicon glyphicon-home'></span> Home
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class='navbar-brand' href='#counter' data-bind='css: { active: route().page === "counter-example" }'>
|
||||
<a class='navbar-brand' href='/counter' data-bind='css: { active: route().page === "counter-example" }'>
|
||||
<span class='glyphicon glyphicon-education'></span> Counter
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class='navbar-brand' href='#fetch-data' data-bind='css: { active: route().page === "fetch-data" }'>
|
||||
<a class='navbar-brand' href='/fetch-data' data-bind='css: { active: route().page === "fetch-data" }'>
|
||||
<span class='glyphicon glyphicon-th-list'></span> Fetch data
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import * as ko from 'knockout';
|
||||
import * as crossroads from 'crossroads';
|
||||
import * as hasher from 'hasher';
|
||||
import { routes } from './routes';
|
||||
|
||||
// This module configures crossroads.js, a routing library. If you prefer, you
|
||||
// can use any other routing library (or none at all) as Knockout is designed to
|
||||
@@ -11,23 +9,41 @@ import { routes } from './routes';
|
||||
// specifies a 'page', which is a Knockout component) - there's nothing built into
|
||||
// Knockout that requires or even knows about this technique. It's just one of
|
||||
// many possible ways of setting up client-side routes.
|
||||
|
||||
export class Router {
|
||||
public currentRoute = ko.observable<Route>({});
|
||||
private disposeHistory: () => void;
|
||||
private clickEventListener: EventListener;
|
||||
|
||||
constructor(routes: Route[]) {
|
||||
// Configure Crossroads route handlers
|
||||
constructor(history: HistoryModule.History, routes: Route[]) {
|
||||
// Reset and configure Crossroads so it matches routes and updates this.currentRoute
|
||||
crossroads.removeAllRoutes();
|
||||
crossroads.resetState();
|
||||
crossroads.normalizeFn = crossroads.NORM_AS_OBJECT;
|
||||
routes.forEach(route => {
|
||||
crossroads.addRoute(route.url, (requestParams) => {
|
||||
this.currentRoute(ko.utils.extend(requestParams, route.params));
|
||||
});
|
||||
});
|
||||
|
||||
// Activate Crossroads
|
||||
crossroads.normalizeFn = crossroads.NORM_AS_OBJECT;
|
||||
hasher.initialized.add(hash => crossroads.parse(hash));
|
||||
hasher.changed.add(hash => crossroads.parse(hash));
|
||||
hasher.init();
|
||||
// Make history.js watch for navigation and notify Crossroads
|
||||
this.disposeHistory = history.listen(location => crossroads.parse(location.pathname));
|
||||
this.clickEventListener = evt => {
|
||||
let target: any = evt.target;
|
||||
if (target && target.tagName === 'A') {
|
||||
let href = target.getAttribute('href');
|
||||
if (href && href.charAt(0) == '/') {
|
||||
history.push(href);
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('click', this.clickEventListener);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this.disposeHistory();
|
||||
document.removeEventListener('click', this.clickEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +51,3 @@ export interface Route {
|
||||
url?: string;
|
||||
params?: any;
|
||||
}
|
||||
|
||||
export function instance() {
|
||||
// Ensure there's only one instance. This is needed to support hot module replacement.
|
||||
const windowOrDefault: any = typeof window === 'undefined' ? {} : window;
|
||||
windowOrDefault._router = windowOrDefault._router || new Router(routes);
|
||||
return windowOrDefault._router;
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import * as ko from 'knockout';
|
||||
import { Route } from './router';
|
||||
|
||||
import navMenu from './components/nav-menu/nav-menu';
|
||||
import homePage from './components/home-page/home-page';
|
||||
import counterExample from './components/counter-example/counter-example';
|
||||
import fetchData from './components/fetch-data/fetch-data';
|
||||
|
||||
ko.components.register('nav-menu', navMenu);
|
||||
ko.components.register('home-page', homePage);
|
||||
ko.components.register('counter-example', counterExample);
|
||||
ko.components.register('fetch-data', fetchData);
|
||||
|
||||
export const routes: Route[] = [
|
||||
{ url: '', params: { page: 'home-page' } },
|
||||
{ url: 'counter', params: { page: 'counter-example' } },
|
||||
{ url: 'fetch-data', params: { page: 'fetch-data' } }
|
||||
];
|
||||
Reference in New Issue
Block a user