第五課 Angular基礎之四
Service與Routing
期中考檢討
元件版型 (template)
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
<div class="navbar-brand">
<img [src]="logo" />
{{ title }}
</div>
<!--略 -->
</nav>
import { Component } from '@angular/core';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css']
})
export class NavbarComponent {
title = '教士會館';
logo = '/assets/images/house_icon_white.png';
constructor() { }
}
元件 (component)
❷ 屬性繫結: 讀取TS檔定義的屬性
❶ 內插: 讀取TS檔定義的屬性
❶
❷
屬性繫結使用時機?
[引數]="屬性名稱"
元件版型 (template)
<div class="col-md-5">
<agm-map [latitude]="location.lat" [longitude]="location.lng" [zoom]="15">
<agm-marker [latitude]="lat2" [longitude]="lng2"></agm-marker>
</agm-map>
</div>
import { Component } from '@angular/core';
@Component({
selector: 'app-contact',
templateUrl: './contact.component.html',
styleUrls: ['./contact.component.css']
})
export class ContactComponent {
title = 'My first AGM project'; // 純量: 字串
lat2 = 25.174926; // 純量: 浮點數
lng2 = 121.4336303;
location = { // 物件: 兩個欄位
lat: 25.174926,
lng: 121.4336303
};
}
元件 (component)
屬性繫結: 純量屬性
屬性繫結: 物件.欄位名稱
元件版型 (template)
<ul class="navbar-nav ml-auto">
<li class="nav-item mr-5 display-5" *ngFor="let m of menu">
<a href="#" class="nav-link">{{ m }}</a>
</li>
</ul>
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'OnePageSite'; // 純量: 字串
menu = [ // 字串陣列
'回到首頁',
'鄰近景點',
'豐富菜色',
'營業項目',
'聯絡我們',
];
}
元件 (component)
內插: 使用迴圈變數
迴圈語法
❷ 陣列名稱
❶ 自訂迴圈變數
?
❷
元件版型 (template)
<ul class="navbar-nav ml-auto">
<li class="nav-item mr-5 display-5" *ngFor="let m of myMenu">
<a href="{{ m.url }}" class="nav-link">{{ m.title }}</a>
</li>
</ul>
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'OnePageSite'; // 純量: 字串
myMenu = [ // 物件陣列: 兩個欄位
{ title: '回到首頁', url: '#'},
{ title: '鄰近景點', url: '#hero-section'},
{ title: '豐富菜色', url: '#feature-section'},
{ title: '營業項目', url: '#product-section'},
{ title: '聯絡我們', url: '#contact-section'}
];
}
元件 (component)
內插: 使用迴圈變數
迴圈語法
❷ 陣列名稱
❶ 自訂迴圈變數
<!-- 內插 或 屬性繫結? -->
<a href="{{ m.url }}"> 內插
<a [href]="m.url"> 屬性繫結
❷
title = 'OnePageSite';
url = 'http://au.edu.tw/au4163';
price = 300;
內插
屬性繫結
<div class="container">
<h1>{{ title }}</h1>
</div>
<div class="jumbotron">
<img [src]="url" />
</div>
事件繫結
雙向繫結
元件版型 (template)
<div class="m-5">
<button class="btn btn-lg" [class]="btnColor" (click)="clickme()">
點選送出
</button>
</div>
import { Component } from '@angular/core';
// ...[略]...
export class AppComponent {
btnColor = 'btn-success'; // BS4: 綠色按鈕
clickme() { // <-- 事件處理器
if (this.btnColor === 'btn-success') {
this.btnColor = 'btn-primary'; // BS4: 藍色按鈕
} else {
this.btnColor = 'btn-success';
}
}
}
元件 (component)
事件繫結
❷ 指定處理器
❶ 綁定click事件
屬性繫結
按鈕顏色參考資料: Bootstrap 4官網 Buttons
❸ 撰寫處理器
元件版型 (template)
<div class="m-5">時段選擇:
<select (change)="changeOption($event)">
<option *ngFor="let day of weekdays">{{ day }}</option>
</select>
</div>
<div>你選擇的項目是: {{ choice }}</div>
import { Component } from '@angular/core';
// ...[略]...
export class AppComponent {
weekdays = ['星期二', '星期三', '星期四', '星期五'];
choice = this.weekdays[0];
changeOption(e) { // 事件處理器
console.log('已更換選項', e);
this.choice = e.target.value;
}
}
元件 (component)
事件繫結
❷ 指定處理器
❶ 綁定change事件
按鈕顏色參考資料: Bootstrap 4官網 Buttons
❹ 撰寫處理器
❸ 系統變數$event為參數
changeOption(e) { // 事件處理器
console.log('已更換選項', e);
this.choice = e.target.value;
}
內插
屬性繫結
<div class="m-5">時段選擇:
<select (change)="changeOption($event)">
<option">時段A</option>
<option">時段B</option>
</select>
</div>
事件繫結
雙向繫結
資料傳送: 需倚賴設定參數
資料服務 功能服務
元件
TS檔
HTML檔
屬性繫結
事件繫結
資料存取物件
如何設計為佳?
資料庫
資料模型TS
資料介面TS
資料服務
資料庫
實作
TS檔
使用
schema
對應
存取
元件
TS檔
HTML檔
屬性繫結
事件繫結
注入
呼叫服務
資料存取設計模式
❺
❶
❷
❸
❹
事件
屬性
app.component.html
app.component.ts
資料服務
功能服務
ng g service 服務名稱
注入器
資料服務
功能服務
export class HeroService {
log(msg: any) { console.log(msg); }
error(msg: any) { console.error(msg); }
warn(msg: any) { console.warn(msg); }
}
hero.service.ts
app.component.ts
import { HeroService } from './hero.service';
constructor(public hs: HeroServicve){
this.hs.log('log服務');
}
資料模型TS
資料介面TS
資料服務
資料庫
實作
TS檔
使用
schema
對應
存取
元件
TS檔
HTML檔
屬性繫結
事件繫結
注入
呼叫服務
Ⓐ 新增_model資料夾
新增「資料模型.ts檔」
Ⓑ 在_model資料夾中
新增「資料界面.ts檔」
Ⓒ 建立服務
ng g service 服務名稱
實作資料存取
Ⓓ 使用服務
export class MenuItem {
title: string; // 共兩個欄位
url: string;
constructor(t, u) { // 建立物件
this.title = t;
this.url = u;
}
// 另外可提供getTitle(), setTitle()等methods
}
資料模型:選單項目
Ⓐ資料模型
資料模型檔:新增_model資料夾,新增menuitem.ts
❶ 為class檔,提供欄位定義
❷ 提供建立物件功能
❸ 提供個別欄位存取功能
import { MenuItem } from './menuitem';
export interface Menu {
items: MenuItem[]; // 選單項目陣列
getMenu(): MenuItem[]; // 回傳選單
}
資料介面:選單
在_model資料夾,繼續新增menu.ts
❶ 引入選單項目if necessary
❷ 設定介面所需欄位此處只需一欄
❸ 設定介面應提供功能此處只有一個功能
Ⓑ 資料界面
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class MenuService {
constructor() { }
}
Angular服務檔:預設內容
❶ 新增服務
❶ Injectable: 可注射(注入其他元件)
Ⓒ 建立服務
ng g service _services/menu
❷ 'root': 代表所有元件都可用不需做其他設定
import { Injectable } from '@angular/core';
import { Menu } from '../_model/menu';
@Injectable({
providedIn: 'root'
})
export class MenuService implements Menu {
// 需定義介面設定的欄位
constructor() {
}
// 需實作介面定義的所有函式
}
❷ 實作介面
❶ 引入資料介面檔
Ⓒ 建立服務
❷ implements: 實作介面檔
❸
❹
❺ 需建立資料(從資料來源讀取)
import { MenuItem } from './../_model/menuitem';
import { Injectable } from '@angular/core';
import { Menu } from '../_model/menu';
@Injectable({
providedIn: 'root'
})
export class MenuService implements Menu {
items: MenuItem[];
constructor() {
this.items = [
new MenuItem('回到首頁', '#'),
new MenuItem('鄰近景點', '#hero-section'),
new MenuItem('豐富菜色', '#feature-section'),
new MenuItem('營業項目', '#product-section'),
new MenuItem('聯絡我們', '#contact-section'),
];
}
getMenu(): MenuItem[] {
return this.items;
}
}
❷ 實作介面
❸ 加入介面定義的欄位
Ⓒ 建立服務
❸ 因欄位需要,故引入
❺ 建立資料(資料來源: 直接設定)
❹ 實作介面定義的所有函式
import { MenuService } from './../_services/menu.service';
import { Component, OnInit } from '@angular/core';
import { MenuItem } from '../_model/menuitem';
@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.css']
})
export class NavbarComponent implements OnInit {
title = '教士會館';
newMenu: MenuItem[];
logo = '/assets/images/house_icon_white.png';
constructor(public ms: MenuService) { }
ngOnInit() {
this.newMenu = this.ms.getMenu();
}
}
使用服務
❶ 注射服務: 在建構子中定義引數
❶ 引入
❷ 使用服務提供的函式
Ⓓ 使用服務
public 引數名稱: 服務名稱
元件 (component)
<!-- 省略 -->
<ul class="navbar-nav ml-auto">
<li class="nav-item mr-5" *ngFor="let menuitem of newMenu">
<a href="{{ menuitem.url }}" class="nav-link">
{{menuitem.title}}
</a>
</li>
</ul>
<!-- 省略 -->
使用服務
屬性繫結
Ⓓ 使用服務
版型 (component)
建立元件、指令
綁定的資料變數內容有所變動
<input [(ng-model)]="name">
元件初始化,僅執行一次!
清除元件,歸還記憶體空間
import { Component, OnInit } from '@angular/core';
...
export class NavbarComponent implements OnInit{
...
constructor(private data: DataService) {}
ngOnInit(): void {
this.data.getSiteSettings().then(value=> this.settings = value);
}
}
1. import OnInit
2. 無回傳值 (void)
為下列Class建立 DAO
//....
export class FeatureComponent implements OnInit {
food: Food[] = [
{name:"素猴菇三杯雞(蛋奶素)", price: 300, img:"/assets/images/mushroom.jpg"},
{name:"養生藥膳花雕雞",price: 300, img:"/assets/images/soup.jpg"},
{name:"北海道鮭魚定食",price: 340, img:"/assets/images/fish.jpg"},
];
...
}
單頁面
about.component.ts
+
about.component.html
contact.component.ts
+
contact.component.html
範例:兩個頁面
新建第二個頁面
ng g component intro
ng g component home
ng g component intro
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-intro',
templateUrl: './intro.component.html',
styleUrls: ['./intro.component.css']
})
export class IntroComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
<nav aria-label="breadcrumb" role="navigation">
<div class="container">
<ol class="breadcrumb bg-light">
<li class="breadcrumb-item"><a href="#">首頁</a></li>
<li class="breadcrumb-item active" aria-current="page">關於我們</li>
</ol>
</div>
</nav>
<div class="container">
<div class="card text-center my-5">
<div class="card-header">
<h1 class="display-4">真理大學教士會館</h1>
</div>
<img class="card-img-top" src="/assets/images/house_outline.jpg">
<div class="card-body">
<p style="font-size: 1.25rem">教士會館前身為宣教士宿舍建於1875年,與馬偕故居同時建造,由馬偕博士親自設計監造,以作為宣教士住宿之用,因外型白色又稱「馬偕的小白宮」。原與馬偕故居為同型建築,二次大戰後租給德記洋行時加蓋了二樓,並在後院蓋了一座翹脊古厝。 馬偕時代住過這裏的有華雅各、閏虔益、黎約翰、吳威廉等助理牧師。日治時代偉彼得牧師住得最久,因此有「偉牧師館」之名。後來被淡江中學租為學寮稱為「玄武寮」。1965年以後為真理大學教士會館。
</p>
</div>
</div>
</div>
注意此元件的
intro.component.html
新的Bootstrap元件
breadcrumb
intro.component.ts
.jumbotron{
position: relative;
background: #000 url("/assets/images/house_outline.jpg") center center;
width: 100%;
height: 800px;
background-size: cover;
overflow: hidden;
}
<nav aria-label="breadcrumb" role="navigation">
<div class="container">
<ol class="breadcrumb bg-light">
<li class="breadcrumb-item"><a href="#">首頁</a></li>
<li class="breadcrumb-item active" aria-current="page">
關於我們
</li>
</ol>
</div>
</nav>
Breadcrumb元件
intro.component.css
ng g component home
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
<app-navbar></app-navbar>
<app-jumbo></app-jumbo>
<app-hero></app-hero>
<app-feature></app-feature>
<app-product></app-product>
<app-contact></app-contact>
將原來 AppComponent的內容改放到HomeComponent
home.component.html
AppComponent版型也貼到HomeComponent版型
home.component.ts
ng new新建專案時,第一個問題回答Y,自動產生 app-routing.module.ts
Router模組:AppRoutingModule
...
import { HomeComponent } from './app.component';
import { IntroComponent } from './intro/intro.component';
const routes: Routes = [
{path: '', component: AppComponent},
{path: 'introduction', component: IntroComponent}
];
摘錄app-routing.module.ts
路徑陣列
每一對路徑至少包含兩個屬性:
(1) path: url相對路徑 (例:http://localhost:4200/introduction)
(2) component: 要執行哪一個元件
...
<router-outlet></router-outlet>
...
定義路徑
app.component.html
以http://localhost:4200/introduction 為例:
IntroComponent的HTML內容會顯示在
<router-outlet></router-outlet>之間
...
const routes: Routes = [
{path: '', component: AppComponent},
{path: 'introduction', component: IntroComponent}
];
摘錄app-routing.module.ts
<app-navbar></app-navbar>
<router-outlet></router-outlet>
<app-contact></app-contact>
<footer class="p-3 footer bg-dark text-muted">
<div class="container">
<p class="text-center">
Copyright © 2017 教士會館. 版權所有
</p>
</div>
</footer>
<router-outlet> 該放在哪裡?
app.component.html
app.component是個好地方
但並非一定要放在appComponent內
...
menu: [
{ title: "教士會館", route: "/" },
{ title: "會館訊息", route: "/" },
{ title: "特色餐點", route: "/" },
{ title: "交通資訊", route: "/" },
{ title: "關於我們", route: "/introduction" }
],
...
<ul class="navbar-nav ml-auto">
<li class="nav-item mr-5 display-5 dropdown" *ngFor="let menuitem of menu">
<a [routerLink]="menuitem.route" class="text-white nav-link">
{{ menuitem.title }}
</a>
</li>
</ul>
.ts檔
.html檔
Consumer
不需停下等待
app端
提出要求
承諾
提供服務
服務端
Consumer
不需停下等待
app端
提出要求
承諾
提供服務
服務端
提供失敗
繼續執行
???