其實超過2-3個值以上的父子之間之溝通,且情境並不直覺時,就可以考慮使用Service來完成
這邊我們回到4-3的範例檔,重新進行撰寫
一樣先將todo、header、section和footer元件給建立出來
todo.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-todo',
templateUrl: './todo.component.html',
styleUrls: ['./todo.component.scss']
})
export class TodoComponent implements OnInit {
title = 'OneTodo';
constructor() { }
ngOnInit() {
}
}
todo.component.html
<section class="todoapp">
<header class="header">
<app-header></app-header>
</header>
<section class="main"
style="display: block;">
<app-section></app-section>
</section>
<footer class="footer"
style="display: block;">
<app-footer></app-footer>
</footer>
</section>
header.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { TodoService } from 'src/app/@services/todo.service';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
@Input()
title!: string;
todoInputModel = '';
constructor(private todoService: TodoService) { }
ngOnInit(): void {
}
add() {
this.todoService.add(this.todoInputModel);
this.todoInputModel = '';
}
}
header.component.html
<h1>
<div>{{title}}</div>
</h1>
<input class="new-todo"
[(ngModel)]="todoInputModel"
(keyup.enter)="add()"
placeholder="What needs to be done????"
autofocus>
section.component.ts
import { Component, OnInit } from '@angular/core';
import { Todo } from 'src/app/@models/todo.model';
import { TodoService } from 'src/app/@services/todo.service';
@Component({
selector: 'app-section',
templateUrl: './section.component.html',
styleUrls: ['./section.component.scss']
})
export class SectionComponent implements OnInit {
get toggleAllBtn() {
return this.todoService.toggleAllBtn;
}
get nowTodoList() {
return this.todoService.nowTodoList;
}
constructor(private todoService: TodoService) { }
ngOnInit(): void {
}
toggleAll() {
this.todoService.toggleAll();
}
clickCheck(item: Todo) {
this.todoService.clickCheck(item);
}
edit(item: Todo) {
if (item.CanEdit) {
item.Editing = true;
}
}
update(item: Todo) {
this.todoService.update(item);
}
delete(item: Todo) {
this.todoService.delete(item);
}
}
section.component.html
<input id="toggle-all"
class="toggle-all"
type="checkbox"
[checked]="toggleAllBtn"
(click)="toggleAll()">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<li *ngFor="let item of nowTodoList; let i=index"
[class]="{completed:item.Status,editing:item.Editing}">
<div class="view">
<input (click)="clickCheck(item)"
class="toggle"
type="checkbox"
[checked]="item.Status"
*ngIf="item.CanEdit">
<label (dblclick)="edit(item)">{{item.Thing}}</label>
<button (click)="delete(item)"
*ngIf="item.CanEdit"
class="destroy"> </button>
</div>
<input *ngIf="item.Editing"
#itemInput
[(ngModel)]="item.Thing"
(keyup.enter)="update(item)"
(blur)="update(item)"
(mouseenter)="itemInput.focus()"
class="edit" />
</li>
</ul>
footer.component.ts
import { Component, OnInit } from '@angular/core';
import { Todo, TodoStatusType } from 'src/app/@models/todo.model';
import { TodoService } from 'src/app/@services/todo.service';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss']
})
export class FooterComponent implements OnInit {
TodoStatusType = TodoStatusType;
get todoActive(): Todo[] {
return this.todoService.todoActive;
}
get nowTodoStatusType() {
return this.todoService.nowTodoStatusType;
}
get todoCompleted(): Todo[] {
return this.todoService.todoCompleted;
}
constructor(private todoService: TodoService) { }
ngOnInit(): void {
}
clearCompleted() {
this.todoService.clearCompleted();
}
setTodoStatusType(type: number) {
this.todoService.setTodoStatusType(type);
}
}
footer.component.html
<span class="todo-count"><strong>{{todoActive.length}}</strong> items left</span>
<ul class="filters">
<li>
<a [class.selected]="nowTodoStatusType===TodoStatusType.All"
(click)="setTodoStatusType(TodoStatusType.All)"
href="#/">All</a>
</li>
<li>
<a [class.selected]="nowTodoStatusType===TodoStatusType.Active"
(click)="setTodoStatusType(TodoStatusType.Active)"
href="#/active">Active</a>
</li>
<li>
<a [class.selected]="nowTodoStatusType===TodoStatusType.Completed"
(click)="setTodoStatusType(TodoStatusType.Completed)"
href="#/completed">Completed</a>
</li>
</ul>
<button class="clear-completed"
(click)="clearCompleted()"
*ngIf="todoCompleted.length">Clear completed</button>
我們直接就切成三個部分,然後各自都依賴注入private todoService: TodoService
接著需要什麼參數,都直接從todoService
裡面拿取
要使用什麼方法,也都使用todoService
中的方法
由於todoService
是註冊在root,所以這三個component取到的todoService
會是同一隻
利用這個特性,三個component都操作這支todoService
,所互動後取到的值都會是一致的
也就輕易的達成元件之間的互動,因此就不需要再把參數傳來傳去的作處理了,既麻煩,又不直覺
但利用Service的方式,就可以很直覺優雅的來做處理了
這邊的作法在官方文件父元件和子元件透過服務來通訊,有提到
但官方的例子對於新手來說非常難懂,我當初初學時,完全看不懂,其實主要原因是使用了Rxjs
並帶有情境的範例,一來不懂語法,二來完全不懂在表達什麼,所以那時直接跳過一陣子
等到後面學了差不多了才又回頭看
有關官方這個例子,我之後會特別示範一下到底在做什麼,有興趣的人可以看一下
那因為我會特別解說語法跟情境,所以不用擔心現在會看不懂,可以先備著了解一下,以後陸續都會用到Rxjs
其實HttpClient的subscribe就是Rxjs的語法之一
範例檔:下載
參考資料:
父元件和子元件透過服務來通訊