angular的學習進階(十七)
阿新 • • 發佈:2020-08-18
虛擬滾動條
響應式應用
有趣的程式碼
https://1loc.dev/
rxjs 運算子相互的區別
export type Shape = '1' | '2';
export type Card = '3' | '4';
public shapes$ = new Subject<Shape>();
public cards$ = new Subject<Card>();
zip 每次壓縮的可觀察值,一組一組發
zip(of(1),of(2)).subscribe(console.log) ngOnInit(): void { zip(this.shapes$, this.cards$).subscribe(console.log); this.shapes$.next('1'); this.cards$.next('3'); this.shapes$.next('2'); this.cards$.next('3'); this.shapes$.next('1'); this.cards$.next('4'); // 不需要complete 也能執行 } ["1", "3"] ["2", "3"] ["1", "4"]
forkJoin 僅在完成之後才發出最後一組的值
forkJoin(of(1),of(2)).subscribe(console.log) [1,2] ngOnInit(): void { forkJoin(this.shapes$, this.cards$).subscribe(console.log); this.shapes$.next('1'); this.cards$.next('3'); this.shapes$.next('2'); this.cards$.next('3'); this.shapes$.next('1'); this.cards$.next('4'); // 在complete完成後才會執行 this.shapes$.complete() this.cards$.complete() } ["1", "4"]
combineLatest 每次可觀察物件發出新值,會發出之前的值
ngOnInit(): void { combineLatest(this.shapes$, this.cards$).subscribe(console.log); this.shapes$.next('1'); this.cards$.next('3'); // 2次 this.shapes$.next('2'); this.cards$.next('3'); // 2次 this.shapes$.next('1'); this.cards$.next('4'); //1 次 // 不需要complete } // ["1", "3"] // ["2", "3"] // ["2", "3"] // ["1", "3"] // ["1", "4"]
distinctUntilChanged 運算子
number,string
[1, 2, 3, 1, 3] of(1, 1, 2, 2, 3, 3, 1, 3, 3, 3).pipe(distinctUntilChanged(), toArray()).subscribe(console.log);
// [1, 2, 3, 1, 3]
of('a', 'A', 'b', 'c', 'D', 'd', 'e', 'f', 'G', 'g', 'h').pipe(
// 把前一個字和後一個值轉化成小寫,所以相同的就可以跳過
// c ,p 可以理解成前一個值,後一個值
distinctUntilChanged((c, p) => c.toLowerCase() === p.toLowerCase()),
toArray()
).subscribe(console.log);
// ["a", "b", "c", "D", "e", "f", "G", "h"]
物件流
將對物件引用的比較,而不是物件屬性值得比較
from([obj,obj,obj]).pipe(distinctUntilChanged()).subscribe(console.log)
// {name: "xxx"}
但是我們寫成函式進行判斷
from([
{ name: 'Porsche', model: '911' },
{ name: 'Porsche', model: '911' },
{ name: 'Ferrari', model: 'F40' }
]).pipe(
distinctUntilChanged((p,n)=>{
return p.name===n.name&&p.model===n.model
}),
).subscribe(console.log)
// {name: "Porsche", model: "911"}
// {name: "Ferrari", model: "F40"}
兩個引數的情況
from([
{ name: 'Porsche', model: '911' },
{ name: 'PORSCHE', model: '911' },
{ name: 'Ferrari', model: 'F40' },
{ name: 'FERRARI', model: 'F40' },
]).pipe(
distinctUntilChanged(
(p,n)=>{
return p.toLowerCase()===n.toLowerCase()
},/*第二個函式你可以理解成 c=>v.name
:{} 我差點矇蔽了,其實就是做了typescript做的型別限制
*/
(car:{name:string,model:string})=>car.name
),
).subscribe(console.log)
// {name: "Porsche", model: "911"}
// {name: "Ferrari", model: "F40"}
封裝input 延遲輸入指令
ng g d delayed/delayed-input
import {Directive, OnInit,OnDestroy,ElementRef, EventEmitter, Input, Output} from '@angular/core';
import {from, fromEvent, Subject, timer} from 'rxjs';
import {debounce, distinctUntilChanged, takeUntil} from 'rxjs/operators';
@Directive({
selector: '[appDelayedInput]'
})
export class DelayedInputDirective implements OnInit,OnDestroy{
// 宣告的 destroy$,配合 takeUntil 運算子一起使用,當指令銷燬時,取消訂閱
private destroy$ = new Subject<void>();
// debounce 配合timer 使用 500ms發出一個值
@Input() delayTime = 500;
@Output() delayedInput = new EventEmitter<Event>();
// 拿到DOM
constructor(private elementRef: ElementRef<HTMLInputElement>) {
}
ngOnInit() :void{
fromEvent(this.elementRef.nativeElement,'input').pipe(
debounce(()=>timer(this.delayTime)),
distinctUntilChanged(null,
(event:Event)=>(event.target as HTMLInputElement).value
),
takeUntil(this.destroy$)
).subscribe(e=>this.delayedInput.emit(e))
}
ngOnDestroy() {
this.destroy$.next();
}
}
使用
<input type="text" appDelayedInput [delayTime]="600" (delayedInput)="search($event)">
search(event: Event) {
console.log((event.target as HTMLInputElement).value);
}
rxjs publish() 冷熱處理
let a = interval(1000).pipe(
tap(v => console.log('列印下'+v)),
take(5),
// 凍住
publish()
) as ConnectableObservable<number>
a.subscribe(res => {
console.log(res);
}, err => {
}, () => {
console.log('再見');
});
// 解除
a.connect();
// 列印下0
// 0
// 列印下1
// 1
// 列印下2
// 2
// 列印下3
// 3
// 列印下4
// 4
// 再見
rxjs delayWhen
of(10).pipe(delayWhen(_=>timer(1000))).subscribe(res=>{
console.log(res);
})
// 延遲傳遞值, 傳遞的引數是一個函式,這種方法好像比較實用
合併
let a=interval(1000).pipe(take(4))
let b=interval(1000).pipe(take(2))
zip
zip(a,b).subscribe(console.log)
// [ 0, 0]
// [ 1, 1]
最小分母數
merge
merge(a,b).subscribe(console.log)
// 0 兩次
// 1 兩次
// 1
// 2
// 3
concat
concat(a,b).subscribe(console.log)
// 0
// 1
// 2
// 3
// 0
// 1
rxjs 運算
// 觀察每次狀態的改變
of(1,2,3,4,5).pipe(scan((acc,val)=>acc+val,0)).subscribe(console.log)
// 1
// 3
// 6
// 10
// 15
// 計算點選的次數
fromEvent(this.ccc.nativeElement,'click').pipe(scan((acc,val)=>++acc,0)).subscribe(console.log)
of(1,2,3,4,5).pipe(max()).subscribe(console.log)
// 5
of(1,2,3,4,5).pipe(min()).subscribe(console.log)
// 1
of(1,2,3,4,5).pipe(reduce((acc,val)=>acc+val)).subscribe(console.log)
// 15
of({ name : 'chris' },{ age : 11 }).pipe(
reduce((acc,curr) => ({ ...acc, ...curr}))
).subscribe(console.log)
// {name: "chris", age: 11}
rxjs buffer緩衝
interval(200).pipe(
buffer(timer(1000))
).subscribe(console.log)
// [0,1,2,3]
//值發生一次
interval(1000).pipe(
buffer(interval(4000))
).subscribe(console.log)
// [0, 1, 2]
// [3, 4, 5, 6]
// [7, 8, 9, 10]
// bufferWhen 引數函式 可以用陣列長度看相隔多少s點選一次
interval(1000).pipe(
bufferWhen(() => fromEvent(this.ccc.nativeElement, 'click'))
).subscribe(console.log);
buferCount
收集緩衝區並在指定數量的值後,作為陣列發出
const src$ = merge(
of(1).pipe(delay(1000)),
of(2).pipe(delay(4000)),
of(3).pipe(delay(5000)),
);
src$.pipe(
bufferCount(3)
).subscribe(console.log)
=============
interval(1000).pipe(
bufferCount(3)
).subscribe(console.log)
// [0,1,2]
// [3,4,5]
// [6,7,8]