mirror of
https://github.com/aspnet/JavaScriptServices.git
synced 2025-12-23 01:58:29 +00:00
Fix templates directory structure to produce correct nupkg output
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { AppModuleShared } from './app.module.shared';
|
||||
import { AppComponent } from './components/app/app.component';
|
||||
|
||||
@NgModule({
|
||||
bootstrap: [ AppComponent ],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppModuleShared
|
||||
],
|
||||
providers: [
|
||||
{ provide: 'BASE_URL', useFactory: getBaseUrl }
|
||||
]
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
|
||||
export function getBaseUrl() {
|
||||
return document.getElementsByTagName('base')[0].href;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ServerModule } from '@angular/platform-server';
|
||||
import { AppModuleShared } from './app.module.shared';
|
||||
import { AppComponent } from './components/app/app.component';
|
||||
|
||||
@NgModule({
|
||||
bootstrap: [ AppComponent ],
|
||||
imports: [
|
||||
ServerModule,
|
||||
AppModuleShared
|
||||
]
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { AppComponent } from './components/app/app.component';
|
||||
import { NavMenuComponent } from './components/navmenu/navmenu.component';
|
||||
import { HomeComponent } from './components/home/home.component';
|
||||
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
|
||||
import { CounterComponent } from './components/counter/counter.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
NavMenuComponent,
|
||||
CounterComponent,
|
||||
FetchDataComponent,
|
||||
HomeComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
HttpModule,
|
||||
FormsModule,
|
||||
RouterModule.forRoot([
|
||||
{ path: '', redirectTo: 'home', pathMatch: 'full' },
|
||||
{ path: 'home', component: HomeComponent },
|
||||
{ path: 'counter', component: CounterComponent },
|
||||
{ path: 'fetch-data', component: FetchDataComponent },
|
||||
{ path: '**', redirectTo: 'home' }
|
||||
])
|
||||
]
|
||||
})
|
||||
export class AppModuleShared {
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
@media (max-width: 767px) {
|
||||
/* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */
|
||||
.body-content {
|
||||
padding-top: 50px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<div class='container-fluid'>
|
||||
<div class='row'>
|
||||
<div class='col-sm-3'>
|
||||
<nav-menu></nav-menu>
|
||||
</div>
|
||||
<div class='col-sm-9 body-content'>
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<h1>Counter</h1>
|
||||
|
||||
<p>This is a simple example of an Angular component.</p>
|
||||
|
||||
<p>Current count: <strong>{{ currentCount }}</strong></p>
|
||||
|
||||
<button (click)="incrementCounter()">Increment</button>
|
||||
@@ -0,0 +1,29 @@
|
||||
/// <reference path="../../../../node_modules/@types/jasmine/index.d.ts" />
|
||||
import { assert } from 'chai';
|
||||
import { CounterComponent } from './counter.component';
|
||||
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
|
||||
|
||||
let fixture: ComponentFixture<CounterComponent>;
|
||||
|
||||
describe('Counter component', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({ declarations: [CounterComponent] });
|
||||
fixture = TestBed.createComponent(CounterComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should display a title', async(() => {
|
||||
const titleText = fixture.nativeElement.querySelector('h1').textContent;
|
||||
expect(titleText).toEqual('Counter');
|
||||
}));
|
||||
|
||||
it('should start with count 0, then increments by 1 when clicked', async(() => {
|
||||
const countElement = fixture.nativeElement.querySelector('strong');
|
||||
expect(countElement.textContent).toEqual('0');
|
||||
|
||||
const incrementButton = fixture.nativeElement.querySelector('button');
|
||||
incrementButton.click();
|
||||
fixture.detectChanges();
|
||||
expect(countElement.textContent).toEqual('1');
|
||||
}));
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'counter',
|
||||
templateUrl: './counter.component.html'
|
||||
})
|
||||
export class CounterComponent {
|
||||
public currentCount = 0;
|
||||
|
||||
public incrementCounter() {
|
||||
this.currentCount++;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<h1>Weather forecast</h1>
|
||||
|
||||
<p>This component demonstrates fetching data from the server.</p>
|
||||
|
||||
<p *ngIf="!forecasts"><em>Loading...</em></p>
|
||||
|
||||
<table class='table' *ngIf="forecasts">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Temp. (C)</th>
|
||||
<th>Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let forecast of forecasts">
|
||||
<td>{{ forecast.dateFormatted }}</td>
|
||||
<td>{{ forecast.temperatureC }}</td>
|
||||
<td>{{ forecast.temperatureF }}</td>
|
||||
<td>{{ forecast.summary }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import { Http } from '@angular/http';
|
||||
|
||||
@Component({
|
||||
selector: 'fetchdata',
|
||||
templateUrl: './fetchdata.component.html'
|
||||
})
|
||||
export class FetchDataComponent {
|
||||
public forecasts: WeatherForecast[];
|
||||
|
||||
constructor(http: Http, @Inject('BASE_URL') baseUrl: string) {
|
||||
http.get(baseUrl + 'api/SampleData/WeatherForecasts').subscribe(result => {
|
||||
this.forecasts = result.json() as WeatherForecast[];
|
||||
}, error => console.error(error));
|
||||
}
|
||||
}
|
||||
|
||||
interface WeatherForecast {
|
||||
dateFormatted: string;
|
||||
temperatureC: number;
|
||||
temperatureF: number;
|
||||
summary: string;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<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='https://angular.io/'>Angular</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>Server-side prerendering</strong>. For faster initial loading and improved SEO, your Angular app is prerendered on the server. The resulting HTML is then transferred to the browser where a client-side copy of the app takes over.</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 Angular app will be rebuilt and a new instance injected 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,8 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'home',
|
||||
templateUrl: './home.component.html'
|
||||
})
|
||||
export class HomeComponent {
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
li .glyphicon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* Highlighting rules for nav menu items */
|
||||
li.link-active a,
|
||||
li.link-active a:hover,
|
||||
li.link-active a: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 (min-width: 768px) {
|
||||
/* On small screens, convert the nav menu to a vertical sidebar */
|
||||
.main-nav {
|
||||
height: 100%;
|
||||
width: calc(25% - 20px);
|
||||
}
|
||||
.navbar {
|
||||
border-radius: 0px;
|
||||
border-width: 0px;
|
||||
height: 100%;
|
||||
}
|
||||
.navbar-header {
|
||||
float: none;
|
||||
}
|
||||
.navbar-collapse {
|
||||
border-top: 1px solid #444;
|
||||
padding: 0px;
|
||||
}
|
||||
.navbar ul {
|
||||
float: none;
|
||||
}
|
||||
.navbar li {
|
||||
float: none;
|
||||
font-size: 15px;
|
||||
margin: 6px;
|
||||
}
|
||||
.navbar li a {
|
||||
padding: 10px 16px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.navbar a {
|
||||
/* If a menu item's text is too long, truncate it */
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
@@ -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' [routerLink]="['/home']">AngularSpa</a>
|
||||
</div>
|
||||
<div class='clearfix'></div>
|
||||
<div class='navbar-collapse collapse'>
|
||||
<ul class='nav navbar-nav'>
|
||||
<li [routerLinkActive]="['link-active']">
|
||||
<a [routerLink]="['/home']">
|
||||
<span class='glyphicon glyphicon-home'></span> Home
|
||||
</a>
|
||||
</li>
|
||||
<li [routerLinkActive]="['link-active']">
|
||||
<a [routerLink]="['/counter']">
|
||||
<span class='glyphicon glyphicon-education'></span> Counter
|
||||
</a>
|
||||
</li>
|
||||
<li [routerLinkActive]="['link-active']">
|
||||
<a [routerLink]="['/fetch-data']">
|
||||
<span class='glyphicon glyphicon-th-list'></span> Fetch data
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'nav-menu',
|
||||
templateUrl: './navmenu.component.html',
|
||||
styleUrls: ['./navmenu.component.css']
|
||||
})
|
||||
export class NavMenuComponent {
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'reflect-metadata';
|
||||
import 'zone.js';
|
||||
import 'bootstrap';
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app/app.module.browser';
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept();
|
||||
module.hot.dispose(() => {
|
||||
// Before restarting the app, we create a new root element and dispose the old one
|
||||
const oldRootElem = document.querySelector('app');
|
||||
const newRootElem = document.createElement('app');
|
||||
oldRootElem!.parentNode!.insertBefore(newRootElem, oldRootElem);
|
||||
modulePromise.then(appModule => appModule.destroy());
|
||||
});
|
||||
} else {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
// Note: @ng-tools/webpack looks for the following expression when performing production
|
||||
// builds. Don't change how this line looks, otherwise you may break tree-shaking.
|
||||
const modulePromise = platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
@@ -0,0 +1,38 @@
|
||||
import 'reflect-metadata';
|
||||
import 'zone.js';
|
||||
import 'rxjs/add/operator/first';
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { enableProdMode, ApplicationRef, NgZone, ValueProvider } from '@angular/core';
|
||||
import { platformDynamicServer, PlatformState, INITIAL_CONFIG } from '@angular/platform-server';
|
||||
import { createServerRenderer, RenderResult } from 'aspnet-prerendering';
|
||||
import { AppModule } from './app/app.module.server';
|
||||
|
||||
enableProdMode();
|
||||
|
||||
export default createServerRenderer(params => {
|
||||
const providers = [
|
||||
{ provide: INITIAL_CONFIG, useValue: { document: '<app></app>', url: params.url } },
|
||||
{ provide: APP_BASE_HREF, useValue: params.baseUrl },
|
||||
{ provide: 'BASE_URL', useValue: params.origin + params.baseUrl },
|
||||
];
|
||||
|
||||
return platformDynamicServer(providers).bootstrapModule(AppModule).then(moduleRef => {
|
||||
const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
|
||||
const state = moduleRef.injector.get(PlatformState);
|
||||
const zone = moduleRef.injector.get(NgZone);
|
||||
|
||||
return new Promise<RenderResult>((resolve, reject) => {
|
||||
zone.onError.subscribe((errorInfo: any) => reject(errorInfo));
|
||||
appRef.isStable.first(isStable => isStable).subscribe(() => {
|
||||
// Because 'onStable' fires before 'onError', we have to delay slightly before
|
||||
// completing the request in case there's an error to report
|
||||
setImmediate(() => {
|
||||
resolve({
|
||||
html: state.renderToString()
|
||||
});
|
||||
moduleRef.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
// Load required polyfills and testing libraries
|
||||
import 'reflect-metadata';
|
||||
import 'zone.js';
|
||||
import 'zone.js/dist/long-stack-trace-zone';
|
||||
import 'zone.js/dist/proxy.js';
|
||||
import 'zone.js/dist/sync-test';
|
||||
import 'zone.js/dist/jasmine-patch';
|
||||
import 'zone.js/dist/async-test';
|
||||
import 'zone.js/dist/fake-async-test';
|
||||
import * as testing from '@angular/core/testing';
|
||||
import * as testingBrowser from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
// There's no typing for the `__karma__` variable. Just declare it as any
|
||||
declare var __karma__: any;
|
||||
declare var require: any;
|
||||
|
||||
// Prevent Karma from running prematurely
|
||||
__karma__.loaded = function () {};
|
||||
|
||||
// First, initialize the Angular testing environment
|
||||
testing.getTestBed().initTestEnvironment(
|
||||
testingBrowser.BrowserDynamicTestingModule,
|
||||
testingBrowser.platformBrowserDynamicTesting()
|
||||
);
|
||||
|
||||
// Then we find all the tests
|
||||
const context = require.context('../', true, /\.spec\.ts$/);
|
||||
|
||||
// And load the modules
|
||||
context.keys().map(context);
|
||||
|
||||
// Finally, start Karma to run the tests
|
||||
__karma__.start();
|
||||
@@ -0,0 +1,26 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/0.13/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '.',
|
||||
frameworks: ['jasmine'],
|
||||
files: [
|
||||
'../../wwwroot/dist/vendor.js',
|
||||
'./boot-tests.ts'
|
||||
],
|
||||
preprocessors: {
|
||||
'./boot-tests.ts': ['webpack']
|
||||
},
|
||||
reporters: ['progress'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
mime: { 'application/javascript': ['ts','tsx'] },
|
||||
singleRun: false,
|
||||
webpack: require('../../webpack.config.js')().filter(config => config.target !== 'node'), // Test against client bundle, because tests run in a browser
|
||||
webpackMiddleware: { stats: 'errors-only' }
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user