天天看點

Angualr入門第一部分AngualrNg Docs

Angualr

一、目錄結構

“assets”: [ 存放靜态資源目錄 “src/favicon.ico”,“src/assets”]

“index”: “src/index.html”, 單頁面,打包編譯結果出口

“main”: “src/main.ts”, 整個子產品化系統啟動的入口,其作用為:加載根子產品,啟動執行子產品系統,為整個系統的程式入口。

package.json:項目包說明檔案,檔案中有scripts字段,左邊是簡易指令,右邊是等效的執行程式,即在終端輸入 npm run start 和 ng serve均會執行編譯程式:

"scripts": {   
     "ng": "ng", 運作檢視Angular CLI腳手架工具使用幫助   
     "start": "ng serve", 運作開發模式    
     "build": "ng build", 運作項目打包建構(用于釋出到生成環境)  "test": "ng test", 運作karma單元測試    
     "lint": "ng lint", 運作TypeScripts代碼校驗   
     "e2e": "ng e2e", 運作protractor端到端測試  
     "npm run pc:start": 啟動pc-fe項目
 }
           

二、元件

Angualr入門第一部分AngualrNg Docs

Component(元件)是整個架構的核心 ,“元件化”的可以用來解耦、複用。

app.component.ts就是一個元件,在Angular中元件就是一個類 。

  • @Component為元件的裝飾器,用來修飾元件的一些特性,裡面包含元件的中繼資料Metadata;
  • selector 用來定義元件的名字
  • templateUrl用來指定元件對應的html模闆檔案
  • styleUrls用來存放css檔案
  • styleUrls是一個數組,用來存放元件相關的樣式檔案

    元件方法中,直接通過this通路元件成員

    Angualr入門第一部分AngualrNg Docs

建立元件的方法:

ng generate component 元件名

三、子產品

NgModule(子產品)是組織業務代碼的利器,按照自己的業務場景,把元件、服務、路由打包到子產品裡面,形成一個個的積木塊,然後用這些積木塊來搭建出高樓大廈,app.module.ts就是子產品檔案,裡面是這樣的:

Angualr入門第一部分AngualrNg Docs
  • exports: [RouterModule] 導出RouterModule,以便它在整個應用程式中生效。
  • declarations:聲明并組裝子產品資源:元件、指令、服務;該應用所擁有的元件。 聲明這裡面有什麼
  • imports:app子產品需要依賴其他的子產品,如BrowserModule等;
  • bootstrap:指定啟動的根元件;
  • providers:各種服務提供者。

四、中繼資料

@Component被稱為中繼資料,用于描述類相關的功能子產品群組件的一些資訊

五、資料綁定和指令

MVVM思想(資料驅動視圖),通過特殊的{{}}文法将資料綁定到DOM元素,當資料改變的時候會影響視圖的更新。

循環指令:*ngFor

條件判斷指令:*ngIf

表單控件雙向綁定指令:[(ngModel)],需要導入

import { FormsModule } from '@angular/forms';
imports:[ FormsModule]
           

六、服務

服務指的是針對某個單一或系統功能的封裝,包括值、函數或應用所需的功能,例如在Angular核心包裡面,最典型的一個服務就是http服務。典型的服務是一個類,具有專注的、明确的用途,應該做一件特定的事情,并把它做好。例如,日志服務,和服務端接口互動的服務等

七、依賴注入

依賴注入是提供類的新執行個體的一種方式,負責處理好類所需的全部依賴。大多數依賴都是服務。Angular使用依賴注入來提供新元件以及元件所需的服務。Angular重要的特性和服務:動畫、表單、HTTP、元件生命周期、管道過濾器、路由和測試。

Ng Docs

一、正常用法:

1插值表達式

<p>{{byObject.centence}}</p>
 <p>{{byArray[0]}}</p>
           
byObject = { centence: '我是對象的值' };
 byArray = ['我是數組的0号元素'];
           

2綁定機制

  • 屬性-從資料源到視圖(單向) 插值表達式 []
  • 事件-從視圖到資料源(單向) ()
  • 表單值-從視圖到資料源再到視圖(雙向) [()]
<span [ngClass]="className">
<!-- 這裡的ngClass等同于 class="{{className}}" -->
<some-element [ngClass]="['first', 'second']">...</some-element>

<some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>

<some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
           
<div>
  <p>案例1:使用模版引用變量(脫離資料源)</p>
  <input style="width: 125px" nz-input #age placeholder="請輸入你的年紀" />
  <button (click)="showMsg(age.value)" nz-button nzType="primary">彈出模闆引用變量的内容</button>
</div>
           

input輸入框輸入值,age将input框進行标記,button通過點選函數取到age.value即input框内的值

<div>
  <p>案例4:既有單向綁定又有雙向綁定</p>
  <input style="width: 125px" nz-input [(ngModel)]="centence"/>
  <button (click)="showMsg(centence)" nz-button nzType="primary">彈出雙向綁定的内容</button>
</div>
           

[(ngModel)]進行資料雙向綁定,input框中的值會傳給contence,button鍵點選觸發showMsg函數,此時展示的值是經過變化後的contence

可選鍊:

意義:有就用,沒有就不用

例子:

我是{{user.name}}, 我的房子所在地為{{user.house?.location}}
           

沒有就顯示:我是誰誰誰,我的房子所在地為

有就顯示:我是誰誰誰,我的房子所在地為上海大别墅

二、指令

ng-content

父傳子,将父類html嵌入到子類中,就是父類html中有一段代碼需要動态生成,相當于vue中的插槽slot。

父元件:

<h1>父元件</h1>
<input [(ngModel)]="sentence" name="sentence" class="sentence">
<stomach>
   <div>{{sentence}}</div>
   <p>所謂的不公,隻是不符合人類社會法則</p>
   <span class="className">卻符合因果關系</span>
</stomach>
           

子元件:

<h1>子元件</h1>
<!--ng-content父類嵌入子類-->
<ng-content select="p"></ng-content>
<ng-content select="span"></ng-content>
<ng-content></content>
<ng-content select="p"></ng-content>
           

ng-container

ng-container隻是一個分組元素, 它的特點就是在渲染頁面時, 自身不加入DOM.你可以使用浏覽器的開發者工具, 檢視運作結果中的這句話, 是否隻有p标簽包圍.

<ng-container>
     <p>輕流招聘啦~詳情請戳https://github.com/Eve-Sama</p>
 </ng-container>
 <ng-container>
   <p>自身不加入dom,隻是分組元素</p>
 </ng-container>
           

ng-template

ng-template是Angular 結構型指令中的一種,用于定義模闆渲染HTML(模闆加載)。定義的模闆不會直接顯示出來,需要通過其他結構型指令(如 ng-if)或 template-ref 将子產品内容渲染到頁面中。

今天吃點啥?
<ng-template [ngIf]="true">
    吮指原味雞
</ng-template>
           

概述

一共有三種類型的指令:

  • 元件-擁有模闆的指令
  • 結構型指令-通過添加和移除 DOM 元素改變 DOM 布局的指令
  • 屬性型指令-改變元素、元件或其他指令的外觀和行為的指令

結構性指令

以*開頭,如*ngIf,*ngFor,否則為屬性型指令,如ngClass,ngStyle等

*ngFor中有以下幾種模闆變量:

  • index: number:周遊時的序号, 下标從0開始
  • first: boolean:若為第一個元素則傳回true, 否則傳回false
  • last: boolean:若為最後一個元素則傳回true, 否則傳回false
  • even: boolean:若為偶數元素則傳回true, 否則傳回false
  • odd: boolean:若為奇數元素則傳回true, 否則傳回false
<table>
    <thead>
        <tr>
            <th>序号</th>
            <th>姓名</th>
            <th>籍貫</th>
            <th>第一條記錄</th>
            <th>最後一條記錄</th>
            <th>偶數記錄</th>
            <th>奇數記錄</th>
        </tr>
    </thead>
    <tbody>
        <tr *ngFor="let obj of ngForData;
            let index = index;
            let first = first;
            let last = last;
            let even = even;
            let odd = odd;">
            <td>{{index}}</td>
            <td>{{obj.name}}</td>
            <td>{{obj.gender}}</td>
            <td>{{obj.hometown}}</td>
            <td [ngClass]="{'high-light-true':first,'high-light-false':!first}">{{first}}</td>
            <td [ngClass]="{'high-light-true':last,'high-light-false':!last}">{{last}}</td>
            <td [ngClass]="{'high-light-true':even,'high-light-false':!even}">{{even}}</td>
            <td [ngClass]="{'high-light-true':odd,'high-light-false':!odd}">{{odd}}</td>
        </tr>
    </tbody>
</table>
           

trackBy

Angular預設是以對象為标記的, 一旦你更新資料源, Angular便更新視圖.更新時, 先移除全部DOM結點, 再依次添加DOM結點.DOM的操作非常消耗性能, 是以, *ngFor提供 trackBy來幫助使用者自定義追蹤函數算法.

就是用*ngFor循環更新周遊資料時,更新整個dom樹,但有時候我們隻是需要更新一小部分内容,那麼此時我們利用trackby進行資料追蹤,就可以提高效率。

<ul>
    <Li *ngFor="let obj of cities; trackBy:trackByCity">
        {{obj.name}}
    </Li>
</ul>
<button (click)="UpdateCities()" nz-button>擷取資料</button>
<button (click)="ResetCities()" nz-button>還原資料</button>
           

自定義結構型指令

  • 導入并添加Directive裝飾器
  • 導入input,TemplateRef,ViewContainRef
  • 設定選擇器,以便Angualr解析指令
  • 在AppModule的declarations中聲明

關于自定義結構型指令需要預先了解以下三個概念:

**TemplateRef:**表示内嵌的HTML模闆.

**ViewContainerRef:**表示視圖容器, 建立和管理内嵌視圖.

**依賴注入:**将控制權交給了父元件

//1
    import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
    import {Directive,Input,TemplateRef}
    //2
    @Directive({
        selector: '[nssMilkTeaType]'
    })
    export class NssMilkTeaDirective {
        //3
        constructor(
            private templateRef: TemplateRef<any>,
            private viewContainer: ViewContainerRef) {
        }
        //設定type方法,裡面的type是它的一個string屬性
        @Input('nssMilkTeaType') set type(type: string) {
            if (type === 'oreo') {
                this.viewContainer.clear();//清除所有内嵌視圖
                this.viewContainer.createEmbeddedView(this.templateRef);//建立内嵌視圖
            } else if (type === 'redBean') {
                this.viewContainer.clear();
                this.viewContainer.createEmbeddedView(this.templateRef);
                this.viewContainer.createEmbeddedView(this.templateRef);
            } else if (type === 'matcha') {
                this.viewContainer.clear();
                this.viewContainer.createEmbeddedView(this.templateRef);
                this.viewContainer.createEmbeddedView(this.templateRef);
                this.viewContainer.createEmbeddedView(this.templateRef);
            }
        }
    }
           

屬性型指令

ngClass:

相當于css中的class選擇器

ngStyle:

相當于直接在裡面寫樣式

ngSwitch:

ngSwitch設定了一個參數變量, 當參數變量與 *ngSwitchCase的值一緻時, 顯示其DOM元素, 否則移除.當所有 *ngSwitchCase都無法比對時, 則顯示 *ngSwitchDefault的DOM元素

<nz-radio-group [(ngModel)]="hobby">
    <label nz-radio nzValue="sweet">我喜歡吃甜的</label>
    <label nz-radio nzValue="salty">我喜歡吃鹹的</label>
    <label nz-radio nzValue="yummy">隻要好吃就行</label>
</nz-radio-group>
<div [ngSwitch]="bobby">
    <p>生活就需要甜甜的味道!</p>
    <p>鹹一點的食物才叫美食,其他的隻能叫吃的!</p>
    <p>隻要是好吃的我都喜歡!</p>
</div>
           

自定義屬性型指令

  • 導入并添加 Directive裝飾器
  • 導入 Input, ElementRef
  • 設定選擇器, 以便Angular解析指令
  • 在AppModule的 declarations 中聲明

三、路由

router-outlet

側邊欄和頂部導航欄是不變的, 隻有内容區是在變化的.如本項目, 左側菜單好像是固定在那邊的, 右邊内容區卻是會變化的.這是如何實作的呢?

實際上, 右邊就是個 router-outlet, 字面意思是路由出口, 告訴路由子產品, 需要加載的頁面需要放在哪個地方.

routerLink

路由的跳轉, 很多時候是由使用者在應用界面上的操作導緻的, 那麼當使用者需要跳轉頁面的時候, 需要加上跳轉連結.在以前, 我們可能是這樣寫的

<a href="../project/author">關于作者</a>
   //a标簽中的href變成routerlink
   <a routerLink="/project/author">關于作者</a>
           

Routes資料結構

import { Routes } from '@angualr/router';
const routes: Routes = [
    {path: 'demo1',component:Demo1Component },
    {path: 'demo2',component:Demo2Component },
    //預設導航路徑. 
    {path: '',   redirectTo: '/demo1', pathMatch: 'full' },
    {path: '**', component: PageNotFoundComponent }
]
           
  • path - 路徑, 用來比對路由的名字 ,’**'是指任何路徑都可以比對到的路徑, 通常用來放置404頁面, 比如使用者惡意修改路徑, 但實際上并沒有對應的路徑.
  • component - 元件名, 對應路徑指向的元件名
  • redirectTo - 重定向指向的路徑, 當比對到這個路徑時轉發請求給重定向的路徑
  • pathMatch - 路徑比對模式

**pathMatch:**通常設定為 full.其值還有一種可能是 prefix, 是指字首比對.思考一下, 當 path 值為 ‘’ 時, 為什麼必須指明 pathMatch為 full?觀察以下URL.

angular.ink
angular.ink/demo1
           

這三個URL都是以 angular.ink開頭的, 這個是根域名, 那麼三個都可以比對到, 是以, 預設路徑必須是 full的比對模式.

異步路由

就是假如登入子產品有兩個身份界面,一個是admin,一個是user,那麼當admin通路時就不需要加載user,當user通路時不需要加載admin,這就是異步路由,這樣可以大大提高加載速度。

例如一個線上超市系統:

app
    pages
        pages-routing.module.ts
        user
            browse-product
                browse-product.component.ts
            buy-product
                buy-product.component.ts
            user-router.module.ts
            user.module.ts
        admin
            manage-product
                manage-product.component.ts
            operator-record
                operator-record.component.ts
            admin-router.module.ts
            admin.module.ts 
           

pages:

  • pages-routing.module.ts - 總的子產品路由
  • user-router.module.ts(admin-router.module.ts) - 具體子產品路由, 決定哪些路徑能夠比對到user(admin)的哪些頁面
  • user.module.ts(admin.module.ts) - 子產品聲明, 元件、管道、服務等需要在此聲明, 才可以被此子產品使用

    user-router.module.ts

import {NgModule} from '@angualr/core';
import {Routes,RouterModule} from '@angualr/router';
import {BrowseProductComponent} from './browse-product/browse-product.component';
import { BuyProductComponent } from './buy-product/buy-product.component';

const routes:Routes = [
    {path: 'browse-product',component: BrowseProductComponent},
    {path: 'buy-product',component: BuyProductComponent}
];

@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule]
})

export class UserRoutingModule{}
           

user.module.ts

import {NgModule} from '@angular/core';
import {Routes,RouterModule} from '@angualr/router';
import {BrowseProductComponent} from './browse-product/browse-product.component';

@NgModule({
   imports: [
       UserRoutingModule,
   ],
   declaration:[
       BrowseProductComponent,
       BuyProductComponent
   ]
})
export class UserModule{}
           

admin-router.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ManageProductComponent } from './manage-product/manage-product.component';
import { OperatorRecordComponent } from './operator-record/operator-record.component';

const routes:Routes = [
  {path: 'manage-product',component:ManageProductComponent },
  {path: 'operator-record',component:OperatorRecordComponent}
];

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})

export class AdminRoutingModule{}
           

admin.module.ts

import { NgModule } from '@angular/core';
import { AdminRoutingModule } from './angular-routing.module';
import { ManageProductComponent } from './manage-product/manage-product.component';
import { OperatorRecordComponent } from './operator-record/operator-record.component';

@NgModule({
   imports:[
     AdminRoutingModule,
   ]
   declarations:[
      ManageProductComponent,
      OperatorRecordComponent
   ]
})
export class AdminModule { }
           

pages-routing.module.ts

import { NgModule } from '@angular/core';
  import { Routes, RouterModule } from '@angular/router';

    const routes: Routes = [
    {
        path: '',
        children: [
            { path: '', redirectTo: '/user/browse-product', pathMatch: 'full' },
            { path: 'user', loadChildren: './user/user.module#UserModule' },
            { path: 'admin', loadChildren: './admin/admin.module#AdminModule' },
            { path: '**', redirectTo: '/user/browse-product' },
        ],
    }
    ];

    @NgModule({
    imports: [RouterModule.forRoot(routes, { useHash: false })],
    exports: [RouterModule],
    })
    export class PagesRoutingModule {}
           

路由傳參

原頁面:

<button (click)="navigate()">攜轉跳轉</button>

navigate(){
    this.router.navigate(['/angualr/params-router'],{
         queryParams:{
              name:'前夕',
              words:‘最美的是相逢,最難的是重逢’
         }
    })
}
           

跳轉後的:

<div class="center">
    <h1>{{name}}</h1>
    <h2>{{words}}<h2>
</div>
activatedRoute.queryParams.subscribe(queryParams =>{
    this.name = queryParams.name;
    this.words = queryParams.words;
})
           

CanActivate

很多時候, 并不是所有人都可以通路所有頁面的.最典型的應用場景是普通使用者并不能通路管理者的操作頁面.那麼我們就需要在使用者通路時判斷這個使用者到底是否有相應的權限.路由配置檔案提供了一個接口 CanActivate用來處理驗證的業務.有時候這些安全措施也被稱為路由守衛.

CanDeactivate

在一些表單編輯頁面, 如果使用者辛辛苦苦輸入了半天, 卻不小心點到導航按鈕, 跳轉到了其他頁面, 丢失了未儲存的資料, 這樣的使用者體驗是非常糟糕的.是以, Angular提供了 CanDeactivate接口, 在跳轉路由之前觸發, 以提醒使用者, 或者做一些業務上的儲存等操作.

CanActivateChild則用來保護子路由. CanLoad則是用來保護子子產品的, 未經允許, 不加載子子產品.

預加載

我們知道, 使用異步路由後, 各自的路由子產品隻有在初次通路時才會加載.而實際上, 在頁面初次加載後, 進入預設路由子產品, 使用者通常需要停留一會, 這個時候繼續加載其他子產品是比較合适的, 這樣做有兩個非常顯著的好處.

  • 初次加載并沒有加載預設路由子產品, 提高加載速度
  • 當使用者通路其他子產品時, 不會出現卡頓, 因為已經預加載了

隻需要在子產品配置處添加以下代碼即可

@NgModule({
    imports: [RouterModule.forRoot(
        routes,
        {
            useHash:false,
            preloadingStategy:PreloadAllModules //開啟預加載
        }
    )],
    exports:[RouterModule],
})
           

四、服務

一般用于寫擷取資料的業務邏輯

  • 元件級注冊, 在 @Component當中的 providers數組當中引入服務
  • 子產品級注冊, 在 @ngModule當中的 providers數組當中引入服務

當元件需要使用服務時, 都需要将服務引入元件内并在構造函數中聲明.

元件級注冊:

import { XxxService } from './xxx.service';
//元件之間互相獨立
@Component({
    providers: [XxxService]
})
           

子產品級注冊:

import { XxxService } from './xxx.service';
//元件之間資料共享
@NgModule({
   providers:[
       XxxService
   ]
})
           

基礎用法:

在概述當中, 我們提到了服務的應用場景, 那麼, 我們現在來實作這樣一個登入功能.

  • 設計一份表單來采集賬号和密碼, 點選登入後驗證使用者身份
  • 當帳号為’admin’, 密碼為’123456’時, 登入成功, 否則登入失敗
<input nz-input #userName name="userName" placeholder="使用者名,預設admin">
<input nz-input #password name="password" placeholder="密碼,預設123456">
<button (click)="login(userName.value,password.value)" nz-button nzType="primary">登入</button>
           
import {Injectable} from '@angular/core';
@Injectable()
export class LoginService{
    login(userName: string, password: string): boolean {
            if (userName === 'admin' && password === '123456') {
                return true;
            }
            return false;
        }
}
           
import { Component } from '@angular/core';
    import { LoginService } from './service/demo.service';
    import { NzMessageService } from 'ng-zorro-antd/message';

    @Component({
        templateUrl: './service.component.html',
        styleUrls: ['./service.component.scss'],
        providers: [LoginService]
    })
    export class ServiceComponent {
        constructor(
            private loginService: LoginService
        ) { }
        login(userName, password) {
            if (this.loginService.login(userName, password)) {
                this.nzMessageService.info('登入成功!');
            } else {
                this.nzMessageService.error('登入失敗!');
            }
        }
    }
           

元件級注冊提供商

元件級注冊最大的作用就是元件之間的資料不會互相影響。

舉個栗子:

  • 設計一個服務, 服務内部有一個字段初始值為0, 定義一個方法, 可以自增這個字段并且彈出顯示(alert, 在案例中使用的antd樣式)
  • 設計父子元件, 在父子元件中均注冊這個服務并且調用這個服務的方法.但是各自的資料互不影響
import { Injectable } from '@angular/core';
import { NzMessageService } from 'ng-zorro-antd/message';

@Injectable()
export class ComponentService{
    constructor(private nzMessageService: NzMessageService){}
    value = 0;
    add(){
        this.value++;
        this.nzMessageService.info(this.value.toString());
 }
}

           
<h1>父元件</h1>
<button (click)="addAndShowComponent"  nz-button nzType="primary">自增且顯示</button>
<component-child></component-child>
           

父元件ts

import { Component } from '@angular/core';
  import { ComponentService } from './service/demo.service';
  
  @Component({
      templateUrl: './service.component.html',
      styleUrls: ['./service.component.scss'],
      providers: [ComponentService]
  })
  
  export class ServiceComponent{
      constructor(
         private componentService:ComponentService{}
      )
      addAndShowComponent(){
         this.componentService.add();
      }
  }
           

子元件html

<h1>子元件</h1>
<span>
   <button (click)="addAndShow()" nz-button nzType="primary">自增且顯示</button>
</span>
           

子元件ts

import { Component } from '@angular/core';
   import { ComponentService } from '../../service/demo.service';

    @Component({
        selector: 'component-child',
        templateUrl: './component-child.component.html',
        styleUrls: ['./component-child.component.scss'],
        providers: [ComponentService]
    })
    export class ComponentChildComponent {
        constructor(private componentService: ComponentService) { }
        addAndShow() {
            this.componentService.add();
        }
    }
           

子產品級注冊提供商

群組件級注冊相反,子產品級注冊要求服務内部之間共享資料

//服務
import { Injectable } from '@angular/core';
import { NzMessageService } from 'ng-zorro-antd/message';

@Injectable()
export class RootService{
    constructor(private nzMessageService:NzMessageService){}
    value = 0;
    add(){
        this.value++;
        this.nzMessageService.info(this.value.toString());
    }
}
           
//服務注冊,寫在根元件内
import { RootService } from './service/service/demo.service';

@NgModule({
    providers:[
        RootService
    ]
})

export class AngualrModule{}//這是我的子產品名
           
//父元件樣式
<h1>父元件</h1>
<button (click)="addAndShowRoot()" nz-button nzType="primary">自增且顯示</button>
<service-child></service-child>
           
//父元件ts
    import { Component } from '@angular/core';
    import { RootService } from './service/demo.service';

    @Component({
        templateUrl: './service.component.html',
        styleUrls: ['./service.component.scss']
    })
    export class ServiceComponent {
        constructor(
            private rootService: RootService
        ) { }
        addAndShowRoot() {
            this.rootService.add();
        }
    }
           
//子元件html
    <h1>子元件</h1>
    <span>
        <button (click)="addAndShow()" nz-button nzType="primary">自增且顯示</button>
    </span>
           
//子元件ts
    import { Component } from '@angular/core';
    import { RootService } from '../../service/demo.service';

    @Component({
        selector: 'service-child',
        templateUrl: './service-child.component.html',
        styleUrls: ['./service-child.component.scss']
    })
    export class ServiceChildComponent {
        constructor(private rootService: RootService) { }
        addAndShow() {
            this.rootService.add();
        }
    }
           

五、元件

父元件調用子元件方法

父元件需要調用子元件的方法, 這裡分為兩種調用方式, 一種是 執行個體調用, 一種是 類調用. 。

這裡需要使用 ViewChild将子元件注入到父元件的資料源. ViewChild相當于影分身,可以擁有原先子元件的屬性和方法.在 類調用 的案例中, child-communication的标簽即使不寫, 也可以調用子元件的方法

類調用

在這個案例中, 子元件有個輸入框, 點選子元件的按鈕可以正常彈出值.但是點選父類, 也能彈出資訊, 但是卻無值, 這是為什麼?

這是因為在父元件的資料源中, 對子元件的引用使用的是 類調用, 即參數都是子元件的類名. Angular隻能知道你有這個子元件, 但是無法定位你子元件所在的位置, 是不是這個道理?是以自然無法取得子元件的動态成員變量.

父元件html

<h1>我是父元件</h1>
<button (click)="callChildFunction2()" nz-button nzType="primary">調用子元件方法</button>
<child-communication></child-communication>
           

父元件ts

@ViewChild(ChildCommunicationComponent) private childComponent: ChildCommunicationComponent;
callChildFunction2(){
    this.childComponent.childFunction2();
}
           

子元件html

<h1>我是子元件</h1>
<input nz-input class="input" [(ngModel)]="childValue2" name="childValue2" placeholder="子元件的輸入框">
<button (click)="childFunction2()" nz-button nzType="primary">調用方法</button>
           

子元件ts

childValue2;
childFunction2(){
    this.messageService.info(this.childValue2);
}
           

執行個體調用

給子元件加上模闆引用變量确定子元件在dom樹中的位置

<h1>我是父元件</h1>
<button (click)="callChildFunction3()" nz-button nzType="primary">調用子元件方法</button>
<child-communication #child></child-communication>
           
@ViewChild('child') private childComponent2: ChildCommunicationComponent;
    callChildFunction3() {
        this.childComponent2.childFunction3();
    }
           

子元件給父元件傳值

在父元件傳值給子元件的時候, 其實是通過指令的方式完成的.但是使用指令的前提, 是被傳值者是一個标簽, 這符合了父元件傳值給子元件的條件.

但是子元件顯然不可能包含父元件的标簽, 是以并不能通過這種方式完成傳值.在開始示範之前, 先了解以下幾個概念:

  1. output用于外部通路, 也就是說, 将其暴露給外部, 允許外部使用
  2. EventEmitter是Angular的事件發射器, 暫時了解為事件的一個大類

觀察代碼和運作結果, 可以看到, 子元件中, 在點選按鈕後, 觸發點選事件, 調用 childFunction4()方法, 在這個方法中, 使用事件發射器 this.event.emit()觸發事件, 将值(案例中彈的是一個對象)彈出.

<h1>我是父元件</h1>
<input nz-input class="input" [(ngModel)]="name" placeholder="姓名">
<input nz-input class="input" [(ngModel)]="bornYear" placeholder="出生年月">
<child-communication (event)="showInfo($event)"></child-communication>
           
//父元件ts
name;
bornYear;
showInfo($event) {
    this.name = $event.name;
    this.bornYear = $event.bornYear;
}
           
<h1>我是子元件</h1>
<input nz-input class="input" [(ngModel)]="name" name="name" placeholder="姓名">
<input nz-input class="input" [(ngModel)]="bornYear" name="bornYear" placeholder="出生年月">
<button (click)="childFunction4()" nz-button nzType="primary">傳值給父元件</button>
           

每一個Subject都是一個Observable(可觀察對象),對于一個Subject,你可以訂閱(

subscribe

)它,Observer會和往常一樣接收到資料,

每一個Subject也可以作為Observer(觀察者) Subject同樣也是一個由

next(v)

error(e)

,和

complete()

這些方法組成的對象。調用

next(theValue)

方法後,Subject會向所有已經在其上注冊的Observer多路推送

theValue

service

import {Injectable} from '@angualr/core';
import {Subject} from 'rxjs';
@Injectable()
export class CommunicationService{
 // 聲明兩個Subject并定義接收資料類型,在本案例中是一個對象
    private father = new Subject<{drink,eat}>();
    private child = new Subject<{drink,eat}>();
 // 将Subject轉化為Observable
    father$ = this.father.asObservable();
    child$ = this.child.asObservable();
 //将變化的值,,發送給訂閱者
    fatherSend(value: {drink,eat}){
        this.father.next(value);
    }
    childSend(value: {drink,eat}){
        this.child.next(value);
    }
}
           

父元件html

<h1>父元件</h1>
 <input nz-input class="input" [(ngModel)]="drink" name="drink" placeholder="最愛喝的飲料">
 <input nz-input class="input" [(ngModel)]="eat" name="eat" placeholder="最愛吃的甜點">
 <button (click)="transferDataByService()" nz-button nzType="primary">傳值給子元件</button>
 <child-communication></child-communication>
           

父元件ts

import { CommunicationService } from './service/communication.service';
    import { Subscription } from 'rxjs';

    @Component({
        templateUrl: './component-communication.component.html',
        styleUrls: ['./component-communication.component.scss'],
        providers: [CommunicationService]
    })
    export class ComponentCommunicationComponent implements OnDestroy {
        subscription: Subscription;
        constructor(
            private communicationService: CommunicationService
        ) {
            this.subscription = communicationService.child$.subscribe(value => {
                this.drink = value.drink;
                this.eat = value.eat;
            });
        }
        drink = '奶茶';
        eat = '泡芙';
        transferDataByService() {
            this.communicationService.fatherSend({ drink: this.drink, eat: this.eat });
        }
        ngOnDestroy() {
            this.subscription.unsubscribe();
        }
    }
           

子元件html

<h1>子元件</h1>
 <input nz-input class="input" [(ngModel)]="drink" name="drink" placeholder="最愛喝的飲料">
 <input nz-input class="input" [(ngModel)]="eat" name="eat" placeholder="最愛吃的甜點">
 <button (click)="transferDataByService()" nz-button nzType="primary">傳值給父元件</button>
           

子元件ts

import { Subscription } from 'rxjs';

    @Component({
        selector: 'child-communication',
        templateUrl: './child-communication.component.html',
        styleUrls: ['./child-communication.component.scss']
    })
    export class ChildCommunicationComponent implements OnDestroy {
        subscription: Subscription;
        constructor(private communicationService: CommunicationService) {
            this.subscription = communicationService.father$.subscribe(value => {
                this.drink = value.drink;
                this.eat = value.eat;
            });
        }
        drink;
        eat;
        transferDataByService() {
            this.communicationService.childSend({ drink: this.drink, eat: this.eat });
        }
        ngOnDestroy() {
            this.subscription.unsubscribe();
        }
    }
           

Service通常有2個作用

一個是網絡請求的存放

另外個作用是集中處理業務, 主要是與視圖無關的一些處理. 這類Service則按需注入

如果隻是子孫的通訊不涉及其他的資料傳遞,就可以用emit

元件樣式

父子元件樣式互相不影響

父元件html

<h1>父元件</h1>
<span class="color bgColor">父元件的内容</span>
<child-styles1></child-styles1>
           

父元件css

.bgColor{
    background-color:red;
}
           

子元件html

<h1>子元件</h1>
<span class="color bgColor">子元件的内容</span>
           

子元件css

.color{
    color:blue;
}
           

給子元件添加邊界

:host(.selector)

:host, 意味着在整個子元件的根元素上添加樣式,selector表示隻有子元件根元素上存在這個選擇器類名selector的元件才應用這個樣式

<h1>父元件</h1>
<child-style2 class="first-element"></child-style2>
           
:host(.first-element){
    display:block;
    border: 1px solid red;
}
           

刺穿選擇器

::ng-deep

當父元件包含子元件時,原則上各自的樣式互不污染,非要進行污染時,則使用該選擇器

this.drink = value.drink;

this.eat = value.eat;

});

}

drink;

eat;

transferDataByService() {

this.communicationService.childSend({ drink: this.drink, eat: this.eat });

}

ngOnDestroy() {

this.subscription.unsubscribe();

}

}

Service通常有2個作用
一個是網絡請求的存放
另外個作用是集中處理業務, 主要是與視圖無關的一些處理. 這類Service則按需注入

如果隻是子孫的通訊不涉及其他的資料傳遞,就可以用emit



### 元件樣式

父子元件樣式互相不影響

**父元件html**

```html
<h1>父元件</h1>
<span class="color bgColor">父元件的内容</span>
<child-styles1></child-styles1>
           

父元件css

.bgColor{
    background-color:red;
}
           

子元件html

<h1>子元件</h1>
<span class="color bgColor">子元件的内容</span>
           

子元件css

.color{
    color:blue;
}
           

給子元件添加邊界

:host(.selector)

:host, 意味着在整個子元件的根元素上添加樣式,selector表示隻有子元件根元素上存在這個選擇器類名selector的元件才應用這個樣式

<h1>父元件</h1>
<child-style2 class="first-element"></child-style2>
           
:host(.first-element){
    display:block;
    border: 1px solid red;
}
           

刺穿選擇器

::ng-deep

當父元件包含子元件時,原則上各自的樣式互不污染,非要進行污染時,則使用該選擇器

繼續閱讀