在Angular2 中導入Service 扮演 MVC 架構底下 Model 的角色
首先,透過Angular CLI 來替我們的Angular2專案加入Service
ng g s service\todo-list
(PS.Service命名的時候,不需要再TodoList後面加上Service,產生的檔案會自動命名多service)
例如上面這串Cli指令新增出來的檔案名稱為 todo-list.service.ts
檔案內容為:
import { Injectable } from '@angular/core';
import {ITodoItem} from '../interface/i-todo-item';
@Injectable()
//class Name會自動在後面補上Service
export class TodoListService {
constructor() { }
}
因為要扮演MVC架構底下Model的腳色,所以接下來把資料處理的程式寫到剛剛新增的
todo-list.service.ts
import { Injectable } from '@angular/core';
import { ITodoItem } from '../interface/i-todo-item';
@Injectable()
export class TodoListService {
todoItems: ITodoItem[] = [
{
id: 1,
value: 'Todo Item No.1',
done: false
},
{
id: 2,
value: 'Todo Item No.2',
done: true
},
{
id: 3,
value: 'Todo Item No.3',
done: false
}
];
constructor() { }
getTodoList() {
return this.todoItems;
};
addTodoListItem(text) {
this.todoItems.push({
id: (new Date()).getTime(),
value: text,
done: false
});
}
delTodoListItem(paraObject: ITodoItem) {
this.todoItems = this.todoItems.filter(function (ele) {
return ((ele.id != paraObject.id) && (ele.value != paraObject.value));
});
}
togoogleTodoItem(item: ITodoItem) {
item.done = !item.done;
}
}
Service定義好之後,要讓Service能夠被注入(其他component可以使用),
我們必須先在src/app/app.module.ts中的 @NgModule 這個 decorator 中的 providers: []中加入剛剛建立好的Service
//要記得 import Service
import { TodoListService } from './service/todo-list.service';
@NgModule({
declarations: [
AppComponent,
AddFormComponent,
HeaderComponent,
TodoItemsComponent
],
imports: [
BrowserModule
],
// 在providers中加入TodoListService
providers: [TodoListService],
bootstrap: [AppComponent]
})
export class AppModule { }
補充說明:自動在Module註冊的CLI指令
如果想要產生Service的時候自動在指定的Module註冊,可在後面加上 -m ModuleName
例如:
ng g s data -m article
這樣會產生一個service:data 並且幫我註冊到article 這一個Module
- 在Component中加入Service
接下來要做三件事情:
1.import 要使用的 Service。
2.在constructor 內宣告參數(注入方式為建構式注入),之後在Component的程式碼都能夠透過this.todoService 來取用這個service的資料。
3.依序將程式改為透過 this.todoService 控制
import { ITodoItem } from '../../interface/i-todo-item';
import { Component, OnInit } from '@angular/core';
//1.import 要使用的 Service
import { TodoListService } from '../../service/todo-list.service';
@Component({
selector: 'app-todo-items',
templateUrl: './todo-items.component.html',
styleUrls: ['./todo-items.component.css']
})
export class TodoItemsComponent implements OnInit {
//2.在constructor 內宣告參數,
//之後在Component的程式碼都能夠透過this.todoService 來取用這個service的資料。
constructor(private todoService: TodoListService) { }
ngOnInit() {
}
//3.依序將程式改為透過 this.todoService 控制
getTodoList() {
return this.todoService.getTodoList();
}
delTodoItem(delItem: ITodoItem) {
return this.todoService.delTodoListItem(delItem);
}
clickTodoList(item: ITodoItem) {
return this.todoService.togoogleTodoItem(item);
}
}
component.html 配合修正
<div class="container">
<div class="row">
<div class="col-sm-5 col-sm-offset-2 ">
<ul>
<li *ngFor="let item of getTodoList()">
<label for="chk_{{item.id}}">
<input type="checkbox" name="" id="" [checked]="item.done" (click)="clickTodoList(item)">{{item.value}}
</label>
<a href="#" (click)="delTodoItem(item)">Delete</a>
<span *ngIf="item.done">(已完成)</span>
</li>
</ul>
</div>
</div>
</div>
在Module內注入Service的作法,也可以使用以下的宣告方式
todoService:TodoListService;
constructor(todoService: TodoListService) {
this.todoService = todoService;
}
上面的寫法則是TypeScript提供簡化後的寫法
constructor(private todoService: TodoListService) { }
PS.private 也可以宣告成public
不要在component 內儲存Service的資料內容
情境描述:
在service內定義了一個Array : data
在component.ts裡面我也定義了一個Array : data
在ngOnInit()的時候去儲存service內的data
constructor(public datasvc: DataService) {}
data = Array();
ngOnInit() {
this.data = this.datasvc.data;
}
這樣做會有一個問題,
service那邊的Array ,在做資料修改(刪除、修改)的時候,
如果是用產生"新"陣列的方式來產生新的陣列
保哥建議在修改陣列內容的時候,以重新產生陣列的方式(map+Object.assign)產生新的陣列)
EditData(post: any) {
this.data = this.data.map(item => {
if (item.id == post.id) {
return Object.assign({}, item, post);
}
return item;
});
}
component這邊的ngOnInit()不會重新執行,所以重新產生的陣列跟component這邊的陣列就不是相同的物件了!
這樣不管使用者在Service內怎麼修改、刪除,資料都無法正常更新回component。
解決方法:直接存取service內的陣列就好!