angular7.0使用指南

本文章宗旨在於快速開始angular開發,或者快速瞭解angular開發注意事項的文章javascript

環境搭建

  1. 腳手架安裝:npm i -g @angular/cli
  2. 新建項目:ng new my-app

若是安裝腳手架報錯,強制清理npm緩存後從新安裝css

組件與模板

當你下載好官方案列後,你可能對目錄都不太熟悉,先不要將關注點放在這裏,文章致力於如何快速上手一個angular項目。html

理解模板表達式上下文

表達式中的上下文變量是由如下三種組成:java

  1. 模板變量 (模板的 $event 對象模板輸入變量 (let hero)模板引用變量(#heroForm)
  2. 指令的上下文變量(指令中的屬性
  3. 組件的成員變量(組件實列

當存在相同的表達式變量優先順序:模板變量>>指令的上下文變量>>組件的成員變量npm

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

//my-app/src/app/app.component.ts
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  private data0:Number = 1121; 
  data1 = '<div>dddddd</div>';
  data2 = {
    aaa:222
  };
  data3(){
  };
  data4 = null;
  data5 = undefined;
  data6 = [1,2,3]
}
複製代碼
<div>
  <div>data0 :{{data0}}</div>
  <div>data1 :{{data1}}</div>
  <div>data2 :{{data2}}</div>
  <div>data3 :{{data3}}</div>
  <div>data4 :{{data4}}</div>
  <div>data5 :{{data5}}</div>
  <div>data6 :{{data6}}</div>
  <div>data7 :{{data7}}</div>
</div>

<!-- data0 :1121 data1 :<div>dddddd</div> data2 :[object Object] data3 :function () { } data4 : data5 : data6 :1,2,3 data7 : -->
複製代碼

理解HTML attribute 與 DOM property

先來看一個列子json

//html:<input type="text" value="a">
var outPutVal = function(){
    console.log('getAttribute:',inO.getAttribute('value'));
    console.log('inO.value:',inO.value);
}

window.onload = function(){
    var inO = document.querySelect('input');
    outPutVal(inO);
    //getAttribute: a
    //inO.value: a
    document.onclick = function(){
        //<input type="text" value="a"> 手動輸入value爲aaaaa後打印
        outPutVal(inO);
        //getAttribute: a
        //inO.value: aaaaa
    }
}
複製代碼

以上原生js展現了HTML attribute 與 DOM property 的區別:緩存

  1. 少許 HTML attribute 和 property 之間有着 1:1 的映射,如 id。
  2. 有些 HTML attribute 沒有對應的 property,如 colspan。
  3. 有些 DOM property 沒有對應的 attribute,如 textContent。

angular中模板綁定是經過 property 和事件來工做的,而不是 attributebash

特殊的attribute 綁定app

[attr.aria-label]="actionName"
<td [attr.colspan]="1 + 1">
複製代碼

指令

指令分爲三種:less

  1. 組件 — 擁有模板的指令
  2. 結構型指令 — 經過添加和移除 DOM 元素改變 DOM 佈局的指令
  3. 屬性型指令 — 改變元素、組件或其它指令的外觀和行爲的指令。

屬性指令

  1. ngClass
  2. ngStyle
  3. ngModel

結構型指令

  1. ngIf
  2. ngFor
  3. ngSwitch
ng-template *號語法
<div *ngIf="hero" class="name">{{hero.name}}</div>
-----------------------------------
<ng-template [ngIf]="hero">
  <div class="name">{{hero.name}}</div>
</ng-template>
複製代碼
<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">
  ({{i}}) {{hero.name}}
</div>
-----------------------------------
<ng-template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById">
  <div [class.odd]="odd">({{i}}) {{hero.name}}</div>
</ng-template>
複製代碼

在渲染視圖以前,Angular 會把 < ng-template > 及其包裹內容替換爲一個註釋

ng-container

會將兄弟元素歸爲一組而且ng-template 會無反作用的包裹內部元素

無反作用是什麼意思?(舉個例子:破壞了html結構!):

<p>
  I turned the corner
  <span *ngIf="hero">
    and saw {{hero.name}}. I waved
  </span>
  and continued on my way.
</p>

---------------------------

<p>
  I turned the corner
  <ng-container *ngIf="hero">
    and saw {{hero.name}}. I waved
  </ng-container>
  and continued on my way.
</p>
複製代碼

組件--特殊的指令(擁有模板的指令)

ng g c components/A 建立組件
複製代碼

組件通訊的幾種方式

1.輸入屬性 @Input()(父組件傳遞數據給子組件)
//a.component.ts
  @Input()
  inAttr:String;


  private _name:String = '';
----------------------------------------------------
  @Input()
  set inAttr2(name:String){
    this._name = name;
  }
  
  get inAttr2():String{
    return this._name;
  }
複製代碼
2.輸出屬性 @Output()(子組件傳遞數據給父組件)
//子組件中
@Output()
myEvent:EventEmitter<DataType> = new EventEmitter();

this.myEvent.emit(Data);

//父組件中
(myEvent)="myHandleEvent($event)"
myHandleEvent(Data:DataType){
    
}
複製代碼
3.中間人模式(兄弟組件經過公共的父組件傳值)
4.父組件得到子組件引用 #child(只能夠在組件模板中使用)
5. @ViewChild()父組件類插入子組件 (在組件類中使用)
  1. 被注入的子組件只有在 Angular 顯示了父組件視圖以後才能訪問(ngAfterViewInit生命週期中訪問)
  2. Angular 的單向數據流規則會阻止在同一個週期內更新父組件視圖。應用在顯示秒數以前會被迫再等一輪。
  3. 解決方法:使用 setTimeout() 來更改數據
@ViewChild(ChildComponent)
private childComponent: ChildComponent;

ngAfterViewInit() {
    setTimeout(() => this.seconds = () => this.childComponent.changeData, 0);
}
複製代碼
6.經過服務更改數據(任意組件結構中使用)

自定義指令

ng g d myDirective/demo
複製代碼
  1. 屬性型指令

ElementRef:對視圖中某個原生元素的包裝器。

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
  selector: '[appDemo]'
})
export class DemoDirective {
  //構造函數要聲明須要注入的元素 el: ElementRef。
  constructor(private el:ElementRef) { }
  // 註冊事件
  @HostListener('click')
  show(){
    console.log(this.el.nativeElement);
    console.log(this.ss);
  }
  //指令參數,當參數名與指令名相同時,能夠直接賦值給指令
  @Input()
  ss:String = 'aaa';
}

複製代碼
<button appDemo [ss]="bbb">click me</button>
複製代碼
  1. 結構型指令

TemplateRef:取得 < ng-template > 的內容

ViewContainerRef: 訪問視圖容器

import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({ selector: '[appUnless]'})
export class UnlessDirective {
  //第一次傳入true時不執行任何if分支,提高性能
  private hasView = false;

  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef) { }

  @Input() set appUnless(condition: boolean) {
    if (!condition && !this.hasView) {
      //實列化一個試圖,並把它插入到該容器中
      this.viewContainer.createEmbeddedView(this.templateRef);
      this.hasView = true;
    } else if (condition && this.hasView) {
      this.viewContainer.clear();
      this.hasView = false;
    }
  }
}
複製代碼

管道

你能夠將管道理解成將數據處理以後再顯示的一種操做符,如:

<p>{{ birthday}}</p>
<p>{{ birthday | date }}</p>
<!--鏈式調用管道-->
<p>{{ birthday | date | uppercase}}</p>
複製代碼

angular內置的管道:

DatePipe、UpperCasePipe、LowerCasePipe、CurrencyPipe 和 PercentPipe.......

自定義管道

import { Pipe, PipeTransform } from '@angular/core';
@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
  transform(value: number, exponent: string): number {
    let exp = parseFloat(exponent);
    return Math.pow(value, isNaN(exp) ? 1 : exp);
  }
}
複製代碼

純(pure)管道與非純(impure)管道

  1. Angular 只有在它檢測到輸入值發生了純變動時纔會執行純管道純變動是指對原始類型值(String、Number、Boolean、Symbol)的更改, 或者對對象引用(Date、Array、Function、Object)的更改。

  2. Angular 會在每一個組件的變動檢測週期中執行非純管道。 非純管道可能會被調用不少次,和每一個按鍵或每次鼠標移動同樣頻繁。

@Pipe({
  name: 'flyingHeroesImpure',
  pure: false
})
export class FlyingHeroesImpurePipe extends FlyingHeroesPipe {}
複製代碼

依賴注入

依賴注入是實現控制反轉的一種方式,將對象的創造權交出去,它的好處能夠在實際開發中體現出來,如下會介紹它

angular實現控制反轉的手段就是依賴注入

依賴注入的好處:

依賴注入會讓你用一種鬆耦合的方式去寫代碼,易於調試:

舉個例子:

當你的服務分爲開發版本與線上版本時,你可能須要兩個不一樣的服務devDataSevice與proDataSevice, 當你不使用依賴注入時,你須要在多個組件中new 出這兩個對象來使用這兩個服務,在線上環境與測試環境之間切換時,你須要更改多個組件中的代碼,若是使用依賴注入的形式,你只須要更改提供器中的代碼就能夠更改全部組件中的服務,這大大下降了代碼的耦合程度而且提升了可維護性。

首先建立一個服務

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

@Injectable({
  providedIn: 'root',
})
export class HeroService {
  constructor() { }
}
複製代碼

服務提供商(注入器)

在哪裏書寫服務提供商:

  1. 在服務自己的 @Injectable() 裝飾器中的 providedIn 的元數據選項

    providedIn的值能夠是'root'或者某個特定的NgModule

  2. 在 NgModule 的 @NgModule() 裝飾器中的providers元數據選項

  3. 在組件的 @Component() 裝飾器中providers元數據選項

  4. 在指令的 @Directive() 裝飾器中providers元數據選項(元素級注入器)

providers中如何書寫:

provider:[ProductService]
provider:[{provide:ProductService,useClass:ProductService}]
provider:[{provide:ProductService,useClass:AnotherProductService}]
provider:[{provide:ProductService,useFactory:(參數A)=>{return ob},deps:[參數A]}]
provider:[{provide:"IS_DEV_ENV",useValue:{isDev:true}}]
複製代碼

注入器冒泡

  1. Angular 會嘗試尋找該組件本身的注入器
  2. 若是該組件的注入器沒有找到對應的提供商,它就去尋找它父組件的注入器直到 Angular 找到一個合法的注入器或者超出了組件樹中的祖先位置爲止。
  3. 若是超出了組件樹中的祖先還未找到,Angular 就會拋出一個錯誤。

注入服務:

在構造函數中注入:

construct(private productService:ProductService){...};
複製代碼
  1. @Optional()是對productService的裝飾器,當找不到服務的提供商時,參數值設爲null
  2. @Host() 裝飾器會禁止在宿主組件以上的搜索。宿主組件一般就是請求該依賴的那個組件。 不過,當該組件投影進某個父組件時,那個父組件就會變成宿主。
  3. @Inject()自定義提供商
  4. @Self() @SkipSelf() 來修改提供商的搜索方式

@angular/router路由

  1. 設置base標籤,告訴路由該如何合成導航
<!--index.html中的head標籤中加入<base href="/">來告訴路由該如何合成導航用的 URL-->
<base href="/">
複製代碼
  1. 導入路由配置
//app.module.ts
//導入路由核心模塊
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  {path:'**',component:AComponent}
];

@NgModule({
  ...
  imports: [RouterModule.forRoot(routes)]
  ...
})
export class AppModule { }
複製代碼

Routes路由配置介紹

  1. path "**"表示匹配全部

  2. redirectTo "表示要轉走的路徑"

  3. pathMatch "full"表示匹配程度

  4. component 表示要顯示的組件

  5. data 數據傳遞

  6. children:[] 子路由

  7. canActivate:[PermissionGuard]

  8. canDeactivate:[FocusGuard]

  9. resolve:{Stock:StockResolve}

  10. outlet 輔助路由

  11. 設置導航輸出位置

<router-outlet></router-outlet> <!-- Routed components go here --> 複製代碼

路由跳轉

  1. 聲明式跳轉
<a [routerLink]="['/path']" routerLinkActive="active">routelink</a>
<a [routerLink]="['./path']">routelink</a>
<a [routerLink]="['{outlets:{aux:'aaa'}}']">routelink</a> 輔助路由 
http://localhost:4200/a(aux:aaa)
複製代碼
  1. 命令式跳轉

Router可調用navigate與navigateByUrl()

路由數據傳遞:

//1.
[routerLink] = "['/path',1]"
//http://localhost:4200/path/1
// this.routeInfo.snapshot.queryParams
//2.
[routerLink]="['/b',1]" [queryParams]="{id:3}"
// http://localhost:4200/b/1?id=3
// this.routeInfo.snapshot.params
// 3.
{path:'a',component:AComponent,data:{id:666}} 
//this.routeInfo.snapshot.queryParams
//this.routeInfo.snapshot.data
複製代碼

ActivatedRoute 經常使用:this.routeInfo見上面

守衛路由

1.canActivate

export class PermissionGuard implements CanActivate{
  canActivate(){
    let hasPemission:boolean = Math.random() < 0.5;
    return hasPemission;
  }
}
複製代碼

2.canDeactivate

export class FocusGuard implements CanDeactivate<CComponent>{
  canDeactivate(component:CComponent){
    if(component.isFoucs){
      return true;
    }else {
      return confirm('不關注一下嘛?');
    }
  }
}
複製代碼

3.resolve 讀取數據前

@Injectable()
export class StockResolve implements Resolve<Stock>{
  constructor(
    private route:Router
  ){}
  resolve(route:ActivatedRouteSnapshot,state:RouterStateSnapshot){
      return new Stock(1,'name');
  }
}
複製代碼

生命週期鉤子

當 Angular的 組件指令, 新建、更新和銷燬時所觸發的鉤子函數

鉤子函數的執行順序

  1. constructor 這個不是生命週期鉤子,可是它必定是最早執行的
  2. ngOnChanges 輸入屬性被賦值時調用 (不可變對象的改變)
  3. ngOnInit 第一次顯示數據綁定和設置指令/組件的輸入屬性以後
  4. ngDoCheck 在每一個變動檢測週期中,緊跟在 ngOnChanges() 和 ngOnInit() 後面調用
  5. ngAfterContentInit 外部內容投影進組件/指令的視圖以後調用
  6. ngAfterContentChecked 投影組件內容的變動檢測以後調用
  7. ngAfterViewInit 初始化完組件視圖及其子視圖以後調用
  8. ngAfterViewChecked 組件視圖和子視圖的變動檢測以後調用
  9. ngOnDestroy 次銷燬指令/組件以前調用

初始化階段

//1.constructor 
//2.ngOnChanges
//3.ngOnInit
//4.ngDoCheck
//5.ngAfterContentInit
//6.ngAfterContentChecked
//7.ngAfterViewInit
//8.ngAfterViewChecked
複製代碼

變化階段

//1.ngOnChanges
//2.ngDoCheck
//3.ngAfterContentChecked
//4.ngAfterViewChecked
複製代碼

組件銷燬階段

//1.ngOnDestroy
// 在路由變動時改變
複製代碼

ngAfterViewInit,ngAfterViewChecked

  1. 子組件組裝好父組件纔會組裝
  2. 組件是在試圖組裝完畢調用
  3. 再此方法中不能夠更改視圖數據

ngAfterContentInit,ngAfterContentChecked,投影

1.子組件
<div>
  <ng-content></ng-content>
</div>
2.父組件
<SComponent>
    <!--在此寫入的東西會投影到子組件-->
</SComponent>
複製代碼

表單

  1. ReactiveFormsModule響應式表單
  2. FormsModule模板式表單

模板式表單

  1. 在angular中會自動加上ngForm來處理表單,若是不用angular來處理則加上ngNoForm
  2. 在表單中加上#myForm="ngForm",則能夠在頁面使用{{myForm.value | json}}去檢測表單中有ngModule的value 對象名爲name值

NgForm 對應 FormGroup

ngModel 對應 FormControl

ngModelGroup 對應 FormArray

<form #myForm="ngForm" action="/regist" (ngSubmit)="createUser(myForm.value)" method="post">
  <div>
    <input ngModel name="a" type="text" required pattern="[a-zA-Z0-9]+">
  </div>
  <div>
    second:<input ngModel #second="ngModel" name="a" type="text" required pattern="[a-zA-Z0-9]+">
  </div>
  <div>
    <input ngModel name="b" type="text" required pattern="[a-zA-Z0-9]+">
  </div>
  <div ngModelGroup="tow">
    <input ngModel name="a" type="text">
    <input ngModel name="b" type="text">
  </div>
  <button type="submit">提交</button>
</form>
<div>
  {{myForm.value | json}}
  <br>
  second值是:{{second.value}}
</div>
複製代碼

響應式表單

private nickName = new FormControl('tom');
private passwordInfo = new  FormGroup({
  password: new FormControl(),
  passwordConfirm:new  FormControl()
});
private email = new FormArray([
    new FormControl('a@a.com'),
    new FormControl('b@b.com')
]);
複製代碼

FormControl

管理單體表單控件的值和有效性狀態

FormGroup

管理一組 AbstractControl 實例的值和有效性狀態

FormArray

管理一組 AbstractControl 實例的值和有效性狀態

<form [formGroup]="formModel" action="/regist" (Submit)="createUser()" method="post">
   <input formControlName="nikname">
   <ul formArrayName="emails">
      <li *ngFor="let email of formModel.get('emails').controls;let i = index;">
        <input [formControlName]="i">
      </li>
    </ul>
    <button >增長email.....</button>
   <input formControlName="emails">
   <div formGroupName="passwordInfo">
      <input formControlName="password">
      <input formControlName="passwordConfirm">
    </div>
</form>
複製代碼

FormBuilder快捷語法

private formModel:FormGroup;
private fb:FormBuilder = new FormBuilder();
/*this.formModel = new FormGroup({ nikname:new FormControl(), emails:new FormArray([ new FormControl() ]), mobile:new FormControl(), passwordInfo:new FormGroup({ password:new FormControl(), passwordConfirm:new FormControl() }) });*/
this.formModel = this.fb.group({
  nikname:[''],
  emails:this.fb.array([
      ['']
  ]),
  mobile:[''],
  passwordInfo:this.fb.group({
    password:[''],
    passwordConfirm:['']
  })
});
複製代碼

angular表單 校驗器

//自定義校驗器
  xxx(param:AbstractControl):{[key:string]:any}{
    return null;
  }
  // eg:
  moblieValid(moblie:FormControl):any{
    ..........
    // return null;表示成功
    // return {...};不成功
  }
  
  
  //預約義校驗器
  Validators.required ......
  nikname:["xxxx",[Validators.required,.....]]
  .....................
  let niknameValid;boolean = this.formModel.get('nikname').valid;
  passwordInfo:this.fb.group({
      password:[''],
      passwordConfirm:['']
    },{validator:this.passwordValidator}) //一次校驗多個字段
    
複製代碼

顯示錯誤信息

<div [hidden]="!formModel.hasError('required','nickname')"></div>
複製代碼

因爲文章過於冗長,關於表單,httpclient等內容我將從新開啓一篇文章,感謝廣大讀者的支持。

相關文章
相關標籤/搜索