From 515c659a474ba8b6be694714909a501de80b7dba Mon Sep 17 00:00:00 2001 From: SteveSandersonMS Date: Tue, 8 Mar 2016 15:56:43 +0000 Subject: [PATCH] Migrate from hasher.js to history.js to support HTML5-style navigation. Also clean up the HMR support. --- templates/KnockoutSpa/ClientApp/boot.ts | 20 +- .../components/app-layout/app-layout.ts | 8 - .../app-root.html} | 0 .../ClientApp/components/app-root/app-root.ts | 39 +++ .../components/nav-menu/nav-menu.html | 8 +- templates/KnockoutSpa/ClientApp/router.ts | 43 ++-- templates/KnockoutSpa/ClientApp/routes.ts | 18 -- templates/KnockoutSpa/Views/Home/Index.cshtml | 2 +- templates/KnockoutSpa/package.json | 2 +- templates/KnockoutSpa/tsd.json | 21 +- .../KnockoutSpa/typings/hasher/hasher.d.ts | 116 --------- .../KnockoutSpa/typings/history/history.d.ts | 61 +++++ .../typings/react-router/history.d.ts | 233 ++++++++++++++++++ templates/KnockoutSpa/typings/tsd.d.ts | 9 +- 14 files changed, 390 insertions(+), 190 deletions(-) delete mode 100644 templates/KnockoutSpa/ClientApp/components/app-layout/app-layout.ts rename templates/KnockoutSpa/ClientApp/components/{app-layout/app-layout.html => app-root/app-root.html} (100%) create mode 100644 templates/KnockoutSpa/ClientApp/components/app-root/app-root.ts delete mode 100644 templates/KnockoutSpa/ClientApp/routes.ts delete mode 100644 templates/KnockoutSpa/typings/hasher/hasher.d.ts create mode 100644 templates/KnockoutSpa/typings/history/history.d.ts create mode 100644 templates/KnockoutSpa/typings/react-router/history.d.ts diff --git a/templates/KnockoutSpa/ClientApp/boot.ts b/templates/KnockoutSpa/ClientApp/boot.ts index e15ef9b..e0346aa 100644 --- a/templates/KnockoutSpa/ClientApp/boot.ts +++ b/templates/KnockoutSpa/ClientApp/boot.ts @@ -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 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((ko).components._allRegisteredComponents).forEach(componentName => { - ko.components.unregister(componentName); - }); - }); - module.hot.accept(); + module.hot.accept(); + module.hot.dispose(() => ko.cleanNode(document.body)); } diff --git a/templates/KnockoutSpa/ClientApp/components/app-layout/app-layout.ts b/templates/KnockoutSpa/ClientApp/components/app-layout/app-layout.ts deleted file mode 100644 index 79bc9e8..0000000 --- a/templates/KnockoutSpa/ClientApp/components/app-layout/app-layout.ts +++ /dev/null @@ -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') }; diff --git a/templates/KnockoutSpa/ClientApp/components/app-layout/app-layout.html b/templates/KnockoutSpa/ClientApp/components/app-root/app-root.html similarity index 100% rename from templates/KnockoutSpa/ClientApp/components/app-layout/app-layout.html rename to templates/KnockoutSpa/ClientApp/components/app-root/app-root.html diff --git a/templates/KnockoutSpa/ClientApp/components/app-root/app-root.ts b/templates/KnockoutSpa/ClientApp/components/app-root/app-root.ts new file mode 100644 index 0000000..bf8cd8f --- /dev/null +++ b/templates/KnockoutSpa/ClientApp/components/app-root/app-root.ts @@ -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; + 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((ko).components._allRegisteredComponents).forEach(componentName => { + ko.components.unregister(componentName); + }); + } +} + +export default { viewModel: AppRootViewModel, template: require('./app-root.html') }; diff --git a/templates/KnockoutSpa/ClientApp/components/nav-menu/nav-menu.html b/templates/KnockoutSpa/ClientApp/components/nav-menu/nav-menu.html index 707a116..d5c1691 100644 --- a/templates/KnockoutSpa/ClientApp/components/nav-menu/nav-menu.html +++ b/templates/KnockoutSpa/ClientApp/components/nav-menu/nav-menu.html @@ -7,23 +7,23 @@ - WebApplicationBasic + WebApplicationBasic