Angular - Service

  • 207
  • 0
  • 2018-05-22

在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>
補充說明 1

在Module內注入Service的作法,也可以使用以下的宣告方式


todoService:TodoListService;
constructor(todoService: TodoListService) {
   this.todoService = todoService;
 }

上面的寫法則是TypeScript提供簡化後的寫法

constructor(private todoService: TodoListService) { }

PS.private 也可以宣告成public 

補充說明 2

不要在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內的陣列就好!