新北市教育局程式應用研發社群工作坊-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 新手入門



【網路資源】

活動照片:

106年5月份三重區資訊組長會議暨成長研習

日期:106年5月16日

地點:仁愛國小電腦教室

會議暨成長研習規劃:

工作報告與活動情形:

  • 賴新田組長下學年度確定不再接任仁愛國小資訊組長,仁愛國小因而也不再接任資訊中心。
    • 資訊中心的經費是採年度配撥,所以下學期接任資訊中心的學校,仍可在本校進行活動。
  • Acer L4620、Z430,仁愛國小有提供映像檔僅各校使用。
  • 對於八期電腦設備,有任何採購建議的請提出:
    • 正義國小:建議採購筆電。

二重國小分享「資安線上稽核填報作法及磨課師教學心得」:

資安線上稽核填報作法:(康組長)

MOOCs磨課師與均一教育平台教學心得:(梁家福老師)

簡報檔:磨課師與均一教育平台(仁愛國小分享)

碧華國小分享「如何在 Scratch 2.0 角色造型上輸入中文字」

參考網址:http://wdpsestea.blogspot.tw/2017/05/scratch2.html (scratch2背景輸入中文 | 陳國全夢幻天地 Blog)

scratch2不管是win或是linux下都無法輸入中文字,經過新北市烜誠(三多國小)和清賢(復興國小)老師的努力,終於可以輸入中文了。烜誠老師還把積木文字放大,更適合國小學生使用。感謝烜誠和清賢老師的努力。

檔案下載位置(裡面有for linux 和for win二個版本)
下載後,解壓㜚把scratch2目錄下的Scratch.swf替換(建議先備份),即可使用了,有更新版需再做一次。
使用時記得字型要選 Donegal
新北市E學園中的epc有win下的免安裝版,也歡迎下載使用。

安裝方法:

「WanaCrypt0r 2.0 勒索病毒」解救之道

「WanaCrypt0r 2.0 攻擊系統漏洞」資安警訊

解救之道:

盡速更新 KB3150513 避免遭受 DoublePulsar 攻擊

【資安威脅】因應WannaCrypt攻擊,微軟官方公布Windows Server 2003及XP修復程式

雖然微軟已經停止支援Windows Server 2003及XP等作業系統的漏洞修補許久,但尚有許多客戶仍使用已不支援之Window版本,為了因應此次WannaCrypt勒索軟體大量攻擊之特殊狀況,微軟破例提供以下數項作業系統之修補程式,若有使用以下作業系統之使用者可至連結中下載修補程式,以免遭受病毒攻擊。
修補程式下載連結:https://blogs.technet.microsoft.com/msrc/2017/05/12/customer-guidance-for-wannacrypt-attacks/

漏洞/資安訊息警訊-WanaCrypt0r 2.0 勒索病毒 (1060513)

行政院國家資通安全會報技術服務中心
漏洞/資安訊息警訊

發布編號 NCCST-ANA-2017-0051 發布時間 Sat May 13 12:55:18 CST 2017
事件類型 公告資訊 發現時間 Sat May 13 00:00:00 CST 2017
警訊名稱 近期勒索軟體WannaCry活動頻繁,請立即更新作業系統與防毒軟體,並注意平時資料備份作業
內容說明 近期加密勒索軟體WannaCry(WanaCrypt0r 2.0) 的新型變種勒索病毒正利用 Windows 漏洞(MS17-010漏洞)肆虐,受感染的電腦將會有大量檔案被加密,並且要求高價比特幣贖金。

此波勒索軟體攻擊是利用微軟伺服器訊息區塊(SMB)協定存在數個安全漏洞,台灣受影響的電腦以 Windows XP/Vista/7/8/8.1居多,作業系統請立刻進行 Windows Update 檢查並安裝更新。

影響平台 Windows XP

Windows Vista

Windows 7

Windows 8.1

Windows RT 8.1

Windows 10

Windows Server 2008

Windows Server 2008 R2

Windows Server 2012

Windows Server 2012 R2

Windows Server 2016

影響等級
建議措施 1. 確實持續更新電腦的作業系統、應用程式及防毒軟體等至最新版本。

2. 定期備份電腦上的檔案及演練資料還原程序。

3. 如不幸受到感染,請立即將受害電腦的網路連線及外接儲存裝置拔除,並關閉受害電腦無線網路。建議在清除惡意軟體前不要開啟任何檔案,或變更檔案存放位置。

參考資料 針對微軟伺服器訊息區塊(SMB)協定存在數個安全漏洞相關說明,可參考106/4/27發布之NCCST-ANA-2017-0046警訊。

106年新北市行動學習學校國小組北區第2次月例會(1060512)

日期:106年5月12日

地點:新北市瑞芳國小

國小組(北區)行動學習學校:

  • 白雲國小、崇德國小、瑞芳國小、米倉國小、碧華國小、長坑國小、中港國小、麗林國小、明志國小、 義學國小

公文及會議流程:

觀課:行動學習教案

講師:楊宗達

單元名稱:台灣的生態

適用年級:六年級(翰林版六下第三章第一節)

行動載具作業系統:iOS 系統 (iPad mini)

行動學習教學策略:

  • 同儕互評策略
  • 簡報發表策略
  • 資料蒐集策略
  • 影片教學策略
  • 擴增實境輔助教學

行動學習教學方法:

  • 專題導向學習法

教育雲使用概述:

教學活動:

  • 課前準備:全班分 8 組、一組發一台平板
  • 老師帶領全班同學靜坐,準備上課
  • 各組輪流上台報告:
    • 各組已將簡報影片先上傳至 YouTube
    • 報告前老師先做「引言」
    • 老師於學生報告時,在不中斷報告的方式下,適時給予回饋或提示、補充。
    • 各組報告結束時:
      • 請班上學生提問、提出優缺點。(老師視情況提問)
      • 老師做簡短結論,並針對學生表現佳者,予以表揚,並強調其優點。
  • 習作練習 5 分鐘:
    • 使用 Aurasma App 掃描習作上的圖片,提供互動線上資料查詢、討論。

教室環境:

活動照片:

議課:

學校分享:

研習活動:

StudyFun互動教學系統