新北市教育局程式應用研發社群工作坊-Angular 2 進階(1060712~0714)

日期:106年7月12日~7月14日

地點:新北市教研中心

公文與計畫:


課程主題:Angular 2 進階

講師:林士立老師

【課程內容】:

開發環境與工具:

安裝開發環境與工具

  • 安裝開發工具 (WebStorm)
  • 安裝 Git
  • 安裝 Chrome 擴充功能:Angular Augury

更新 Node.js, npm, Angular CLI

Node.js 重新安裝即可

npm i -g npm

npm unstall -g @angular/cli

npm i -g @angular/cli

實作操作指令:

練習專案檔下載:ng-demo-routing

  • 下載解壓縮後,請至專案資料夾中,下達下列指令:
    • npm install => 重建 node_modules 

ng new ng-demo-routing --routing

npm start

ng build --prod

ng g c intro/group1

ng g c intro/group2

ng g c navbar

ng g c home

ng g c page-not-found

ng g c intro/intro

ng g m intro --routing


實作延遲載入及預先載入:

練習專案檔下載:ng-demo-routing (同上).

  • 下載解壓縮後,請至專案資料夾中,下達下列指令:
    • npm install => 重建 node_modules 

ng g m m2 --routing

installing module
create src\app\m2\m2-routing.module.ts
create src\app\m2\m2.module.ts
WARNING Module is generated but not provided, it must be provided to be used

ng g c m2/demo1

installing component
create src\app\m2\demo1\demo1.component.css
create src\app\m2\demo1\demo1.component.html
create src\app\m2\demo1\demo1.component.spec.ts
create src\app\m2\demo1\demo1.component.ts
update src\app\m2\m2.module.ts

ng g c m2

installing component
create src\app\m2\m2.component.css
create src\app\m2\m2.component.html
create src\app\m2\m2.component.spec.ts
create src\app\m2\m2.component.ts
update src\app\m2\m2.module.ts


Module 功能介紹

Feature Module => 功能

  • Lazy Loading Module
  • Pre Loading Module

Core Module

Share Module


Angular Form 實作練習:

練習專案檔下載:ng-demo-forms

  • 下載解壓縮後,請至專案資料夾中,下達下列指令:
    • npm install => 重建 node_modules

ng new ng-demo-forms --routing

npm start

ng g c reactive


WebStorm 操作指令:

  • Ctrl + Alt + o:移除未用的指令行
  • Ctrl + Shift + 上下方向鍵:程式碼上下移,若為陣列元素,為自動調整 “," (逗號)。

Todo 專案前、後端程式整合實作

後端程式安裝:

安裝 XAMPP 及 Composer

範例檔下載:ng2-php-todo-demo (註:已將原 vendor 及 node_modules 資料夾刪除)

cd todo-php

composer install => 重建 vendor 資料夾

將 todo-php\app 設為 Apache 首頁路徑

設定 todo-php\.env => 連線至 MySQL 資料庫的組態設定

.\vendor\bin\phinx migrate => 建立測試資料表

使用 http://localhost 測試網站系統

  • adminer.php => MySQL 線上管理程式
  • todos.php => 顯示測試資料表內容

前端 Todo 專案建立實作:

練習專案檔下載:ng-demo-todo

  • 下載解壓縮後,請至專案資料夾中,下達下列指令:
    • npm install => 重建 node_modules

ng new ng-demo-todo --routing

npm start

ng g s todo

app.component.ts

import { Component, OnInit } from '@angular/core';
import { TodoService } from './todo.service';
@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
 title = 'app';
todos = []; // {id: 1, title: '...', content: '...'}
constructor(private todoService: TodoService) {}
ngOnInit(): void {
 this.todoService.getlist().subscribe(res => this.todos = res.data);
}
addTodo() {
 let newTodo = {
   title: this.title,
   content: this.title,
 }
 // this.todos.push(newTodo);
 this.todoService.add(newTodo).subscribe(
 // success
   res => {
     newTodo['id'] = res.data.id;
     this.todos.push(newTodo)
   }
 // error
 // complete
 )
 }
}

todo.service.ts

import { Injectable } from '@angular/core';
import { Http, Headers, RequestOptions, Response } from '@angular/http';
import 'rxjs/add/operator/map'
@Injectable()
export class TodoService {
headers = new Headers({'Content-Type': 'application/json'});
 options = new RequestOptions({headers: this.headers});
constructor(private http: Http) { }
add(todo) {
 return this.http.post(
   'http://localhost/todos.php?op=addTodo',
   todo,
   this.options
 ).map((res: Response) => res.json());
}
getlist() {
 return this.http.get(
   'http://localhost/todos.php'
   ).map((res: Response) => res.json());
 }
}

 app.component.html

Title:
<input type="text" [(ngModel)]="title">
<button (click)="addTodo()">Submit</button>
<hr />
<ul>
 <li *ngFor="let todo of todos">
   {{ todo.id }} - {{ todo.title }}
 </li>
</ul>

【學習資源】:


Angular 線上讀書會

投影片:Angular 2 新手急救站

投影片:Angular 2 開發實戰:進階開發篇 – RxJS 新手入門


【網路資源】

新北市教育局程式應用研發社群工作坊-KendoUI(1060710-0711)

日期:106年7月10-7日11日

地點:教研中心電腦教室

講師:三重區正義國小詹博文老師


課程講義:KENDOUI程式設計研習

範例網頁下載:kendo_web_20170711

範例網頁實作:http://webnas.bhes.ntpc.edu.tw/chiubor/kendo_web/

KendoUI課程錄影,明德高中許明福老師提供:


課程內容:

表單實作練習

<form>
<h1>公告管理</h1>
<hr/>
<label>公告人:</label>
<input id="lab_man" />

<label>發佈單位:</label>
<select name="organization">
<option value="o1">教務處</option>
<option value="o2">學務處</option>
<option value="o3">總務處</option>
<option value="o4">輔導室</option>
</select><br/>

<label>隱密性:</label>
<input type="radio" name="secur" value="p">公開</input>
<input type="radio" name="secur" value="s">限會員</input><br/>

<label>公告啟起日:</label>
<input id="start_date">

<label>公告結束日:</label>
<input id="end_date" /><br/>

<label>公告主題:</label>
<input id="txt_title" /><br/>

<label>公告內容:</label>
<textarea id="txt_context" cols="80" rows="5"></textarea><br/>
<hr/>
<input type="submit" value="新增" />
</form>

 


kendoUI_upload元件+PHP存檔範例

學習資源:

在Windows 10下使用Visual Studio Code開發Angular應用程式

參考自:Will 保哥的技術交流中心

要在 Windows 10 底下順利的使用 Visual Studio Code 開發 Angular 應用程式,以下是必要的設定步驟:

  1. 更新 Visual Studio Code 到最新版本 ( v1.13+ )
  2. 變更 Windows 10 預設輸入法為「英文」( 重要 )
    Windows 8 小技巧: 繁體中文語言如何變更預設輸入法(英文)
    http://blog.miniasp.com/…/Windows-8-Tips-How-to-change-defa…
  3. 到 Visual Studio Code 設定語言為 zh-TW (繁體中文)
    在 VSCode 按下 F1 鍵並輸入「Configure Language」開啟設定檔
    修改 “locale":"en" 為 “locale":"zh-TW" 並存檔後重新啟動
  4. 安裝 Angular Extension Pack 擴充套件
    Angular Extension Pack
    https://marketplace.visualstudio.com/items…
  5. 調整 Visual Studio Code 設定 ( 按下 Ctrl-, 開啟設定 )
{
 "editor.snippetSuggestions": "top",
 "editor.renderIndentGuides": false,
 "editor.multiCursorModifier": "ctrlCmd",
 "editor.minimap.renderCharacters": false,
 "genGetSet.spacedImportLine": true,
 "tsimporter.noStatusBar": true,
 "tslint.autoFixOnSave": true,
 "tslint.enable": true,
 "emmet.useNewEmmet": true
}

6. 記憶一些常用的 Visual Studio Code 快速鍵
官方版小抄: https://aka.ms/vscodekeybindings

新北市教育局程式應用研發社群工作坊-Angular 2(1060503~0621)

日期:106年5月3日~6月21日

時間:每週三下午

地點:新北市教研中心

公文與計畫:


課程主題:Angular

講師:林士立老師


【5月3日】:

【學習資源】【5月3日】【5月10日】【5月17日】【5月24日】【5月31日】【6月7日】【6月14日】【6月21日】

課前預習:

開發環境與工具:

安裝開發環境與工具

  • 安裝 nodejs
  • 安裝開發工具 (WebStorm)
  • 安裝 Git
  • 安裝 Chrome 擴充功能:Angular Augury

安裝 Angular CLI

npm install -g @angular/cli //@類似 namespace 功能
ng -v


ng new demo --routing
cd demo
npm install @angular/language-service --save-dev
ng -v

執行專案專屬的 TypeScript 編譯程式

  • .\node_modules\.bin\tsc
  • 編譯 src\main.ts 為 src\main.js
  • -w : 持續監視 src\main.ts,一有變動,就立即編譯

.\node_modules\.bin\tsc src\main -w


【5月10日】:

【學習資源】【5月3日】【5月10日】【5月17日】【5月24日】【5月31日】【6月7日】【6月14日】【6月21日】

課前預習:

更新 Angular CLI

angular-cli 更新到 1.0.2 版了,更新步驟如下:

global 版本更新:

npm uninstall -g @angular/cli
npm cache clean
npm install -g @angular/cli@latest

查看 angular-cli 版本( 專案目錄之外執行 ):
ng -v

————————————————————

舊版 angular-cli 產生的專案內的版本( local )更新:

注意!!
以下指令要在專案根目錄下執行,也會升級其他 package

Linux or Mac
rm -rf node_modules dist

Windows 命令提示字元
rmdir /S/Q node_modules dist

Windows PowerShell
rm -r -fo node_modules,dist

所有作業系統
npm install –save-dev @angular/cli@latest
npm install

單獨升級專案內的 angular-cli

npm uninstall @angular/cli –save-dev
npm install –save-dev @angular/cli

查看 local angular-cli 與 angular 相關 package 版本( 專案目錄下執行 ):
ng -v


使用 Angular 2 開發 TodoMVC 應用程式完整實作教學

操作指令:

ng new todomvc

cd todomvc

code . ==> 啟用 Visual Studio Code,並開啟當前所在的資料夾(todomvc)

cd ..

git clone https://github.com/coolrare/todomvc-template.git

  • 複製 todomvc-template/assets/css 資料夾至 todomvc/src/assets 下
  • 開啟 todomvc-template/index.html 將內容整個複製至 todomvc/src/index.html 內做編輯。
  • 將原 todomvc-template/index.html 中 <head> 中的內容:
    • <title> 取代 原 todomvc/src/index.html 中的 <title>
    • 兩個 <link> 複製至原 todomvc/index.html <head> 中
  • 將原 todomvc-template/index.html 中 <body> 的內容取代 todomvc/src/app/app.component.html 中的內容。

npm start ==> 啟用網頁伺服器 http://localhost:4200,並監視網頁更新情形,一有更新立即重新編譯,並更新網頁

ng g c title

ng g c rooter

ng g pipe filter

ng g service data


課程活動:

講義:Angular 簡介

參考網站:不專業網管筆記 | 林士立老師


實作:

範例程式下載:https://drive.google.com/drive/folders/0B_wx0SuvjnGWd1pyZmY3Zi1memM

參考資料:

操作指令及步驟:

  • ng new toh-demo –routing (注意:routing前有兩個 – 號)
  • 開啟 WebStrom
  • npm start  => 啟用網頁伺服器 http://localhost:4200,並監視網頁更新情形,一有更新立即重新編譯,並更新網頁
  • 範例檔案下載解壓縮至專案根目錄下
  • ng g c navbar (ng generate component navbar) => 產生 src/app/navbar 資料夾

installing component
create src\app\navbar\navbar.component.css
create src\app\navbar\navbar.component.html
create src\app\navbar\navbar.component.spec.ts
create src\app\navbar\navbar.component.ts
update src\app\app.module.ts

  • 將範例檔 heroes.html<nav> (含)網頁內容取代 navbar.component.html 中所有的網頁內容。
  • 將範例檔 heroes.html 下列資料複製至 index.html 相對位置
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
  • ng g c heroes

installing component
create src\app\heroes\heroes.component.css
create src\app\heroes\heroes.component.html
create src\app\heroes\heroes.component.spec.ts
create src\app\heroes\heroes.component.ts
update src\app\app.module.ts

  • 將範例檔 heroes.html<div class="container"> (不含)網頁內容取代 heroes.component.html 中所有的網頁內容。
  • ng g cl Hero
    installing class
    create src\app\hero.ts
  • 複製 mock-heroes.tssrc/app

修改 heroes.component.html 內容

<!-- 清單 -->
<div class="list-group">
  <a class="list-group-item my-item" *ngFor="let hero of heroes">
    <span>{{ hero.id }}</span>{{ hero.name }}<span class="badge">X</span>
  </a>
</div>
<!-- 清單結束 -->

hero.ts 內容

export class Hero {
  id: number;
  name: string;
}

app.component.html 網頁內容

<app-navbar></app-navbar>
<div class="container">
  <app-heroes></app-heroes>
</div>

Angular 系統運作說明:

main.ts 說明:

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule);

app.module.ts 說明:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { CommonModule } from '@angular/common';
import { AppRoutingModule } from './app-routing.module';

import { AppComponent } from './app.component';
import { NavbarComponent } from './navbar/navbar.component';
import { HeroesComponent } from "./heroes/heroes.component";

@NgModule({
  declarations: [
    AppComponent,
    NavbarComponent,
    HeroesComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    CommonModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts 說明:

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',  // HTML Tag
  templateUrl: './app.component.html',  // 樣板網頁置於另一個檔案
/*
  template: `<app-navbar></app-navbar>`,  // 樣板網頁內容置於倒單引號「`
*/
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';
}

【5月17日】:

【學習資源】【5月3日】【5月10日】【5月17日】【5月24日】【5月31日】【6月7日】【6月14日】【6月21日】

課前預習:

上課講義:

Angular2 Binding: 組合鍵

keydown.a
keydown.b
keydown.c
keydown.dot
keydown.Spacebar
keydown.meta.Enter  <= meta: Windows Key
keydown.alt.Enter
keydown.control.Enter
keydown.shift.Enter
keydown.meta.o
keydown.meta.s
keydown.meta.f
keydown.escape
keydown.ArrowLeft
keydown.ArrowRight
keydown.ArrowUp
keydown.ArrowDown
keydown.0
keydown.1
keydown.2
keydown.3
keydown.4
keydown.5
keydown.6
keydown.7
keydown.8
keydown.9

使用 keyup 也行,只是觸發時間點不同而已。

課堂練習及習作解答:僅供參考

app.component.html

<h1>{{title}}</h1>
<input type="text" #newurl (keydown.control.enter)="changeUrl(newurl.value)">
<button (click)="changeUrl(newurl.value)">submit</button>
<img [src]="url" [width]="w">
<hr>
寬:{{divx}}<input type="range" min=0 max=200 [value]="divx" style="width: 200px;" [(ngModel)]="divx">
高:{{divy}}<input type="range" min=0 max=200 [value]="divy" style="width: 200px;" [(ngModel)]="divy">
<br>
<div style="border: 1px red solid;" [style.width.px]="divx" [style.height.px]="divy"></div>

app.component.ts

import { Component } from '@angular/core';
@Component({
 selector: 'app-root',
 templateUrl: './app.component.html',
 styleUrls: ['./app.component.css']
})
export class AppComponent {
 title = 'app works!';
 url = 'https://angular.io/resources/images/logos/angular/angular.svg';
 w = 100;
 divx = 100;
 divy = 100;

 changeUrl(newUrl) {
   this.url = newUrl;
 }
}

【5月24日】:

【學習資源】【5月3日】【5月10日】【5月17日】【5月24日】【5月31日】【6月7日】【6月14日】【6月21日】


分享重點:

  • Angular 中的 css 樣式處理
  • NgIf 、 NgFor 、 NgSwitch 家族

上課講義:

Directive 中文參考資料:

參考資料:

上課筆記:

app.component.html

<table border="2">
  <tr>
    <td [attr.colspan]="cols">1</td>
    <td>2</td>
  </tr>
  <tr>
    <td>4</td>
    <td>5</td>
    <td>6</td>
  </tr>
</table>

<br />

<div class="abc"
     [class.active]="active"
     [class.title]="active"></div>

<button (click)="active = !active">{{active}}</button>

<div class="abc active inactive"
     [ngClass]="getObj()"></div>

<textarea [(ngModel)]="msg" *ngIf="true"></textarea>

<textarea [(ngModel)]="msg" *ngIf="false"></textarea>
<textarea [(ngModel)]="msg" [hidden]="true"></textarea>

<button *ngFor="let item of array1" (click)="handle(item)">
  {{item}}
</button>

<br />

<button (click)="color='red'">red</button>
<button (click)="color='blue'">blue</button>

<div *ngIf="color==='red'; then thenblock; else other"></div>

<ng-template #thenblock>
  <div style="background-color: red; width: 100px; height: 100px;"></div>
</ng-template>

<ng-template #other>
  <div style="background-color: blue; width: 100px; height: 100px;"></div>
</ng-template>

app.component.ts

export class AppComponent {
  cols = 2;
  active = true;
  msg = '';
  array1 = ['aa', 'bb', 'cc', 'dd'];
  color = 'red';

  getObj() {
    return {
      active: this.active,
      inactive: !this.active,
      myTitle: true
    };
  }

  handle(m: string) {
    this.msg += this.msg ? (',' + m) : m;
  }

}

【5月31日】:

【學習資源】【5月3日】【5月10日】【5月17日】【5月24日】【5月31日】【6月7日】【6月14日】【6月21日】


上課講義、範例、錄影檔:

實作:

ng g c child

child\child.component.html

<h2>{{ title }}</h2>
<button (click)="send('msg from child');">點我!點我</button>

child\child.component.ts

export class ChildComponent implements OnInit {
  @Input() title = 'default child title';

  @Output() sendmsg = new EventEmitter<string>();

  constructor() { }

  ngOnInit() {
  }

  send(msg) {
    console.log(msg);
    this.sendmsg.emit(msg);
  }

}


ng g c counter-button

counter-button\counter-button.component.html

<button (click)="count();">{{ value }}</button>

counter-button\counter-button.component.ts

export class CounterButtonComponent implements OnInit {
  @Input('step') value = 1;

  @Output('changed') valueChange = new EventEmitter<number>();

  constructor() { }

  ngOnInit() {
  }

  count() {
    this.valueChange.emit(this.value);
  }

}


app.component.html

<app-child
  [title]="'this is new title'"
  (sendmsg)="log($event)">
</app-child>
<br>
<app-counter-button [value]="-2" (changed)="change($event);"></app-counter-button>
{{ counter }}
<app-counter-button [value]="+5" (changed)="change($event);"></app-counter-button>

app.component.ts

counter = 10;
log(e) {
  console.log(e);
}

change(v: number) {
  this.counter += v;
}

作業:

  • 按下方圖塊,圖塊的顏色會顯示在上方
  • 按下方圖塊的按鈕[edit],會出現按鈕[close]及色彩三原色(紅、綠、藍)拉霸,可混色為在其上方的圖塊顏色,原按鈕[edit]會消失。
  • 按下方圖塊的按鈕[close],會出現按鈕[edit],原按鈕[close]及色彩三原色(紅、綠、藍)拉霸會消失。

◎林士立老師作業實作參考:

https://gist.github.com/t301000/fa692be5208abdf6b793e94901c20b57

邱昭士老師作業實作參考:

ng g c color-panel

color-panel\color-panel.component.html

<div style="width: 100px; height: 100px;" [style.backgroundColor]="getMyStyle()" (click)="setStyle()"></div>
<div *ngIf="edit; then thenBlock; else otherBlock"></div>
<ng-template #thenBlock>
  <button (click)="edit=false;">close</button>
  <input type="range" min="0" max="255" style="background-color: red;" [(ngModel)]="color.r">
  <input type="range" min="0" max="255" style="background-color: green;" [(ngModel)]="color.g">
  <input type="range" min="0" max="255" style="background-color: blue;" [(ngModel)]="color.b">
</ng-template>

<ng-template #otherBlock>
  <button (click)="edit=true;">edit</button>
</ng-template>

color-panel\color-panel.component.css

input[type="range"]{
  -webkit-appearance: none;
  overflow:hidden;     /* 限定範圍 */
  width:90px;
  height:15px;
  outline : none;      /* 避免點選會有藍線或虛線 */
  background:none;
}

color-panel\color-panel.component.ts

import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';

@Component({
  selector: 'app-color-panel',
  templateUrl: './color-panel.component.html',
  styleUrls: ['./color-panel.component.css']
})
export class ColorPanelComponent implements OnInit {
 edit = false;
 @Input() color = {r: 255, g: 255, b: 255};
 @Output() colorSet = new EventEmitter<string>();

 constructor() { }

 ngOnInit() {
 }

 getMyStyle() {
   return `rgb(${this.color.r}, ${this.color.g}, ${this.color.b})`;
 }

 setStyle() {
   this.colorSet.emit(this.getMyStyle());
 }

}

app.component.html

<table border="1">
  <tr>
    <td [attr.colspan]="colors.length">
      <div style="width: 100%; height: 100px;" [style.backgroundColor]="myColor"></div>
    </td>
  </tr>
  <tr style="vertical-align: top;">
    <td *ngFor="let color of colors">
      <app-color-panel [color]="color" (colorSet)="myColor=$event"></app-color-panel>
    </td>
  </tr>
</table>

app.component.ts

export class AppComponent {
  colors = [
    {r: 0, g: 0, b: 0},
    {r: 255, g: 0, b: 0},
    {r: 255, g: 255, b: 0},
    {r: 255, g: 0, b: 255},
    {r: 0, g: 255, b: 0},
    {r: 0, g: 255, b: 255},
    {r: 0, g: 0, b: 255},
  ];
  myColor = `rgb(255, 0, 0)`;

}

參考資料:


【6月7日】:

【學習資源】【5月3日】【5月10日】【5月17日】【5月24日】【5月31日】【6月7日】【6月14日】【6月21日】


上課講義、範例、錄影檔:


【6月14日】:

【學習資源】【5月3日】【5月10日】【5月17日】【5月24日】【5月31日】【6月7日】【6月14日】【6月21日】


上課講義、範例、錄影檔:

實作:

index.html

<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Tour Of Heroes</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
  <!--[if lt IE 9]>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js"></script>
  <![endif]-->
</head>
<body>
  <app-root>Loading...</app-root>

  <script src="https://code.jquery.com/jquery.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>

app.component.html

<app-navbar></app-navbar>

<div class="container">
  <router-outlet></router-outlet>
</div>

app-routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { HeroesComponent } from './heroes/heroes.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { HeroDetailComponent } from './hero-detail/hero-detail.component';

const routes: Routes = [
  {
    path: '',
    redirectTo: 'heroes',
    pathMatch: 'full'
  },
  { path: 'heroes', component: HeroesComponent },
  { path: 'heroes/:id', component: HeroDetailComponent },
  { path: 'dashboard', component: DashboardComponent },
  { path: '**', component: PageNotFoundComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

hero.service.ts

import { Injectable } from '@angular/core';
import { Headers, Http, RequestOptions, Response } from '@angular/http';

import { Hero } from './hero';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/observable/throw';
import 'rxjs/add/observable/of';

@Injectable()
export class HeroService {
  baseUrl = '/api/heroes';

  constructor(private http: Http) { }

  getHeroes(): Observable<Hero[]> {
    return this.http.get(this.baseUrl)
              .map((res: Response) => res.json().data || {})
              .catch(this.handleError);
  }

  addHero(name: string): Observable<Hero> {
    const headers = new Headers({
      'Content-Type': 'application/json'
    });
    const options = new RequestOptions({headers});
    return this.http.post(this.baseUrl, {name}, options)
              .map((res: Response) => res.json().data)
              .catch(this.handleError);
  }

  deleteHero(id: number): Observable<any> {
    return this.http.delete(`${this.baseUrl}/${id}`)
              .map((res: Response) => null)
              .catch(this.handleError);
  }

  getHero(id: number): Observable<Hero> {
    return this.http.get(`${this.baseUrl}/${id}`)
              .map((res: Response) => res.json().data || {})
              .catch(this.handleError);
  }

  updateHero(id: number, name: string): Observable<any> {
    const headers = new Headers({
      'Content-Type': 'application/json'
    });
    const options = new RequestOptions({headers});
    return this.http.put(`${this.baseUrl}/${id}`, {id, name}, options)
              .map(() => null)
              .catch(this.handleError);
  }

  searchHero(keyword: string): Observable<Hero[]> {
    return this.http.get(`${this.baseUrl}/?name=${keyword}`).do(() => console.log(keyword))
               .map((res: Response) => res.json().data as Hero[] || [])
               .catch(this.handleError);
  }

  private handleError(error: Response | any): Observable<any> {
    // console.log(error);
    let errMsg: string;
    if (error instanceof Response) {
      const body = error.json() || '';
      const err = body.error || JSON.stringify(body);
      errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
    } else {
      errMsg = error.message ? error.message : error.toString();
    }
    console.error(errMsg);
    return Observable.throw(errMsg);
  }
}

hero.ts

export class Hero {
  id: number;
  name: string;
}

heroes.data.ts

import { InMemoryDbService } from 'angular-in-memory-web-api';
import { Hero } from './hero';

export class HeroDB implements InMemoryDbService {
  createDb(): {[key: string]: Hero[]} {
    const heroes: Hero[] = [
      {id: 11, name: 'Mr. Nice'},
      {id: 12, name: 'Narco'},
      {id: 13, name: 'Bombasto'},
      {id: 14, name: 'Celeritas'},
      {id: 15, name: 'Magneta'},
      {id: 16, name: 'RubberMan'},
      {id: 17, name: 'Dynama'},
      {id: 18, name: 'Dr IQ'},
      {id: 19, name: 'Magma'},
      {id: 20, name: 'Tornado'}
    ];
    return {heroes};
  }
}

heroes\heroes.component.html

<div class="page-header">
  <h1>My Heroes</h1>
</div>

<app-new-hero (createHero)="addHero($event)"></app-new-hero>
<hr />

<!-- 清單 -->
<div class="list-group">
  <a class="list-group-item my-item"
      *ngFor="let hero of heroes"
      (click)="selectedHero = hero"
      [class.active]="selectedHero === hero">
    <span>{{ hero.id }}</span>{{ hero.name }}
    <span class="badge" (click)="deleteHero(hero); $event.stopPropagation()"> X </span>
  </a>
</div>
<!-- 清單結束 -->
<div *ngIf="errorMessage">{{ errorMessage }}</div>
<app-mini-detail [hero]="selectedHero" *ngIf="selectedHero"></app-mini-detail>

heroes\heroes.component.ts

import { Component, OnInit } from '@angular/core';

import { Hero } from '../hero';
import { HeroService } from '../hero.service';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  heroes: Hero[] = [];
  selectedHero: Hero = null;
  errorMessage: string = null;

  constructor(private heroService: HeroService) { }

  ngOnInit() {
    this.heroService.getHeroes()
        .subscribe(
          heroes => this.heroes = heroes,
          this.showError.bind(this)
        );
  }

  addHero(name: string) {
    this.heroService.addHero(name)
        .subscribe(
          hero => this.heroes.push(hero),
          this.showError.bind(this)
        );
  }

  deleteHero(hero: Hero) {
    this.heroService.deleteHero(hero.id)
        .subscribe(
          () => {
            this.heroes = this.heroes.filter(item => item !== hero);
            this.resetSelectedHero(hero);
          },
          this.showError.bind(this)
        );
  }

  private resetSelectedHero(hero: Hero) {
    if (this.selectedHero && this.selectedHero === hero) {
      this.selectedHero = null;
    }
  }

  private showError(error: string) {
    this.errorMessage = error;
  }

}

new-hero\new-hero.component.html

<div class="form-inline">
  <div class="form-group">
    <label for="name">Hero Name: </label>
    <input type="text" class="form-control" id="name"
           [placeholder]="hintWord" [(ngModel)]="name"
           (keydown.enter)="addHero()">
  </div>
  <button class="btn btn-primary"
          (click)="addHero()" [disabled]="!name.trim()">新增</button>
</div>

new-hero\new-hero.component.ts

import { Component, EventEmitter, OnInit, Output } from '@angular/core';

@Component({
  selector: 'app-new-hero',
  templateUrl: './new-hero.component.html',
  styleUrls: ['./new-hero.component.css']
})
export class NewHeroComponent implements OnInit {
  name = '';
  hintWord = 'New Hero\'s name';
  @Output() createHero = new EventEmitter<string>();

  constructor() { }

  ngOnInit() {
  }

  addHero() {
    const name = this.name.trim();
    if (name) {
      this.createHero.emit(name);
      this.name = '';
    }
  }
}

mini-detail\mini-detail.component.html

<h2>{{ hero.name }} is my hero.</h2>
<div class="btn btn-info" (click)="goDetail()">View Details</div>

mini-detail.component.ts

import { Component, Input, OnInit } from '@angular/core';
import { Hero } from '../hero';
import { Router } from '@angular/router';

@Component({
  selector: 'app-mini-detail',
  templateUrl: './mini-detail.component.html',
  styleUrls: ['./mini-detail.component.css']
})
export class MiniDetailComponent implements OnInit {
  @Input() hero: Hero;

  constructor(private router: Router) { }

  ngOnInit() {
  }

  goDetail() {
    this.router.navigate(['/heroes', this.hero.id]);
  }

}

navbar\navbar.component.html

<nav class="navbar navbar-inverse" role="navigation">
  <div class="container">
    <!-- Brand and toggle get grouped for better mobile display -->
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <span class="navbar-brand glyphicon glyphicon-text-background"></span>
    </div>

    <!-- Collect the nav links, forms, and other content for toggling -->
    <div class="collapse navbar-collapse navbar-ex1-collapse">
      <ul class="nav navbar-nav">
        <li routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">
        <!-- 路由連結 (不加上中括號的屬性繫結,只能繫結固定的字串資料) -->
          <a [routerLink]="['/']">Home</a>
        </li>
        <li routerLinkActive="active">
          <a [routerLink]="['/dashboard']">Dashboard</a>
        </li>
        <li routerLinkActive="active">
          <a [routerLink]="['/heroes']">Heroes</a>
        </li>
      </ul>
    </div><!-- /.navbar-collapse -->
  </div>
</nav>

【6月21日】:

【學習資源】【5月3日】【5月10日】【5月17日】【5月24日】【5月31日】【6月7日】【6月14日】【6月21日】


上課講義、範例、錄影檔:

路由說明:

const routes: Routes = [
  //{ path: '', redirectTo: 'heroes', pathMatch: 'full'}, //pathMatch 預設值為 'prefix'
  {path: 'heroes', component: HeroesComponent},
  {path: 'heroes/hello', component: HeroHelloComponent}, //一定要擺在 'heroes/:id' 之前,否則會永遠比對不到,因為系統會誤認:id='hello'
  {path: 'heroes/:id', component: HeroDetailComponent},
  {path: 'dashboard', component: DashboardComponent},
  {path: '**', component: PageNotFoundComponent}
];

實作:

安裝 angular-in-memory-web-api

  • npm i angular-in-memory-web-api –save-dev
  • angular2系列教程(八)In-memory web api、HTTP服务、依赖注入、Observable

hero.service.ts

import { Injectable } from '@angular/core';
import { Headers, Http, RequestOptions, Response } from '@angular/http';

import { Hero } from './hero';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/observable/throw';
import 'rxjs/add/observable/of';

@Injectable()
export class HeroService {
  baseUrl = '/api/heroes';

  constructor(private http: Http) { }

  getHeroes(): Observable<Hero[]> {
    return this.http.get(this.baseUrl)
              .map((res: Response) => res.json().data || {})
              .catch(this.handleError);
  }

  addHero(name: string): Observable<Hero> {
    const headers = new Headers({
      'Content-Type': 'application/json'
    });
    const options = new RequestOptions({headers});
    return this.http.post(this.baseUrl, {name}, options)
              .map((res: Response) => res.json().data)
              .catch(this.handleError);
  }

  deleteHero(id: number): Observable<any> {
    return this.http.delete(`${this.baseUrl}/${id}`)  // this.baseUrl + '/' + id
              .map((res: Response) => null)
              .catch(this.handleError);
  }

  getHero(id: number): Observable<Hero> {
    return this.http.get(`${this.baseUrl}/${id}`)
              .map((res: Response) => res.json().data || {})
              .catch(this.handleError);
  }

  updateHero(id: number, name: string): Observable<any> {
    const headers = new Headers({
      'Content-Type': 'application/json'
    });
    const options = new RequestOptions({headers});
    return this.http.put(`${this.baseUrl}/${id}`, {id, name}, options)
              .map(() => null)
              .catch(this.handleError);
  }

  searchHero(keyword: string): Observable<Hero[]> {
    return this.http.get(`${this.baseUrl}/?name=${keyword}`).do(() => console.log(keyword))
               .map((res: Response) => res.json().data as Hero[] || [])
               .catch(this.handleError);
  }

  private handleError(error: Response | any): Observable<any> {
    // console.log(error);
    let errMsg: string;
    if (error instanceof Response) {
      const body = error.json() || '';
      const err = body.error || JSON.stringify(body);
      errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
    } else {
      errMsg = error.message ? error.message : error.toString();
    }
    console.error(errMsg);
    return Observable.throw(errMsg);
  }
}

heroes.data.ts

import { InMemoryDbService } from 'angular-in-memory-web-api';
import { Hero } from './hero';

export class HeroDB implements InMemoryDbService {
  const HEROES: Hero[] = [
      {id: 11, name: 'Mr. Nice'},
      {id: 12, name: 'Narco'},
      {id: 13, name: 'Bombasto'},
      {id: 14, name: 'Celeritas'},
      {id: 15, name: 'Magneta'},
      {id: 16, name: 'RubberMan'},
      {id: 17, name: 'Dynama'},
      {id: 18, name: 'Dr IQ'},
      {id: 19, name: 'Magma'},
      {id: 20, name: 'Tornado'}
    ];
 createDb(): {
   return {heroes: this.HEROES};
 }
}

heroes\heroes.component.html

<div class="page-header">
  <h1>My Heroes</h1>
</div>

<app-new-hero (createHero)="addHero($event)"></app-new-hero>
<hr />

<!-- 清單 -->
<div class="list-group">
  <a class="list-group-item my-item"
      *ngFor="let hero of heroes"
      (click)="selectedHero = hero"
      [class.active]="selectedHero === hero">
    <span>{{ hero.id }}</span>{{ hero.name }}
    <span class="badge" (click)="deleteHero(hero); $event.stopPropagation()"> X </span>
  </a>
</div>
<!-- 清單結束 -->
<div *ngIf="errorMessage">{{ errorMessage }}</div>
<app-mini-detail [hero]="selectedHero" *ngIf="selectedHero"></app-mini-detail>

heroes\heroes.component.ts

import { Component, OnInit } from '@angular/core';

import { Hero } from '../hero';
import { HeroService } from '../hero.service';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  heroes: Hero[] = [];
  selectedHero: Hero = null;
  errorMessage: string = null;

  constructor(private heroService: HeroService) { }

  ngOnInit() {
    this.heroService.getHeroes()
        .subscribe(
          heroes => this.heroes = heroes, //成功 success ==> onNext
          this.showError.bind(this)       //失敗 error   ==> onError
                                          //已完成 completed ==> onCompleted
        );
  }

  addHero(name: string) {
    this.heroService.addHero(name)
        .subscribe(
          hero => this.heroes.push(hero),
          this.showError.bind(this)
        );
  }

  deleteHero(hero: Hero) {
    this.heroService.deleteHero(hero.id)
        .subscribe(
          () => {
            this.heroes = this.heroes.filter(item => item !== hero);
            this.resetSelectedHero(hero);
          },
          this.showError.bind(this)
        );
  }

  private resetSelectedHero(hero: Hero) {
    if (this.selectedHero && this.selectedHero === hero) {
      this.selectedHero = null;
    }
  }

  private showError(error: string) {
    this.errorMessage = error;
  }

}

app.module.ts

import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { HeroService } from './hero.service';
import { HeroDB } from './heroes.data';

@NgModule({
  declarations: [
   ...
  ],
  imports: [
    ...
    HttpModule,
    InMemoryWebApiModule.forRoot(HeroDB, {delay: 500}), // must after HttpModule, default delay 500ms
    AppRoutingModule
  ],
  providers: [HeroService],
  bootstrap: [AppComponent]
})
export class AppModule { }

【學習資源】:

【學習資源】【5月3日】【5月10日】【5月17日】【5月24日】【5月31日】【6月7日】【6月14日】【6月21日】

上課講義:

投影片:Angular 2 新手急救站


投影片:Angular 2 開發實戰:進階開發篇 – RxJS 新手入門



【網路資源】

活動照片:

新北市教育局程式應用研發社群工作坊(1060426)

日期:106年4月26日下午

地點:新北市教研中心

公文與計畫:


士立:Live Deepin 作業系統安裝與簡介

Live Deepin 安裝:

Deepin 開機隨身碟製作工具:deepin-boot-maker.exe

 

修改 isolinux\live.cfg 檔:

  • 複製 label Install 成 label live,置於其前,設
    menu label ^Live Deepin
    menu default
  • 移除 append 中  livecd-installer
  • locales=zh_TW.UTF-8
  • 移除原  label Install 中 menu default
label live
 menu label ^Live Deepin
 menu default
 linux /multiboot/deepin-15.4-amd64/live/vmlinuz
 initrd /multiboot/deepin-15.4-amd64/live/initrd.lz
 append live-media-path=/multiboot/deepin-15.4-amd64/live cdrom-detect/try-usb=true noprompt boot=live components quiet splash union=overlay locales=zh_TW.UTF-8
label Install
 menu label ^Install Deepin
 linux /multiboot/deepin-15.4-amd64/live/vmlinuz
 initrd /multiboot/deepin-15.4-amd64/live/initrd.lz
 append live-media-path=/multiboot/deepin-15.4-amd64/live cdrom-detect/try-usb=true noprompt boot=live components quiet splash union=overlay livecd-installer locales=zh_TW.UTF-8
label live-failsafe
 menu label ^Deepin failsafe
 linux /multiboot/deepin-15.4-amd64/live/vmlinuz
 initrd /multiboot/deepin-15.4-amd64/live/initrd.lz
 append live-media-path=/multiboot/deepin-15.4-amd64/live cdrom-detect/try-usb=true noprompt boot=live components memtest noapic noapm nodma nomce nolapic nomodeset nosmp nosplash vga=normal union=overlay

Deepin 簡介:

 

Deepin 15.4版本細節優化如下:

任務欄

新增24/12小時制切換功能;
優化任務欄UI細節(菜單和tooltip的背景統一變模糊);
優化圖標顯示,解決圖標顯示模糊的問題;
修復圖標可能找不到的問題;
修復調節聲音可能導致崩潰的問題;

控制中心

全新的毛玻璃加列表顯示設計;
新增天氣預報、通知中心;
新增試驗性的遠程投射功能;
新增首頁快捷入口功能(聲音、亮度、網絡、藍牙、投射等);
新增記錄NumLock狀態的功能;
新增圖標主題和光標主題;
解決按下Capslock導致窗口失去焦點問題;
優化鍵盤佈局切換快捷鍵實現;
優化啟動速度、內存佔用情況;
優化多屏配置交互、徹底解決雙屏交互邏輯問題;
優化時區選擇交互邏輯,解決夏令時等問題;

桌面

對桌面進行重構,解決桌面操作中的邏輯問題;
優化桌面熱區的交互體驗;
修復設置壁紙程序縮略圖可能跟實際壁紙不對應的問題;
修復在智能隱藏模式下切換任務欄遇到桌面圖標未自動避讓的問題;
修復桌面上文件夾和應用圖標直接使用Enter鍵不能打開的問題;

啟動器

優化啟動器圖標的顯示效果;
啟動器字體跟隨系統字體大小;
優化啟動器界面相關細節;

窗口管理器

新增多工作區設置不同壁紙的功能;
新增工作區切換的提示效果,切換工作區更加方便;
新增窗口背景毛玻璃效果支持;
界面細節更加豐富、交互更加流暢;
修復切換卡死等嚴重bug;

其他

預裝深度錄屏;
預裝深度錄音;
預裝最新的思源宋體;
預裝福昕閱讀器(中英文);
預裝新的XServer版本和驅動;
預裝基於CrossOver 16的QQ 8.9版本;
統一登錄界面、鎖屏與系統的界面風格;
修復了Steam無法更新的問題;
修復了BCM4322等無線網卡無法使用的問題。


保太:PStart 免安裝版+免安裝版軟體

PStart 軟體簡介

PStart 是一個可以讓我們用來快速啟動軟體的輕巧軟體,它除了可以安裝在電腦上,直接當做快捷列工具外,它還可以安裝在 USB 隨身碟,讓我們的 USB 就像是擁有著一個「開始」工作列一般,可以直接快速的啟動 USB 隨身碟上面的應用程式。


補充教材:

一、YUMI 軟體介紹:Windows 作業系統下USB開機製作軟體

二、Clonezilla及DRBL

製作 CloneZilla_Live 隨身碟 – 樹林國小黃保太 – YouTube