Runtime Store(後述) の全パッケージを参照
※.NET Framework 4.6.1 の場合は引き続き個別指定
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<MvcRazorCompileOnPublish>true</MvcRazorCompileOnPublish>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
</ItemGroup>
</Project>
GAC のようなもの
Windows:C:/Program Files/dotnet/store
macOS/Linux:/usr/local/share/dotnet/store
Runtime Store
App1.dll + 3rdparty.dll
App2.dll + 3rdparty.dll
dotnet new
dotnet new --install Microsoft.AspNetCore.SpaTemplates::*
Built code for client
Built code for SSPR
Bootstrapping code
Application code
Test code
Component は部品
Module は 部品を1つの機能としてまとめる仕組み
Angular アプリケーションは1つ以上の Module から構成
アプリ起動時に呼び出されるモジュールはルートモジュールと呼ばれ、慣例的に AppModule と命名される。アプリのエントリーポイント。
AppModule
Comp1
Comp2
Comp3
ルートモジュールの起動
import { AppModule } from './app/app.module.browser';
const modulePromise = platformBrowserDynamic().bootstrapModule(AppModule);
@NgModule({
bootstrap: [ AppComponent ],
imports: [
BrowserModule,
AppModuleShared
],
providers: [
{ provide: 'BASE_URL', useFactory: getBaseUrl }
]
})
export class AppModule {
}
ルーティングの設定
@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 {
}
router-outlet の部分が動的に切り替わる
<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>
URL | Tag |
---|---|
"/home" | <home></home> |
"/counter" | <counter></counter> |
"/fetch-data" | <fetchdata></fetchdata> |
"/", ”/hoge” | <home></home> |
fetchdaga.component.ts
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;
}
SampleDataController.cs
private static string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
[HttpGet("[action]")]
public IEnumerable<WeatherForecast> WeatherForecasts()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
DateFormatted = DateTime.Now.AddDays(index).ToString("d"),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
});
}
public class WeatherForecast
{
public string DateFormatted { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
public int TemperatureF
{
get
{
return 32 + (int)(TemperatureC / 0.5556);
}
}
}
Server
Views/Home/Index.cshtml の中でプリレンダリングされた main-server.js の内容で置き換え
@{
ViewData["Title"] = "Home Page";
}
<app asp-prerender-module="ClientApp/dist/main-server">Loading...</app>
<script src="~/dist/vendor.js" asp-append-version="true"></script>
@section scripts {
<script src="~/dist/main-client.js" asp-append-version="true"></script>
}
環境セットアップ、実装の複雑化
サーバーのCPU負荷が上がる
キャッシュ対策が必要になる
動的コンテンツはそもそもキャッシュしてもあまり意味がない
# Instrallation
npm install -g @angular/cli
# Create
ng new my-app
# Run
cd my-app
ng serve -o
# Build
ng build --prod
# Test
ng test
2. Run the Web API via Whack Whack Terminal and check using Postman.
3. Add dotnet watch tool to the project
Then "dotnet restore"
4. Move to the solution folder(not to the project folder)
5. Create an Angular application via Angular CLI command
6. Add the Angular project as an existing website to the solution.
7. Run the Angular project via NPM Task Runner
8. Import HttpClientModule to the AppModule
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
8. Inject HttpClient to the AppComponent and fetch data
import { Component, OnInit } from '@angular/core';
import { HttpClient } from "@angular/common/http";
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
title = 'app';
values: string[];
constructor(private _httpClient: HttpClient) { }
ngOnInit() {
this._httpClient.get<string[]>('/api/values')
.subscribe(response => this.values = response);
}
}
<ul>
<li *ngFor="let value of values">
{{ value }}
</li>
</ul>
app.component.ts
app.component.html
9. Proxy API call
{
"name": "client",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
・・・
}
{
"/api": {
"target": "http://localhost:1714",
"secure": false
}
}
package.json
poxy.conf.json
10. Release build configuration
{
"name": "client",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"ng": "ng",
"start": "ng serve -o --proxy-config proxy.conf.json",
"build": "ng build",
"build:prod": "ng build --prod",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
・・・
}
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "client"
},
"apps": [
{
"root": "src",
"outDir": "../Server/wwwroot",
"assets": [
"assets",
"favicon.ico"
],
・・・
}
package.json
angular-cli.json
11. SPA Routing
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}");
routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" });
});
}
Startup.cs
12. Add HomeController.cs and always return index.html
public class HomeController : Controller
{
// GET: /<controller>/
public IActionResult Index()
{
return File("/index.html", "text/html");
}
}
HomeController.cs
13. Deploy to Azure
http://netconf2017tokyo.azurewebsites.net/