บทช่วยสอน Ngrx และ Angular 2: การสร้างแอปพลิเคชันปฏิกิริยา
เผยแพร่แล้ว: 2022-03-11เราพูดกันมากเกี่ยวกับการเขียนโปรแกรมเชิงโต้ตอบในขอบเขตเชิงมุม การเขียนโปรแกรมเชิงโต้ตอบและ Angular 2 ดูเหมือนจะจับมือกัน อย่างไรก็ตาม สำหรับใครก็ตามที่ไม่คุ้นเคยกับเทคโนโลยีทั้งสองนี้ อาจเป็นงานที่ค่อนข้างยุ่งยากที่จะค้นหาว่าทั้งหมดนี้เกี่ยวกับอะไร
ในบทความนี้ ผ่านการสร้างแอปพลิเคชันเชิงโต้ตอบ Angular 2 โดยใช้ Ngrx คุณจะได้เรียนรู้ว่ารูปแบบคืออะไร ซึ่งรูปแบบสามารถพิสูจน์ได้ว่ามีประโยชน์ และวิธีที่รูปแบบสามารถใช้เพื่อสร้างแอปพลิเคชัน Angular 2 ที่ดีขึ้น
Ngrx เป็นกลุ่มของไลบรารีเชิงมุมสำหรับส่วนขยายแบบรีแอกทีฟ Ngrx/Store ใช้รูปแบบ Redux โดยใช้การสังเกต RxJS ที่รู้จักกันดีของ Angular 2 ซึ่งมีข้อดีหลายประการโดยทำให้สถานะแอปพลิเคชันของคุณง่ายขึ้นสำหรับวัตถุธรรมดา บังคับใช้การไหลของข้อมูลแบบทิศทางเดียว และอื่นๆ ไลบรารี Ngrx/Effects ช่วยให้แอปพลิเคชันสามารถสื่อสารกับโลกภายนอกโดยเรียกผลข้างเคียง
การเขียนโปรแกรมเชิงโต้ตอบคืออะไร?
การเขียนโปรแกรมเชิงโต้ตอบเป็นคำที่คุณได้ยินบ่อยในทุกวันนี้ แต่จริงๆ แล้วหมายความว่าอย่างไร
การเขียนโปรแกรมเชิงโต้ตอบเป็นวิธีที่แอปพลิเคชันจัดการกับเหตุการณ์และการไหลของข้อมูลในแอปพลิเคชันของคุณ ในการเขียนโปรแกรมเชิงโต้ตอบ คุณออกแบบส่วนประกอบและซอฟต์แวร์อื่นๆ ของคุณเพื่อตอบสนองต่อการเปลี่ยนแปลงเหล่านั้นแทนที่จะขอการเปลี่ยนแปลง นี่อาจเป็นการเปลี่ยนแปลงครั้งใหญ่
เครื่องมือที่ยอดเยี่ยมสำหรับการเขียนโปรแกรมเชิงโต้ตอบอย่างที่คุณทราบคือ RxJS
ไลบรารีนี้จะช่วยให้คุณจัดการเหตุการณ์ในแอปพลิเคชันของคุณได้ด้วยการจัดเตรียมสิ่งที่สังเกตได้และโอเปอเรเตอร์จำนวนมากในการแปลงข้อมูลขาเข้า ในความเป็นจริง ด้วยสิ่งที่สังเกตได้ คุณจะเห็นเหตุการณ์เป็นกระแสของเหตุการณ์ ไม่ใช่เหตุการณ์ที่เกิดขึ้นเพียงครั้งเดียว สิ่งนี้ทำให้คุณสามารถรวมมันเข้าด้วยกัน ตัวอย่างเช่น เพื่อสร้างกิจกรรมใหม่ที่คุณจะรับฟัง
การเขียนโปรแกรมเชิงโต้ตอบเป็นการเปลี่ยนแปลงวิธีการสื่อสารระหว่างส่วนต่างๆ ของแอปพลิเคชัน แทนที่จะส่งข้อมูลโดยตรงไปยังส่วนประกอบหรือบริการที่ต้องการ ในการเขียนโปรแกรมเชิงโต้ตอบ กลับเป็นส่วนประกอบหรือบริการที่ตอบสนองต่อการเปลี่ยนแปลงข้อมูล
คำเกี่ยวกับ Ngrx
เพื่อให้เข้าใจแอปพลิเคชันที่คุณจะสร้างผ่านบทช่วยสอนนี้ คุณต้องเจาะลึกแนวคิดหลักของ Redux อย่างรวดเร็ว
เก็บ
ร้านค้าสามารถถูกมองว่าเป็นฐานข้อมูลฝั่งไคลเอ็นต์ของคุณ แต่ที่สำคัญกว่านั้นคือมันสะท้อนถึงสถานะของแอปพลิเคชันของคุณ คุณสามารถเห็นมันเป็นแหล่งเดียวของความจริง
เป็นสิ่งเดียวที่คุณเปลี่ยนแปลงเมื่อคุณทำตามรูปแบบ Redux และคุณแก้ไขโดยส่งการดำเนินการไป
ลด
ตัวลดคือฟังก์ชันที่รู้ว่าต้องทำอย่างไรกับการกระทำที่กำหนดและสถานะก่อนหน้าของแอปของคุณ
ตัวลดจะใช้สถานะก่อนหน้าจากร้านค้าของคุณและใช้ฟังก์ชันบริสุทธิ์กับมัน Pure หมายความว่าฟังก์ชันจะคืนค่าเดิมเสมอสำหรับอินพุตเดียวกันและไม่มีผลข้างเคียง จากผลลัพธ์ของฟังก์ชันที่บริสุทธิ์นั้น คุณจะมีสถานะใหม่ที่จะใส่ในร้านค้าของคุณ
การกระทำ
การดำเนินการคือเพย์โหลดที่มีข้อมูลที่จำเป็นในการเปลี่ยนแปลงร้านค้าของคุณ โดยทั่วไป การดำเนินการจะมีประเภทและส่วนของข้อมูลซึ่งฟังก์ชันตัวลดจะดำเนินการเพื่อเปลี่ยนสถานะ
ผู้จัดส่ง
ผู้มอบหมายงานเป็นเพียงจุดเริ่มต้นสำหรับคุณในการดำเนินการของคุณ ใน Ngrx มีวิธีการจัดส่งโดยตรงที่ร้านค้า
มิดเดิลแวร์
มิดเดิลแวร์คือฟังก์ชันบางอย่างที่จะสกัดกั้นแต่ละการกระทำที่ถูกส่งออกไปเพื่อสร้างผลข้างเคียง แม้ว่าคุณจะไม่ใช้ในบทความนี้ก็ตาม มีการใช้งานในไลบรารี Ngrx/Effect และมีโอกาสสูงที่คุณจะต้องใช้มันในขณะที่สร้างแอปพลิเคชันในโลกแห่งความเป็นจริง
ทำไมต้องใช้ Ngrx?
ความซับซ้อน
การจัดเก็บและการไหลของข้อมูลแบบทิศทางเดียวช่วยลดการเชื่อมต่อระหว่างส่วนต่างๆ ของแอปพลิเคชันของคุณอย่างมาก การมีเพศสัมพันธ์ที่ลดลงนี้ช่วยลดความซับซ้อนในการใช้งานของคุณ เนื่องจากแต่ละส่วนให้ความสำคัญกับสถานะเฉพาะเท่านั้น
เครื่องมือช่าง
สถานะทั้งหมดของแอปพลิเคชันของคุณถูกเก็บไว้ในที่เดียว ดังนั้นจึงง่ายที่จะมีมุมมองทั่วโลกเกี่ยวกับสถานะแอปพลิเคชันของคุณและช่วยในระหว่างการพัฒนา นอกจากนี้ Redux ยังมีเครื่องมือสำหรับนักพัฒนาที่ดีมากมายที่ใช้ประโยชน์จากร้านค้าและสามารถช่วยในการสร้างสถานะบางอย่างของแอปพลิเคชันหรือเดินทางข้ามเวลา เป็นต้น
ความเรียบง่ายทางสถาปัตยกรรม
ประโยชน์หลายประการของ Ngrx สามารถทำได้ด้วยโซลูชันอื่นๆ ท้ายที่สุด Redux เป็นรูปแบบสถาปัตยกรรม แต่เมื่อคุณต้องสร้างแอปพลิเคชันที่เหมาะสมกับรูปแบบ Redux เช่น เครื่องมือแก้ไขร่วมกัน คุณสามารถเพิ่มคุณสมบัติได้อย่างง่ายดายโดยทำตามรูปแบบ
แม้ว่าคุณจะไม่ต้องคิดว่าคุณกำลังทำอะไรอยู่ แต่การเพิ่มข้อมูลบางอย่าง เช่น การวิเคราะห์ผ่านแอปพลิเคชันทั้งหมดของคุณก็กลายเป็นเรื่องเล็กน้อย เนื่องจากคุณสามารถติดตามการดำเนินการทั้งหมดที่ถูกส่งออกไปได้
เส้นโค้งการเรียนรู้ขนาดเล็ก
เนื่องจากรูปแบบนี้ใช้กันอย่างแพร่หลายและเรียบง่าย จึงเป็นเรื่องง่ายสำหรับคนใหม่ๆ ในทีมที่จะติดตามสิ่งที่คุณทำอย่างรวดเร็ว
Ngrx โดดเด่นที่สุดเมื่อคุณมีผู้ดำเนินการภายนอกจำนวนมากที่สามารถปรับเปลี่ยนแอปพลิเคชันของคุณได้ เช่น แดชบอร์ดการตรวจสอบ ในกรณีดังกล่าว การจัดการข้อมูลขาเข้าทั้งหมดที่ส่งไปยังแอปพลิเคชันของคุณเป็นเรื่องยาก และการจัดการสถานะจะยากขึ้น นั่นคือเหตุผลที่คุณต้องการลดความซับซ้อนด้วยสถานะที่ไม่เปลี่ยนรูป และนี่คือสิ่งหนึ่งที่ร้าน Ngrx มอบให้เรา
การสร้างแอปพลิเคชันด้วย Ngrx
พลังของ Ngrx จะเปล่งประกายที่สุดเมื่อคุณมีข้อมูลภายนอกที่ถูกส่งไปยังแอปพลิเคชันของเราแบบเรียลไทม์ ด้วยเหตุนี้ เรามาสร้างตารางฟรีแลนซ์ง่ายๆ ที่แสดงฟรีแลนซ์ออนไลน์และให้คุณกรองผ่านพวกมันได้
ตั้งโครงการ
Angular CLI เป็นเครื่องมือที่ยอดเยี่ยมที่ช่วยให้ขั้นตอนการตั้งค่าง่ายขึ้นมาก คุณอาจไม่ต้องการใช้ แต่จำไว้ว่าส่วนที่เหลือของบทความนี้จะใช้มัน
npm install -g @angular/cliถัดไป คุณต้องการสร้างแอปพลิเคชันใหม่และติดตั้งไลบรารี Ngrx ทั้งหมด:
ng new toptal-freelancers npm install ngrx --saveฟรีแลนซ์ รีดิวเซอร์
ตัวลดขนาดเป็นส่วนสำคัญของสถาปัตยกรรม Redux เหตุใดจึงไม่เริ่มต้นกับสิ่งเหล่านี้ก่อนในขณะที่สร้างแอปพลิเคชัน
ขั้นแรก ให้สร้างตัวลด "ฟรีแลนซ์" ที่จะรับผิดชอบในการสร้างสถานะใหม่ของเราทุกครั้งที่มีการส่งการดำเนินการไปยังร้านค้า
freelancer-grid/freelancers.reducer.ts
import { Action } from '@ngrx/store'; export interface AppState { freelancers : Array<IFreelancer> } export interface IFreelancer { name: string, email: string, thumbnail: string } export const ACTIONS = { FREELANCERS_LOADED: 'FREELANCERS_LOADED', } export function freelancersReducer( state: Array<IFreelancer> = [], action: Action): Array<IFreelancer> { switch (action.type) { case ACTIONS.FREELANCERS_LOADED: // Return the new state with the payload as freelancers list return Array.prototype.concat(action.payload); default: return state; } }นี่คือตัวลดการทำงานฟรีแลนซ์ของเรา
ฟังก์ชันนี้จะถูกเรียกทุกครั้งที่มีการดำเนินการดำเนินการผ่านร้านค้า หากแอ็คชันเป็น FREELANCERS_LOADED จะเป็นการสร้างอาร์เรย์ใหม่จากเพย์โหลดแอ็คชัน หากไม่เป็นเช่นนั้น ระบบจะส่งคืนการอ้างอิงสถานะเดิมและจะไม่มีการต่อท้ายใดๆ
สิ่งสำคัญที่ควรทราบในที่นี้คือ หากมีการส่งคืนการอ้างอิงสถานะเดิม สถานะจะถือว่าไม่เปลี่ยนแปลง ซึ่งหมายความว่าหากคุณเรียก state.push(something) สถานะจะไม่ถูกพิจารณาว่ามีการเปลี่ยนแปลง พึงระลึกไว้เสมอว่าในขณะที่ทำหน้าที่ลดของคุณ
รัฐจะไม่เปลี่ยนรูป ต้องส่งคืนสถานะใหม่ทุกครั้งที่มีการเปลี่ยนแปลง
Freelancer Grid Component
สร้างองค์ประกอบกริดเพื่อแสดงฟรีแลนซ์ออนไลน์ของเรา ตอนแรกจะสะท้อนเฉพาะของที่อยู่ในร้านเท่านั้น
ng generate component freelancer-gridใส่สิ่งต่อไปนี้ใน freelancer-grid.component.ts
import { Component, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState, IFreelancer, ACTIONS } from './freelancer-reducer'; import * as Rx from 'RxJS'; @Component({ selector: 'app-freelancer-grid', templateUrl: './freelancer-grid.component.html', styleUrls: ['./freelancer-grid.component.scss'], }) export class FreelancerGridComponent implements OnInit { public freelancers: Rx.Observable<Array<IFreelancer>>; constructor(private store: Store<AppState>) { this.freelancers = store.select('freelancers'); } }และต่อไปนี้ใน freelancer-grid.component.html :
<span class="count">Number of freelancers online: {{(freelancers | async).length}}</span> <div class="freelancer fade thumbail" *ngFor="let freelancer of freelancers | async"> <button type="button" class="close" aria-label="Close" (click)="delete(freelancer)"><span aria-hidden="true">×</span></button><br> <img class="img-circle center-block" src="{{freelancer.thumbnail}}" /><br> <div class="info"><span><strong>Name: </strong>{{freelancer.name}}</span> <span><strong>Email: </strong>{{freelancer.email}}</span></div> <a class="btn btn-default">Hire {{freelancer.name}}</a> </div>แล้วคุณเพิ่งทำอะไร?
ขั้นแรก คุณได้สร้างองค์ประกอบใหม่ที่เรียกว่า freelancer-grid
ส่วนประกอบมีคุณสมบัติที่ชื่อ freelancers ซึ่งเป็นส่วนหนึ่งของสถานะแอปพลิเคชันที่มีอยู่ในร้านค้า Ngrx ด้วยการใช้โอเปอเรเตอร์ที่เลือก คุณเลือกที่จะรับการแจ้งเตือนโดยคุณสมบัติ freelancers ซ์ของสถานะแอปพลิเคชันโดยรวมเท่านั้น ดังนั้นทุกครั้งที่คุณสมบัติ freelancers ของสถานะแอปพลิเคชันเปลี่ยนแปลง ข้อมูลที่สังเกตได้ของคุณจะได้รับการแจ้งเตือน
สิ่งหนึ่งที่สวยงามสำหรับโซลูชันนี้คือคอมโพเนนต์ของคุณมีการพึ่งพาเพียงอย่างเดียว และเป็นร้านค้าที่ทำให้คอมโพเนนต์ของคุณซับซ้อนน้อยลงและนำกลับมาใช้ใหม่ได้ง่าย
ในส่วนของเทมเพลต คุณไม่ได้ทำอะไรที่ซับซ้อนเกินไป สังเกตการใช้ไพพ์ async ในไฟล์ *ngFor freelancers ที่สังเกตได้นั้นไม่สามารถทำซ้ำได้โดยตรง แต่ด้วย Angular เรามีเครื่องมือในการแกะมันและผูก dom เข้ากับค่าของมันโดยใช้ไพพ์ async ทำให้การทำงานกับสิ่งที่สังเกตได้ง่ายกว่ามาก
การเพิ่มฟังก์ชัน Remove Freelancer
ตอนนี้คุณมีฐานการทำงานแล้ว มาเพิ่มการดำเนินการบางอย่างในแอปพลิเคชันกัน
คุณต้องการที่จะสามารถลบ freelancer ออกจากรัฐได้ ตามวิธีการทำงานของ Redux คุณต้องกำหนดการกระทำนั้นในแต่ละสถานะที่ได้รับผลกระทบจากการกระทำนั้นก่อน
ในกรณีนี้ เป็นเพียงตัวลด freelancers เท่านั้น:
export const ACTIONS = { FREELANCERS_LOADED: 'FREELANCERS_LOADED', DELETE_FREELANCER: 'DELETE_FREELANCER', } export function freelancersReducer( state: Array<IFreelancer> = [], action: Action): Array<IFreelancer> { switch (action.type) { case ACTIONS.FREELANCERS_LOADED: // Return the new state with the payload as freelancers list return Array.prototype.concat(action.payload); case ACTIONS.DELETE_FREELANCER: // Remove the element from the array state.splice(state.indexOf(action.payload), 1); // We need to create another reference return Array.prototype.concat(state); default: return state; } }การสร้างอาร์เรย์ใหม่จากอาร์เรย์เก่าเพื่อให้มีสถานะที่ไม่เปลี่ยนรูปแบบใหม่เป็นสิ่งสำคัญมาก
ตอนนี้ คุณสามารถเพิ่มฟังก์ชัน Delete freelancer ให้กับส่วนประกอบของคุณ ซึ่งจะส่งการดำเนินการนี้ไปยังร้านค้า:
delete(freelancer) { this.store.dispatch({ type: ACTIONS.DELETE_FREELANCER, payload: freelancer, }) }มันดูไม่ง่ายเหรอ?
ตอนนี้คุณสามารถลบ freelancer คนใดคนหนึ่งออกจากรัฐได้ และการเปลี่ยนแปลงนั้นจะเผยแพร่ผ่านแอปพลิเคชันของคุณ

แล้วถ้าคุณเพิ่มส่วนประกอบอื่นในแอปพลิเคชันเพื่อดูว่าพวกเขาสามารถโต้ตอบกันผ่านสโตร์ได้อย่างไร
ตัวลดตัวกรอง
และเช่นเคย มาเริ่มกันที่ตัวลดก่อน สำหรับองค์ประกอบนั้น มันค่อนข้างง่าย คุณต้องการให้ตัวลดขนาดส่งคืนสถานะใหม่ด้วยทรัพย์สินที่เราส่งไปเท่านั้น ควรมีลักษณะดังนี้:
import { Action } from '@ngrx/store'; export interface IFilter { name: string, email: string, } export const ACTIONS = { UPDATE_FITLER: 'UPDATE_FITLER', CLEAR_FITLER: 'CLEAR_FITLER', } const initialState = { name: '', email: '' }; export function filterReducer( state: IFilter = initialState, action: Action): IFilter { switch (action.type) { case ACTIONS.UPDATE_FITLER: // Create a new state from payload return Object.assign({}, action.payload); case ACTIONS.CLEAR_FITLER: // Create a new state from initial state return Object.assign({}, initialState); default: return state; } }ส่วนประกอบตัวกรอง
import { Component, OnInit } from '@angular/core'; import { IFilter, ACTIONS as FilterACTIONS } from './filter-reducer'; import { Store } from '@ngrx/store'; import { FormGroup, FormControl } from '@angular/forms'; import * as Rx from 'RxJS'; @Component({ selector: 'app-filter', template: '<form class="filter">'+ '<label>Name</label>'+ '<input type="text" [formControl]="name" name="name"/>'+ '<label>Email</label>'+ '<input type="text" [formControl]="email" name="email"/>'+ '<a (click)="clearFilter()" class="btn btn-default">Clear Filter</a>'+ '</form>', styleUrls: ['./filter.component.scss'], }) export class FilterComponent implements OnInit { public name = new FormControl(); public email = new FormControl(); constructor(private store: Store<any>) { store.select('filter').subscribe((filter: IFilter) => { this.name.setValue(filter.name); this.email.setValue(filter.email); }) Rx.Observable.merge(this.name.valueChanges, this.email.valueChanges).debounceTime(1000).subscribe(() => this.filter()); } ngOnInit() { } filter() { this.store.dispatch({ type: FilterACTIONS.UPDATE_FITLER, payload: { name: this.name.value, email: this.email.value, } }); } clearFilter() { this.store.dispatch({ type: FilterACTIONS.CLEAR_FITLER, }) } }ขั้นแรก คุณได้สร้างเทมเพลตอย่างง่ายที่มีแบบฟอร์มที่มีสองฟิลด์ (ชื่อและอีเมล) ที่สะท้อนถึงสถานะของเรา
คุณรักษาฟิลด์เหล่านั้นให้สอดคล้องกับสถานะค่อนข้างแตกต่างไปจากที่คุณทำกับสถานะ freelancers ตามจริงแล้ว อย่างที่คุณเห็น คุณสมัครรับสถานะตัวกรอง และทุกครั้งที่มีการทริกเกอร์ คุณกำหนดค่าใหม่ให้กับ formControl
สิ่งหนึ่งที่ดีกับ Angular 2 คือมันมีเครื่องมือมากมายในการโต้ตอบกับสิ่งที่สังเกตได้
คุณเคยเห็น async ไปป์ก่อนหน้านี้แล้ว และตอนนี้คุณเห็นคลาส formControl ที่ให้คุณมีค่าที่สังเกตได้ของอินพุต ซึ่งช่วยให้ทำสิ่งแฟนซีได้ เช่น สิ่งที่คุณทำในองค์ประกอบตัวกรอง
อย่างที่คุณเห็น คุณใช้ Rx.observable.merge เพื่อรวมสองสิ่งที่สังเกตได้จาก formControls ของคุณ จากนั้นคุณ debounce ใหม่ที่สังเกตได้ก่อนที่จะทริกเกอร์ฟังก์ชัน filter
พูดง่ายๆ ก็คือ คุณรอหนึ่งวินาทีหลังจากเปลี่ยนชื่อหรืออีเมล formControl แล้วเรียกใช้ฟังก์ชัน filter
ไม่น่ากลัวเหรอ?
ทั้งหมดนี้ทำได้ในโค้ดไม่กี่บรรทัด นี่เป็นหนึ่งในเหตุผลที่คุณจะหลงรัก RxJS มันช่วยให้คุณทำสิ่งแฟนซีมากมายได้อย่างง่ายดายที่อาจซับซ้อนกว่านี้
มาต่อกันที่ฟังก์ชันตัวกรองกัน มันทำอะไร?
มันเพียงแค่ส่งการดำเนินการ UPDATE_FILTER ด้วยค่าของชื่อและอีเมล และตัวลดจะดูแลการเปลี่ยนแปลงสถานะด้วยข้อมูลนั้น
มาดูสิ่งที่น่าสนใจกันดีกว่า
คุณจะทำให้ตัวกรองนั้นโต้ตอบกับกริด freelancer ที่คุณสร้างไว้ก่อนหน้านี้ได้อย่างไร
เรียบง่าย. คุณต้องฟังส่วนตัวกรองของร้านเท่านั้น มาดูกันว่าโค้ดมีหน้าตาเป็นอย่างไร
import { Component, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState, IFreelancer, ACTIONS } from './freelancer-reducer'; import { IFilter, ACTIONS as FilterACTIONS } from './../filter/filter-reducer'; import * as Rx from 'RxJS'; @Component({ selector: 'app-freelancer-grid', templateUrl: './freelancer-grid.component', styleUrls: ['./freelancer-grid.component.scss'], }) export class FreelancerGridComponent implements OnInit { public freelancers: Rx.Observable<Array<IFreelancer>>; public filter: Rx.Observable<IFilter>; constructor(private store: Store<AppState>) { this.freelancers = Rx.Observable.combineLatest(store.select('freelancers'), store.select('filter'), this.applyFilter); } applyFilter(freelancers: Array<IFreelancer>, filter: IFilter): Array<IFreelancer> { return freelancers .filter(x => !filter.name || x.name.toLowerCase().indexOf(filter.name.toLowerCase()) !== -1) .filter(x => !filter.email || x.email.toLowerCase().indexOf(filter.email.toLowerCase()) !== -1) } ngOnInit() { } delete(freelancer) { this.store.dispatch({ type: ACTIONS.DELETE_FREELANCER, payload: freelancer, }) } }ไม่มีอะไรซับซ้อนไปกว่านั้น
อีกครั้ง คุณใช้พลังของ RxJS เพื่อรวมตัวกรองและสถานะของ freelancer
อันที่จริง combineLatest จะทำงานหากหนึ่งในสองรายการที่สังเกตได้เริ่มทำงาน จากนั้นรวมแต่ละสถานะโดยใช้ฟังก์ชัน applyFilter มันส่งกลับสิ่งที่สังเกตได้ใหม่ที่ทำเช่นนั้น เราไม่ต้องเปลี่ยนบรรทัดอื่นของรหัส
สังเกตว่าส่วนประกอบไม่สนใจวิธีการรับ แก้ไข หรือจัดเก็บตัวกรอง มันฟังมันเหมือนกับที่จะทำเพื่อรัฐอื่น เราเพิ่งเพิ่มฟังก์ชันตัวกรองและไม่ได้เพิ่มการพึ่งพาใหม่
ทำให้มันเปล่งประกาย
จำได้ไหมว่าการใช้ Ngrx นั้นยอดเยี่ยมจริงๆ เมื่อเราต้องจัดการกับข้อมูลแบบเรียลไทม์? มาเพิ่มส่วนนั้นในแอปพลิเคชันของเราและดูว่ามันจะเป็นอย่างไร
แนะนำ freelancers-service ซ์
ng generate service freelancerบริการ freelancer จะจำลองการดำเนินการตามเวลาจริงกับข้อมูลและควรมีลักษณะดังนี้
import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; import { AppState, IFreelancer, ACTIONS } from './freelancer-grid/freelancer-reducer'; import { Http, Response } from '@angular/http'; @Injectable() export class RealtimeFreelancersService { private USER_API_URL = 'https://randomuser.me/api/?results=' constructor(private store: Store<AppState>, private http: Http) { } private toFreelancer(value: any) { return { name: value.name.first + ' ' + value.name.last, email: value.email, thumbail: value.picture.large, } } private random(y) { return Math.floor(Math.random() * y); } public run() { this.http.get(`${this.USER_API_URL}51`).subscribe((response) => { this.store.dispatch({ type: ACTIONS.FREELANCERS_LOADED, payload: response.json().results.map(this.toFreelancer) }) }) setInterval(() => { this.store.select('freelancers').first().subscribe((freelancers: Array<IFreelancer>) => { let getDeletedIndex = () => { return this.random(freelancers.length - 1) } this.http.get(`${this.USER_API_URL}${this.random(10)}`).subscribe((response) => { this.store.dispatch({ type: ACTIONS.INCOMMING_DATA, payload: { ADD: response.json().results.map(this.toFreelancer), DELETE: new Array(this.random(6)).fill(0).map(() => getDeletedIndex()), } }); this.addFadeClassToNewElements(); }); }); }, 10000); } private addFadeClassToNewElements() { let elements = window.document.getElementsByClassName('freelancer'); for (let i = 0; i < elements.length; i++) { if (elements.item(i).className.indexOf('fade') === -1) { elements.item(i).classList.add('fade'); } } } }บริการนี้ไม่สมบูรณ์แบบ แต่ทำในสิ่งที่ทำ และเพื่อจุดประสงค์ในการสาธิต ทำให้เราสามารถสาธิตบางสิ่งได้
ประการแรก บริการนี้ค่อนข้างง่าย มันสอบถาม API ผู้ใช้และผลักผลลัพธ์ไปยังร้านค้า เป็นเรื่องง่ายและคุณไม่จำเป็นต้องคิดว่าข้อมูลจะไปที่ใด มันไปที่ร้าน ซึ่งเป็นสิ่งที่ทำให้ Redux มีประโยชน์และอันตรายในเวลาเดียวกัน—แต่เราจะกลับมาที่นี่ในภายหลัง ทุก ๆ สิบวินาที บริการจะเลือก freelancer สองสามคน และส่งการดำเนินการเพื่อลบพวกเขาพร้อมกับการดำเนินการไปยัง freelancer อื่นๆ สองสามราย
หากเราต้องการให้ตัวลดของเราจัดการกับมันได้ เราจำเป็นต้องแก้ไขมัน:
import { Action } from '@ngrx/store'; export interface AppState { freelancers : Array<IFreelancer> } export interface IFreelancer { name: string, email: string, } export const ACTIONS = { LOAD_FREELANCERS: 'LOAD_FREELANCERS', INCOMMING_DATA: 'INCOMMING_DATA', DELETE_FREELANCER: 'DELETE_FREELANCER', } export function freelancersReducer( state: Array<IFreelancer> = [], action: Action): Array<IFreelancer> { switch (action.type) { case ACTIONS.INCOMMING_DATA: action.payload.DELETE.forEach((index) => { state.splice(state.indexOf(action.payload), 1); }) return Array.prototype.concat(action.payload.ADD, state); case ACTIONS.FREELANCERS_LOADED: // Return the new state with the payload as freelancers list return Array.prototype.concat(action.payload); case ACTIONS.DELETE_FREELANCER: // Remove the element from the array state.splice(state.indexOf(action.payload), 1); // We need to create another reference return Array.prototype.concat(state); default: return state; } }ตอนนี้เราสามารถดำเนินการดังกล่าวได้แล้ว
สิ่งหนึ่งที่แสดงให้เห็นในบริการนั้นก็คือ กระบวนการทั้งหมดของการเปลี่ยนแปลงสถานะที่ทำพร้อมกันนั้น เป็นสิ่งสำคัญมากที่จะต้องสังเกตว่า หากแอปพลิเคชันของสถานะไม่ตรงกัน ให้เรียกใช้ this.addFadeClassToNewElements(); จะไม่ทำงานเนื่องจากองค์ประกอบ DOM จะไม่ถูกสร้างขึ้นเมื่อมีการเรียกใช้ฟังก์ชันนี้
โดยส่วนตัวแล้ว ฉันพบว่ามีประโยชน์มาก เพราะมันช่วยเพิ่มความสามารถในการคาดเดา
การสร้างแอปพลิเคชัน, ปฏิกิริยาตอบสนอง
ผ่านบทช่วยสอนนี้ คุณได้สร้างแอปพลิเคชันปฏิกิริยาโดยใช้ Ngrx, RxJS และ Angular 2
อย่างที่คุณเห็น นี่คือเครื่องมือที่ทรงพลัง สิ่งที่คุณสร้างขึ้นที่นี่ยังสามารถเห็นได้ว่าเป็นการนำสถาปัตยกรรม Redux ไปใช้งาน และ Redux นั้นทรงพลังในตัวมันเอง อย่างไรก็ตาม มันก็มีข้อจำกัดบางประการเช่นกัน ในขณะที่เราใช้ Ngrx ข้อจำกัดเหล่านั้นย่อมสะท้อนให้เห็นในส่วนของแอปพลิเคชันที่เราใช้อย่างหลีกเลี่ยงไม่ได้
แผนภาพด้านบนเป็นสถาปัตยกรรมคร่าวๆ ที่คุณเพิ่งทำ
คุณอาจสังเกตเห็นว่าแม้ว่าองค์ประกอบบางอย่างจะมีอิทธิพลซึ่งกันและกัน แต่ก็มีความเป็นอิสระจากกัน นี่เป็นลักษณะเฉพาะของสถาปัตยกรรมนี้: คอมโพเนนต์มีการพึ่งพาร่วมกัน ซึ่งก็คือร้านค้า
อีกอย่างหนึ่งเกี่ยวกับสถาปัตยกรรมนี้คือ เราไม่เรียกฟังก์ชันแต่ส่งการดำเนินการ ทางเลือกอื่นสำหรับ Ngrx คือการสร้างบริการที่จัดการสถานะเฉพาะด้วยแอปพลิเคชันที่สังเกตได้ของคุณและเรียกใช้ฟังก์ชันในบริการนั้นแทนการดำเนินการ ด้วยวิธีนี้ คุณจะได้รับการรวมศูนย์และการตอบสนองของรัฐในขณะที่แยกสถานะที่มีปัญหา วิธีการนี้สามารถช่วยคุณลดค่าใช้จ่ายในการสร้างตัวลดและอธิบายการดำเนินการเป็นออบเจกต์ธรรมดา
เมื่อคุณรู้สึกว่าแอปพลิเคชันของคุณได้รับการอัปเดตจากแหล่งต่างๆ และเริ่มยุ่งเหยิง Ngrx คือสิ่งที่คุณต้องการ
