mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-25 11:07:29 +00:00
Initial KnockoutSpa template
This commit is contained in:
23
templates/KnockoutSpa/ClientApp/boot.ts
Normal file
23
templates/KnockoutSpa/ClientApp/boot.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
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';
|
||||
|
||||
ko.components.register('app-layout', appLayout);
|
||||
ko.applyBindings();
|
||||
|
||||
// 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();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class='container-fluid'>
|
||||
<div class='row'>
|
||||
<div class='col-sm-3'>
|
||||
<nav-menu params='route: route'></nav-menu>
|
||||
</div>
|
||||
<div class='col-sm-9' data-bind='component: { name: route().page, params: route }'></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
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,7 @@
|
||||
<h2>Counter</h2>
|
||||
|
||||
<p>This is a simple example of a Knockout component.</p>
|
||||
|
||||
<p>Current count: <strong data-bind='text: currentCount'></strong></p>
|
||||
|
||||
<button data-bind='click: incrementCounter'>Increment</button>
|
||||
@@ -0,0 +1,12 @@
|
||||
import * as ko from 'knockout';
|
||||
|
||||
class CounterExampleViewModel {
|
||||
public currentCount = ko.observable(0);
|
||||
|
||||
public incrementCounter() {
|
||||
let prevCount = this.currentCount();
|
||||
this.currentCount(prevCount + 1);
|
||||
}
|
||||
}
|
||||
|
||||
export default { viewModel: CounterExampleViewModel, template: require('./counter-example.html') };
|
||||
@@ -0,0 +1,24 @@
|
||||
<h1>Weather forecast</h1>
|
||||
|
||||
<p>This component demonstrates fetching data from the server.</p>
|
||||
|
||||
<p data-bind='ifnot: forecasts'><em>Loading...</em></p>
|
||||
|
||||
<table class='table' data-bind='if: forecasts'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Temp. (C)</th>
|
||||
<th>Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-bind='foreach: forecasts'>
|
||||
<tr>
|
||||
<td data-bind='text: dateFormatted'></td>
|
||||
<td data-bind='text: temperatureC'></td>
|
||||
<td data-bind='text: temperatureF'></td>
|
||||
<td data-bind='text: summary'></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -0,0 +1,22 @@
|
||||
import * as ko from 'knockout';
|
||||
|
||||
interface WeatherForecast {
|
||||
dateFormatted: string;
|
||||
temperatureC: number;
|
||||
temperatureF: number;
|
||||
summary: string;
|
||||
}
|
||||
|
||||
class FetchDataViewModel {
|
||||
public forecasts = ko.observableArray<WeatherForecast>();
|
||||
|
||||
constructor() {
|
||||
fetch('/api/SampleData/WeatherForecasts')
|
||||
.then(response => response.json())
|
||||
.then((data: WeatherForecast[]) => {
|
||||
this.forecasts(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default { viewModel: FetchDataViewModel, template: require('./fetch-data.html') };
|
||||
@@ -0,0 +1,15 @@
|
||||
<h1>Hello, world!</h1>
|
||||
<p>Welcome to your new single-page application, built with:</p>
|
||||
<ul>
|
||||
<li><a href='https://get.asp.net/'>ASP.NET Core</a> and <a href='https://msdn.microsoft.com/en-us/library/67ef8sbd.aspx'>C#</a> for cross-platform server-side code</li>
|
||||
<li><a href='http://knockoutjs.com/'>Knockout.js</a> and <a href='http://www.typescriptlang.org/'>TypeScript</a> for client-side code</li>
|
||||
<li><a href='https://webpack.github.io/'>Webpack</a> for building and bundling client-side resources</li>
|
||||
<li><a href='http://getbootstrap.com/'>Bootstrap</a> for layout and styling</li>
|
||||
</ul>
|
||||
<p>To help you get started, we've also set up:</p>
|
||||
<ul>
|
||||
<li><strong>Client-side navigation</strong>. For example, click <em>Counter</em> then <em>Back</em> to return here.</li>
|
||||
<li><strong>Webpack dev middleware</strong>. In development mode, there's no need to run the <code>webpack</code> build tool. Your client-side resources are dynamically built on demand. Updates are available as soon as you modify any file.</li>
|
||||
<li><strong>Hot module replacement</strong>. In development mode, you don't even need to reload the page after making most changes. Within seconds of saving changes to files, your Knockout app will be rebuilt and a new instance injected is into the page.</li>
|
||||
<li><strong>Efficient production builds</strong>. In production mode, development-time features are disabled, and the <code>webpack</code> build tool produces minified static CSS and JavaScript files.</li>
|
||||
</ul>
|
||||
@@ -0,0 +1,6 @@
|
||||
import * as ko from 'knockout';
|
||||
|
||||
class HomePageViewModel {
|
||||
}
|
||||
|
||||
export default { viewModel: HomePageViewModel, template: require('./home-page.html') };
|
||||
@@ -0,0 +1,33 @@
|
||||
<div class='main-nav'>
|
||||
<div class='navbar navbar-inverse'>
|
||||
<div class='navbar-header'>
|
||||
<button type='button' class='navbar-toggle' data-toggle='collapse' data-target='.navbar-collapse'>
|
||||
<span class='sr-only'>Toggle navigation</span>
|
||||
<span class='icon-bar'></span>
|
||||
<span class='icon-bar'></span>
|
||||
<span class='icon-bar'></span>
|
||||
</button>
|
||||
<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" }'>
|
||||
<span class='glyphicon glyphicon-home'></span> Home
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<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" }'>
|
||||
<span class='glyphicon glyphicon-th-list'></span> Fetch data
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
import * as ko from 'knockout';
|
||||
import { Route } from '../../router';
|
||||
|
||||
interface NavMenuParams {
|
||||
route: KnockoutObservable<Route>;
|
||||
}
|
||||
|
||||
class NavMenuViewModel {
|
||||
public route: KnockoutObservable<Route>;
|
||||
|
||||
constructor(params: NavMenuParams) {
|
||||
// This viewmodel doesn't do anything except pass through the 'route' parameter to the view.
|
||||
// You could remove this viewmodel entirely, and define 'nav-menu' as a template-only component.
|
||||
// But in most apps, you'll want some viewmodel logic to determine what navigation options appear.
|
||||
this.route = params.route;
|
||||
}
|
||||
}
|
||||
|
||||
export default { viewModel: NavMenuViewModel, template: require('./nav-menu.html') };
|
||||
66
templates/KnockoutSpa/ClientApp/css/site.css
Executable file
66
templates/KnockoutSpa/ClientApp/css/site.css
Executable file
@@ -0,0 +1,66 @@
|
||||
.main-nav li .glyphicon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* Highlighting rules for nav menu items */
|
||||
.main-nav li a.active,
|
||||
.main-nav li a.active:hover,
|
||||
.main-nav li a.active:focus {
|
||||
background-color: #4189C7;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Keep the nav menu independent of scrolling and on top of other items */
|
||||
.main-nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
/* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */
|
||||
body {
|
||||
padding-top: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
/* On small screens, convert the nav menu to a vertical sidebar */
|
||||
.main-nav {
|
||||
height: 100%;
|
||||
width: calc(25% - 20px);
|
||||
}
|
||||
.main-nav .navbar {
|
||||
border-radius: 0px;
|
||||
border-width: 0px;
|
||||
height: 100%;
|
||||
}
|
||||
.main-nav .navbar-header {
|
||||
float: none;
|
||||
}
|
||||
.main-nav .navbar-collapse {
|
||||
border-top: 1px solid #444;
|
||||
padding: 0px;
|
||||
}
|
||||
.main-nav .navbar ul {
|
||||
float: none;
|
||||
}
|
||||
.main-nav .navbar li {
|
||||
float: none;
|
||||
font-size: 15px;
|
||||
margin: 6px;
|
||||
}
|
||||
.main-nav .navbar li a {
|
||||
padding: 10px 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.main-nav .navbar a {
|
||||
/* If a menu item's text is too long, truncate it */
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
44
templates/KnockoutSpa/ClientApp/router.ts
Normal file
44
templates/KnockoutSpa/ClientApp/router.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
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
|
||||
// compose cleanly with external libraries.
|
||||
//
|
||||
// You *don't* have to follow the pattern established here (each route entry
|
||||
// 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>({});
|
||||
|
||||
constructor(routes: Route[]) {
|
||||
// Configure Crossroads route handlers
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
18
templates/KnockoutSpa/ClientApp/routes.ts
Normal file
18
templates/KnockoutSpa/ClientApp/routes.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
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