angular7.0使用參考手冊

angular 各功能應用demo

啓動項目

進入各demo項目下執行:

    cnpm i
    ng serve --open

常用指令

  *ngfor
  *ngIf
  插值表達式 {{}}
  屬性綁定 []
  事件綁定 ()
  //數據雙向綁定
  [(ngModel)]="hero.name"

app.module各模塊含義

組件

路由

1.路由配置

app.routing注入;


import { Routes, RouterModule } from '@angular/router'
import { Observable, of, } from 'rxjs';
import { catchError, map, tap ,switchMap,distinctUntilChanged,debounceTime,combineLatest} from 'rxjs/operators';

2.路徑配置

  const routes: Routes = [
    { path: '**', redirectTo: '/dashboard', pathMatch: 'full' },
    { path: 'heroes', component: HeroesComponent },
    { path: 'dashboard', component: DashboardComponent },
    { path: 'detail/:id', component: HeroDetailComponent 
      children:[{  //二級路由配置
        path:'/stores',
        component:StoreDetailComponent
      }]
    },
  ];

3.路由參數的獲取以及頁面跳轉

獲取參數的頁面引入並且注入:

  import { ActivatedRoute ,Router} from '@angular/router'
  import { Location } from '@angular/common';

  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private heroService: HeroService,
    private location: Location
  ) {}

  this.location.back();//返回上一頁
  this.router.navigateByUrl(path:string) //js實現跳轉到指定路徑

之後再this.route裏面即可獲取需要的路由信息

  let obsCombined = Observable.combineLatest(this.route.params, this.route.queryParams, (params, qparams) => ({
            params,
            qparams
        }));


  obsCombined.subscribe(ap => {
      console.log(ap);
      let params = ap['params'];
      let qparams = ap['qparams'];
      alert(qparams['title']);
      alert(params['id']);
  });

獲取id舉例:

  this.route.paramMap.subscribe(params => {
    let id = params.get('productId');
  }

禁止瀏覽器history

 <a routerLink='/sharing'  skipLocationChange="false">

表單

配置

需要在app.module中加入ReactiveFormsModule模塊

import { FormsModule, ReactiveFormsModule } from '@angular/forms';

imports: [
  FormsModule,
  ReactiveFormsModule
]

響應式表單html結構
注意點:

[formGroup]處寫上表單名稱

響應式表單中的每個控件都必須要有自己的formControlName,而且不能加上雙向綁定[(ngModel)]

(ngModelChange)事件會在控件值發生改變時觸發,參數$event實際爲改變後的值

如果控件需要要驗證信息,則要在控件的html後面加上nz-form-explain標籤,裏面寫具體的錯誤提示

<form nz-form [formGroup]="qrcodeForm">
  <nz-form-item class="nz-form-item">

    <nz-form-label class="nz-formlable" nzFor="type" nzRequired>二維碼用途</nz-form-label>

    <nz-form-control>

      <nz-radio-group name="type" [nzDisabled]="id" formControlName="type" (ngModelChange)="qrcodeTypeChange($event)">
        <label nz-radio nzValue="1">掃碼後關注公衆號</label>
        <label nz-radio nzValue="2">掃碼後參與指定活動</label>
      </nz-radio-group>
      
    </nz-form-control>

  </nz-form-item>

  <nz-form-item class="nz-form-item">

    <nz-form-label class="nz-formlable" nzFor="fissionId" nzRequired>模板活動</nz-form-label>

    <nz-form-control>

      <nz-select name="fissionId" [nzDisabled]="id" formControlName="fissionId" nzAllowClear
        style="width: 260px;" nzPlaceHolder="請選擇模板活動">
        <nz-option *ngFor="let opt of fissionList" nzValue="{{ opt.id }}" nzLabel="{{ opt.name }}">
        </nz-option>
      </nz-select>

      <nz-form-explain *ngIf="qrcodeForm.get('fissionId')?.dirty && qrcodeForm.get('fissionId')?.errors">
        請選擇模板活動!
      </nz-form-explain>

    </nz-form-control>

  </nz-form-item>
</form>
```

## 表單初始化
注意點:

如果控件初始值爲空,輸入框input的值要設爲'',其他控件的值都要設爲null,否則可能會有placeholder不顯示的問題

這裏寫的控件名要一一與html上的formControlName對應

控件可以添加自己寫的驗證規則

```ts
  import { FormBuilder, Validators, FormGroup } from '@angular/forms';

  // qrcodeForm: FormGroup;
  // fb: FormBuilder

  this.qrcodeForm = this.fb.group({
    type: ["1", [Validators.required]], // 二維碼用途
    action: ["2", [Validators.required]], // 二維碼類型
    name: ['', [Validators.required, this.qrcodeNameValidator]], // 二維碼名稱
    fissionId: [null], // 模板活動
    weixinMpId: [null, [Validators.required]], // 公衆號
    expireDays: ['', [Validators.required, this.expireDaysValidator]], // 有效天數
    sourceId: [null, [Validators.required]], // 來源
    sourceContent: ['', [this.sourceContentValidator]],
    replyContent: ['']
  });
```

## 表單取值
```ts
// 取表單裏的單個值
let name = this.qrcodeForm.controls.name.value;

// 取表單裏的所有值
let data = this.qrcodeForm.getRawValue();
表單賦值
patchValue的參數可以是多個,也可以是單個,通常都用patchValue賦值

setValue的參數必須爲表單裏所有的控件值,且如果賦的值驗證不通過會提示錯誤信息

// patchValue
this.qrcodeForm.patchValue({
  type: res.type.toString(), // 二維碼用途
  action: res.action.toString(), // 二維碼類型
  name: res.name, // 二維碼名稱
  fissionId: res.fissionId ? res.fissionId.toString() : null, // 模板活動
  weixinMpId: res.weixinMpId.toString(), // 公衆號
});

// setValue
this.qrcodeForm.setValue({
  type: res.type.toString(), // 二維碼用途
  action: res.action.toString(), // 二維碼類型
  name: res.name, // 二維碼名稱
  fissionId: res.fissionId ? res.fissionId.toString() : null, // 模板活動
  weixinMpId: res.weixinMpId.toString(), // 公衆號
  expireDays: res.expireDays ? res.expireDays.toString() : '1', // 有效天數
  sourceId: res.sourceId.toString(), // 來源
  sourceContent: res.sourceContent, // 來源內容
  replyContent: res.replyContent
});
```

## 表單增加和刪除控件項
```ts
// 增加控件項
this.qrcodeForm.addControl('point', this.fb.control(null, Validators.required));

// 刪除控件項
this.qrcodeForm.removeControl('point');
表單動態增加和刪除驗證
頁面有時候在選中一個下拉框選項時,需要隱藏或顯示另外一個控件,這個控件需要把驗證加上或刪除,否則頁面雖然看不到這個控件了,但提交表單仍可能通不過驗證

clearValidators後,要調用updateValueAndValidity重新計算控件值並更新驗證狀態

// 增加驗證
this.qrcodeForm.controls.fissionId.setValidators(Validators.required);

// 刪除驗證
this.qrcodeForm.controls.fissionId.clearValidators();
this.qrcodeForm.controls.fissionId.updateValueAndValidity();
提交表單
提交表單需要先在前端進行驗證,需要調用public-method裏的表單驗證方法,校驗不通過時,不用去請求接口

通過判斷this.qrcodeForm.valid可以知道驗證狀態,true爲校驗通過,false爲校驗不通過

import { FormMethod } from '../../../common/public-method';
// 表單部分的通用方法
export const FormMethod = {

  /**
   * 更新表單狀態
   * @param form FormGroup
   */

  updateFormStatus: (form: FormGroup): void => {
    // tslint:disable-next-line:forin
    for (const i in form.controls) {
      form.controls[i].markAsDirty();
      form.controls[i].updateValueAndValidity();
    }
  }
};
```

// 驗證每個組件,如果有錯誤會顯示信息
FormMethod.updateFormStatus(this.qrcodeForm);

// 校驗不通過,返回
if (!this.qrcodeForm.valid) {
  return;
}
```

TS:

```ts
  import { FormBuilder } from '@angular/forms'; 
  export class CartComponent implements OnInit {

    checkoutForm:any;
    
    constructor(
      private formBuilder: FormBuilder
    ) { 

      this.checkoutForm = this.formBuilder.group({
        name: '',
        address: ''
      });
    }

    onSubmit(formData:object){
      //獲取到要提交的表單的數據
      console.log('form data: ',formData);
      //購物車數據提交後需要清空
      this.cart=this.cartService.clearCart();
      //表單重置
      this.checkoutForm.reset()
    }
  }
```

HTML:
```html

  <form class="form" [formGroup]="checkoutForm" (ngSubmit)="onSubmit(checkoutForm.value)">
    <div class="form_cow">
      <label>Name</label>
      <input type="text" formControlName="name">
    </div>
    
    <div class="form_cow"> 
      <label>Address</label>
      <input type="text" formControlName="address">
    </div>
    
    <button class="button" nz-button nzType="primary" type="submit">submit</button>
  </form>    

```

### 防抖

```ts
  import { Observable, of, } from 'rxjs';
  import { catchError, map, tap ,switchMap,distinctUntilChanged,debounceTime,combineLatest} from 'rxjs/operators';

  private searchText$ = new Subject<string>();
  
  search(packageName: string) {
    this.searchText$.next(packageName);
  }
  
  ngOnInit() {
    this.packages$ = this.searchText$.pipe(
      debounceTime(500),
      distinctUntilChanged(),
      switchMap(packageName =>
        this.searchService.search(packageName))
    );
  }
  
  constructor(private searchService: PackageSearchService) { }

```



## http

app.module;
```ts
  import {HttpClientModule,HttpClientJsonpModule  } from "@angular/common/http";

```

使用的模塊引入,一般爲某個service:

```ts
  import { HttpClient, HttpParams,HttpHeaders } from '@angular/common/http';
  import { Observable, of } from 'rxjs';
  import { catchError, map, tap ,filter} from 'rxjs/operators';


  constructor(
    private http:HttpClient
  ) { }

  //get獲取數據
  public get(path:string,params:any){

    let url=environment.baseUrl+path;

    let dataParams = new HttpParams();
    for (const key in params) {
      if (params[key] === false || params[key]) {
        dataParams = dataParams.set(key, params[key]);
      }
    }

    // 底層http調用
    return this.http.get(url, { params: dataParams })
    // 響應轉化爲Promise
    // .toPromise();
  }
  //post發送請求

  public post(path:string,option:any){
    const httpOptions = {
      headers: new HttpHeaders({
        'Content-Type':  'application/json',
        'Authorization': 'my-auth-token'
      })
    }

    return this.http.post<Hero>(path, data, httpOptions)
  }

  //請求頭是不能修改的,只能重置
  httpOptions.headers = httpOptions.headers.set('Authorization', 'my-new-auth-token');

```

返回的數據都是一個Observable對象,使用subscribe()來獲取
調用該服務;

```ts

  //請求json數據時
  this.service.post(path,parma).subscribe(
    (data: Config) => this.config = { ...data }, // success path
    error => this.error = error // error path
  )
  //請求非json數據時
  this.service.post(path,parma).pipe(
    tap(
        data => this.log(filename, data),
        error => this.logError(filename, error)
    );
  ).subscribe()

```




## RXJS

ReactiveX來自微軟,它是一種針對異步數據流的編程

```ts
const ob = Observable.fromPromise(somePromise); // Promise轉爲Observable
const promise = someObservable.toPromise(); // Observable轉爲Promise
```

優點:
- 一次可發送多個請求
- 可以取消訂閱

### Observable 用法
```ts
 import {Observable} from 'rxjs' 

  function xxx(){
    return new Observable((observable)=>{
      this.httpService.get('/blog',{},false).then(((res:Respones)=>{
        if(res.code===200){
          observable.next(res)
          
        }else if(res.status==='ok'){
          observable.next(res)
        }else{
          observable.error(res)
        }
      }))
    })
  }

  //調用
  xxx().pipe(
    filter((res)=>{
      return res.code>0
    }
  )
      //map
      xxx().pipe(
        map((res)=>{
          return res.data
      }
  )

  ).subscribe((res)=>{
    console.log(res);
    //這裏被訂閱了纔會執行
  })




```

### 使用 RXJS防抖
```js
var text = document.querySelector('#text');
var inputStream = Rx.Observable.fromEvent(text, 'keyup') //爲dom元素綁定'keyup'事件
                    .debounceTime(250) // 防抖動
                    .pluck('target', 'value') // 取值
                    .switchMap(url => Http.get(url)) // 將當前輸入流替換爲http請求
                    .subscribe(data => render(data)); // 接收數據

```


# 總結以及要注意地方

### 父子組件的傳值
父組件
```ts

    import { Component, OnInit ,ViewChild} from '@angular/core';
    @ViewChild('demo') son:any;
    
    console.log("son:",this.son.str);

```
子組件
```ts
    import { Component, OnInit ,ViewChild,Input,Output,EventEmitter} from '@angular/core';
    
    
    
      public str:string="song daat00000"
    
      @Input () parentData:any;
      @Input() fromFun:any;
      @Output() testSon=new EventEmitter();
    
    
    
    
        console.log(this.parentData);
    
    
      ngAfterViewInit() {
        //在這個鉤子中來操作DOM
        console.log('demo view load');
        
        let demo:any=document.getElementById('demo')
        //console.log(demo);
        console.log(this.myDemo.nativeElement.innerHTML);
      }
    
      //方法
      test(){
        this.testSon.emit('son emit')
      }
```


### 渲染
獲取數據之前頁面會渲染一次,直接渲染未定義的數據會報錯,所以渲染數據之前要用*ngIf先進行判斷;

組件間通信
組件之間的交互方式,通常有以下幾種:

1 通過輸入型綁定把數據從父組件傳到子組件
```ts
@Input()
子組件:

import { Component, Input } from '@angular/core';

import { Hero } from './hero';

@Component({
  selector: 'app-hero-child',
  template: `
    <h3>{{hero.name}} says:</h3>
    <p>I, {{hero.name}}, am at your service, {{masterName}}.</p>
  `
})
export class HeroChildComponent {
  @Input() hero: Hero;
  @Input('master') masterName: string;
}
父組件:

import { Component } from '@angular/core';

import { HEROES } from './hero';

@Component({
  selector: 'app-hero-parent',
  template: `
    <h2>{{master}} controls {{heroes.length}} heroes</h2>
    <app-hero-child *ngFor="let hero of heroes" 
      [hero]="hero" 
      [master]="master">
    </app-hero-child>
  `
})
export class HeroParentComponent {
  heroes = HEROES;
  master = 'Master';
}
```
## 2 通過ngOnChanges()來截聽輸入屬性值的變化
使用 OnChanges 生命週期鉤子接口的 ngOnChanges() 方法來監測輸入屬性值的變化並做出迴應。

子組件:
```ts
import { Component, Input, OnChanges, SimpleChange } from '@angular/core';

@Component({
  selector: 'app-version-child',
  template: `
    <h3>Version {{major}}.{{minor}}</h3>
    <h4>Change log:</h4>
    <ul>
      <li *ngFor="let change of changeLog">{{change}}</li>
    </ul>
  `
})
export class VersionChildComponent implements OnChanges {
  @Input() major: number;
  @Input() minor: number;
  changeLog: string[] = [];

  ngOnChanges(changes: {[propKey: string]: SimpleChange}) {
    let log: string[] = [];
    for (let propName in changes) {
      let changedProp = changes[propName];
      let to = JSON.stringify(changedProp.currentValue);
      if (changedProp.isFirstChange()) {
        log.push(`Initial value of ${propName} set to ${to}`);
      } else {
        let from = JSON.stringify(changedProp.previousValue);
        log.push(`${propName} changed from ${from} to ${to}`);
      }
    }
    this.changeLog.push(log.join(', '));
  }
}
```
父組件:

```ts
import { Component } from '@angular/core';

@Component({
  selector: 'app-version-parent',
  template: `
    <h2>Source code version</h2>
    <button (click)="newMinor()">New minor version</button>
    <button (click)="newMajor()">New major version</button>
    <app-version-child [major]="major" [minor]="minor"></app-version-child>
  `
})
export class VersionParentComponent {
  major = 1;
  minor = 23;

  newMinor() {
    this.minor++;
  }

  newMajor() {
    this.major++;
    this.minor = 0;
  }
}
```
3 EventEmitter彈射事件
父組件監聽子組件的事件,子組件暴露一個 EventEmitter 屬性,當事件發生時,子組件利用該屬性 emits(向上彈射)事件。父組件綁定到這個事件屬性,並在事件發生時作出迴應。子組件的 EventEmitter 屬性是一個輸出屬性,通常帶有@Output 裝飾器,就像在 VoterComponent 中看到的。

子組件:

import { Component, EventEmitter, Input, Output } from '@angular/core';

@Component({
  selector: 'app-voter',
  template: `
    <h4>{{name}}</h4>
    <button (click)="vote(true)"  [disabled]="didVote">Agree</button>
    <button (click)="vote(false)" [disabled]="didVote">Disagree</button>
  `
})
export class VoterComponent {
  @Input()  name: string;
  @Output() voted = new EventEmitter<boolean>();
  didVote = false;

  vote(agreed: boolean) {
    this.voted.emit(agreed);
    this.didVote = true;
  }
}
父組件:

import { Component }      from '@angular/core';

@Component({
  selector: 'app-vote-taker',
  template: `
    <h2>Should mankind colonize the Universe?</h2>
    <h3>Agree: {{agreed}}, Disagree: {{disagreed}}</h3>
    <app-voter *ngFor="let voter of voters" 
      [name]="voter" 
      (voted)="onVoted($event)">
    </app-voter>
  `
})
export class VoteTakerComponent {
  agreed = 0;
  disagreed = 0;
  voters = ['Mr. IQ', 'Ms. Universe', 'Bombasto'];

  onVoted(agreed: boolean) {
    agreed ? this.agreed++ : this.disagreed++;
  }
}
4 ViewChild
父組件調用@ViewChild(),如果父組件的類需要讀取子組件的屬性值或調用子組件的方法,可以把子組件作爲 ViewChild,注入到父組件裏面。

子組件:

import { Component, OnDestroy, OnInit } from '@angular/core';

@Component({
  selector: 'app-countdown-timer',
  template: '<p>{{message}}</p>'
})
export class CountdownTimerComponent implements OnInit, OnDestroy {

  intervalId = 0;
  message = '';
  seconds = 11;

  clearTimer() { clearInterval(this.intervalId); }

  ngOnInit()    { this.start(); }
  ngOnDestroy() { this.clearTimer(); }

  start() { this.countDown(); }
  stop()  {
    this.clearTimer();
    this.message = `Holding at T-${this.seconds} seconds`;
  }

  private countDown() { 
    this.clearTimer();
    this.intervalId = window.setInterval(() => {
      this.seconds -= 1;
      if (this.seconds === 0) {
        this.message = 'Blast off!';
      } else {
        if (this.seconds < 0) { this.seconds = 10; } // reset
        this.message = `T-${this.seconds} seconds and counting`;
      }
    }, 1000);
  }
}
父組件:

import { AfterViewInit, ViewChild } from '@angular/core';
import { Component }                from '@angular/core';
import { CountdownTimerComponent }  from './countdown-timer.component';

@Component({
  selector: 'app-countdown-parent-vc',
  template: `
  <h3>Countdown to Liftoff (via ViewChild)</h3>
  <button (click)="start()">Start</button>
  <button (click)="stop()">Stop</button>
  <div class="seconds">{{ seconds() }}</div>
  <app-countdown-timer></app-countdown-timer>
  `,
  styleUrls: ['../assets/demo.css']
})
export class CountdownViewChildParentComponent implements AfterViewInit {

  @ViewChild(CountdownTimerComponent)
  private timerComponent: CountdownTimerComponent;

  seconds() { return 0; }

  ngAfterViewInit() {
    // Redefine `seconds()` to get from the `CountdownTimerComponent.seconds` ...
    // but wait a tick first to avoid one-time devMode
    // unidirectional-data-flow-violation error
    setTimeout(() => this.seconds = () => this.timerComponent.seconds, 0);
  }

  start() { this.timerComponent.start(); }
  stop() { this.timerComponent.stop(); }
}
5 RxJS的Observable
父組件和子組件通過服務來通訊,父組件和它的子組件共享同一個服務,利用該服務在組件家族內部實現雙向通訊。

該服務實例的作用域被限制在父組件和其子組件內。這個組件子樹之外的組件將無法訪問該服務或者與它們通訊。

服務:

import { Injectable } from '@angular/core';
import { Subject }    from 'rxjs';

@Injectable()
export class MissionService { 

  // Observable string sources
  private missionAnnouncedSource = new Subject<string>();
  private missionConfirmedSource = new Subject<string>();

  // Observable string streams
  missionAnnounced$ = this.missionAnnouncedSource.asObservable();
  missionConfirmed$ = this.missionConfirmedSource.asObservable();

  // 父組件調用子組件
  announceMission(mission: string) {
    this.missionAnnouncedSource.next(mission);
  }
  //子組件調用父組件
  confirmMission(astronaut: string) {
    this.missionConfirmedSource.next(astronaut);
  }
}
子組件:

import { Component, Input, OnDestroy } from '@angular/core';

import { MissionService } from './mission.service';
import { Subscription }   from 'rxjs';

@Component({
  selector: 'app-astronaut',
  template: `
    <p>
      {{astronaut}}: <strong>{{mission}}</strong>
      <button
        (click)="confirm()" 
        [disabled]="!announced || confirmed">
        Confirm
      </button>
    </p>
  `
})
export class AstronautComponent implements OnDestroy {
  @Input() astronaut: string;
  mission = '<no mission announced>';
  confirmed = false;
  announced = false;
  subscription: Subscription;

  constructor(private missionService: MissionService) {
    this.subscription = missionService.missionAnnounced$.subscribe(
      mission => {
        this.mission = mission;
        this.announced = true;
        this.confirmed = false;
    });
  }

  confirm() {
    this.confirmed = true;
    this.missionService.confirmMission(this.astronaut);
  }

  ngOnDestroy() {
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();
  }
}
父組件:

import { Component }          from '@angular/core';

import { MissionService }     from './mission.service';

@Component({
  selector: 'app-mission-control',
  template: `
    <h2>Mission Control</h2>
    <button (click)="announce()">Announce mission</button>
    <app-astronaut *ngFor="let astronaut of astronauts" 
      [astronaut]="astronaut">
    </app-astronaut>
    <h3>History</h3>
    <ul>
      <li *ngFor="let event of history">{{event}}</li>
    </ul>
  `,
  providers: [MissionService]
})
export class MissionControlComponent {
  astronauts = ['Lovell', 'Swigert', 'Haise'];
  history: string[] = [];
  missions = ['Fly to the moon!',
              'Fly to mars!',
              'Fly to Vegas!'];
  nextMission = 0;

  constructor(private missionService: MissionService) {
    missionService.missionConfirmed$.subscribe(
      astronaut => {
        this.history.push(`${astronaut} confirmed the mission`);
      });
  }

  announce() {
    let mission = this.missions[this.nextMission++];
    this.missionService.announceMission(mission);
    this.history.push(`Mission "${mission}" announced`);
    if (this.nextMission >= this.missions.length) { this.nextMission = 0; }
  }
}







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章