1. 程式人生 > 實用技巧 >angular學習進階(十五)

angular學習進階(十五)

resolve(解決守衛)

保證了資料獲取後在進行路由跳轉,防止因為資料延遲而出現的空元件情況

簡單的理解成解決延遲守衛

建立一個介面

product/product.ts
ng g i product/product.ts
export interface Product {
  id: number;
  name: string;
  price: number
}

建立一個服務

ng g s product/product

import {Injectable} from '@angular/core';
import {Observable, of} from 'rxjs';
import {Product} from './product';
import {delay} from 'rxjs/internal/operators/delay';
import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router';

@Injectable({
  providedIn: 'root'
})
export class ProductService implements Resolve<Product[]> {

  constructor() {
  }
// 模擬請求
  getList(): Observable<Product[]> {
    return of([
      {id: 12, name: 'sss', price: 124},
      {id: 13, name: 'xxx', price: 1231},
      {id: 14, name: 'bbb', price: 132},
    ]).pipe(delay(2000));
  }

// 把請求放在延遲函式中
  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Product[]> | Promise<Product[]> | Product[] {
    return this.getList();// 可以對這個值進行二次加工
  }
}

app-routing.module

 {
    path: 'three',
    component: ThreeComponent,
    resolve: {products: ProductService}
  }

從其他頁面跳轉到three頁面

<a [routerLink]="['three']">three</a>

ThreeComponent 頁面拿到這個值

export class ThreeComponent implements OnInit {

  constructor(private route:ActivatedRoute) { }
  ngOnInit(): void {
    // 拿到獲取的資料
    console.log(this.route.snapshot.data['products']);
  }
}

rxjs 中的map和switchMap區別

switchMap 建立一個內部課觀察物件,進行訂閱併發出其值為可觀察者

of(1, 2, 3, 4).pipe(
      map(v => v * 2),
      toArray()
    ).subscribe(res=>{
      console.log(res);
     //[2, 4, 6, 8]
    })

of(1, 2, 3, 4, 5).pipe(
      switchMap(val => of(val * 2)),
      toArray()
    ).subscribe(res=>{
      console.log(res);
      //  [2, 4, 6, 8, 10]  
    })

點選按鈕,每1s傳送一個值,再次點選,取消之前的訂閱重新開始傳送值

<button #button>click</button>

@ViewChild('button') button;
  ngAfterViewInit(): void {
    fromEvent(this.button.nativeElement,'click').pipe(
      switchMap(()=>interval(1000))
    ).subscribe(res=>{
      console.log(res);
    })
  }

比如拿到xxx/:id

this.activateRoute.paramMap.pipe(
	switchMap(params=>params.get('id'))
).subscribe(res=>{
    console.log(id)
})

from表單觸發某個值,提交請求

this.mainForm.get("productCode").valueChanges
.pipe(
  debounceTime(700),
  switchMap(val => {
    return this.queryDepositData();
  })
)
.subscribe(data => {
  this.product=data;
})

Rxjs 學習原理圖

https://thinkrx.io/rxjs/

看著原理圖學習,會印象深刻一些

angular的相關命令細節

--flat 強制修改建立檔案的位置

ng g c --f  fix/hello
在app的fix/hello位置下建立一個hello元件

-export 新增到對應模組的exports

ng  g c  home --export
// 會新增到對應模組的exports

--prefix 新增元件選擇器字首

ng g c --p=d  hello
// 選擇器的名稱  d-hello

selector: 'd-hello',

--selector 替換元件選擇器

ng g c fix/hello-e --selector=hello-g

  selector: 'hello-g',// 而不是hello-e

建立一個class 檔案

ng g class xxx

建立模組

ng g m south    //會建立一個south模組
ng g m south --routing // 同時建立模組和路由
如果建立了模組沒有沒有建立路由,可以使用下面的命令
ng g m -f south --routing

模板字串規範

不好

{{num/60}}

推薦

{{num}}

get num(){
    return this.total/60
}

父子元件傳遞

不好

{{getOffer(amount)}}

@Input() amount:number

getOffer(amount:number){
    if(amount > 3500  && amount < 4999)
       return `You will get 20% off on 5k purchase`;
    else if (amount > 5000)
       return `You will get 30% off on 7k purchase`;
    else
       return `5% off on your existing purchase.`;
}

推薦

{{offerMessage}}

offerMessage: string;

@Input() set amount(value: number) {
    let message: string = '';
    if (value > 5000)
        message = `You will get 30% off on 7k purchase`;
    else if (value > 3500 && value < 4999)
        message = `You will get 20% off on 5k purchase`;
    else
        message = `5% off on your existing purchase.`;
    this.offerMessage = message;
}

正常情況下這樣也行

{{getGrade(student)}}

getGrade(marks: number) {
    let grade: string = 'F';
    if (marks >= 85)
        grade = 'S'
    else if (marks > 60 && marks < 85)
        grade = 'A'
    return grade;
}

在使用ngFor 繫結資料的時候

<tr *ngFor="let student of students; trackBy:trackByFn">
    <td>{{student.name}}</td>
    <td>{{student.grade}}</td>
</tr>

trackByFn(index, item) {
    return item.id;
}

RXjs 具體api的實際運用

debounceTime

輸入資料搜尋資料的時候,而不按搜尋的資料按鈕,請求下拉框

我們發現當修改一個的時候回立刻請求值,所以我們可以加上,在一段時間內,修改只會發一次請求

this.from.get('xxx').valueChanges.pipe(
	debounceTime(300),
    switchMap(key=>this.http.get('xxx',key))
)

distinctUntilChanged

噹噹前值和之前值有變化才會傳送訂閱

<form [formGroup]="myForm">
  <input formControlName="firstName">
</form>

 constructor(private fb: FormBuilder,) {
  }

 myForm = this.fb.group({
      firstName: ['']
    }
  );

ngOnInit(): void {
    this.myForm.get('firstName').valueChanges.pipe(
      debounceTime(300),// 當300ms沒有新資料,才會執行
      distinctUntilChanged()// 當[內容真正有修改]時,才執行搜尋
    ).subscribe(res=>{
      console.log(res);
    })
  }

filter

 this.myForm.get('firstName').valueChanges.pipe(
      debounceTime(300),// 當300ms沒有新資料,才會執行
      distinctUntilChanged(),// 當[內容真正有修改]時,才執行搜尋
      filter(key =>key.length>4)// 當之大於4的是偶,才會進行搜尋
    ).subscribe(res => {
      console.log(res);
    });

Subject

  public sub = new Subject();
	sub.subscribe(res=>{
      console.log(res); //2,3,5,6
    })
	this.sub.next(2);
    this.sub.next(3);
	this.sub.subscribe(res=>{
      console.log(res); //5,6
    })
    this.sub.next(5)
    this.sub.next(6)

ReplaySubject

// 指定記錄次數  	
public subject = new ReplaySubject(2);
	ngOnInit(): void {
        this.subject.next(1);
        this.subject.subscribe(res=>{
          console.log(res); //1,2,3,4
        })
        this.subject.next(2)
        this.subject.next(3)
        this.subject.subscribe(res=>{
          console.log(res);//2,3,4 (2,3是最近2次重播的值)
        })
        this.subject.next(4)
	}	

asyncSubject

只有在complete() 才會被呼叫,訂閱到[最後一次next()]的內容

  public asySub = new AsyncSubject();
  ngOnInit(): void {
    this.asySub.subscribe(res=>{
      console.log(res); //3
    })
    this.asySub.next(1)
    this.asySub.next(2)
    this.asySub.next(3)
    this.asySub.complete()
  }

處理錯誤

subject.subscribe(
res=>{
// 成功    
},error=>{
// 失敗    
})

修改,記錄錯誤類似於try...catch

this.http.get('....').pipe(
	catchError(err=>{
        console.log(err)
        return of([])
    })
)

如果想主動丟擲錯誤

this.http.get('....').pipe(
    tap(data=>{
        if(data.length==0){
            throwError('no data')
        }
    }),
	catchError(err=>{
        console.log(err)
        return of([])
    })
)

finalize 放置到最後執行的程式,這樣this.lading=false 就不用subscribe裡面了

this.http.get('...').subscribe(res=>{
    this.loading=false;
},error=>{
    this.loading=false;
})
修改
this.http.get('...').pipe(
	finalize(()=>{
        // 不管出現什麼錯誤等等等,都一定會進入finalize裡面
        this.loading=false
    })
)