<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                # Output() 軟件設計中有一個重要的原則是:高內聚、低耦合。那么什么是高內聚低耦合呢?簡單來說就是:能不求人時,盡量別求人。 我們以Login組件為例,在其中注入了Index組件。這就意味著若要成功的運行Login組件,當前模塊必須提供一個Index組件,也就是Login組件與Index組件是綁定在一起的。我們把這種Login組件與Index組件的綁定關系稱為**耦合**。而這種關系越多,就說明耦合度越高;這種關系越少,說明耦合度越低。 在父子組件這層關系中,父組件是離不開子組件的,而子組件完全的可以脫離父組件。這從我們的開發順序上便能夠完全體現出來:我們在開發登錄組件時并沒有父組件Index,但Login組件同樣被運行了起來;而在開發父組件Index時,就必須有這個Login組件。 父組件就像是一輛汽車,它是離不開發動機的。而子組件就像是發動機,沒有汽車發動機同樣運轉。用戶購買汽車的同時,必然要連同發動機一起賣給客戶;但如果用戶只想購買一個發動機,你卻要打包將汽車一起銷售給客戶就顯得不合理了。 而當前Login組件依賴于Index組件的情況,便屬于這種買發動機捆綁銷售汽車的不合理。 ## 解綁 現實生活中的汽車與發動機是靠規定的連接規則連在一起的,我們把這個規則統稱為**接口**。正是有了這些接口的存在,我們現實生活中的汽車實現了同一車型可以搭載不同排量,甚至是燃燒不同油品的發動機。 ![image-20210305142211529](https://img.kancloud.cn/a2/1a/a21a0234b95398c436abe4b7c0501d93_1510x818.png) 在Angular中同樣可以為組件定義接口,該接口規定好子組件向外發送的數據格式,父組件再去通過一定的方式接收該數據。從而達到子組件與父組件解綁的目的同,解綁后父組件仍然依賴于子組件,但子組件已經不再依賴于父組件了。 ## Output() 我們可以在Login組件中以`@Output()`定義相關屬性,繼而通過該屬性將登錄成功的消息發送給父組件。首先,我們還原Login組件中相關的歷史方法。 ```typescript +++ b/first-app/src/app/login/login.component.ts @@ -1,6 +1,5 @@ import {Component, OnInit} from '@angular/core'; import {HttpClient, HttpHeaders} from '@angular/common/http'; -import {IndexComponent} from '../index/index.component'; @Component({ selector: 'app-login', @@ -13,8 +12,7 @@ export class LoginComponent implements OnInit { password: string }; - constructor(private httpClient: HttpClient, - private indexComponent: IndexComponent) { + constructor(private httpClient: HttpClient) { } ngOnInit(): void { @@ -33,7 +31,7 @@ export class LoginComponent implements OnInit { .get( 'http://angular.api.codedemo.club:81/teacher/login', {headers: httpHeaders}) - .subscribe(teacher => this.indexComponent.login = true, + .subscribe(teacher => console.log('success'), error => console.log('發生錯誤, 登錄失敗', error)); } } ``` 然后增加用于通知父組件登錄成功的屬性,該屬性的類型為`EventEmmiter`,即:事件彈射器,彈射意為由下向上發射,在發射過程中可以發射多個,也可以發射一個,當然也可以不發射。以當前登錄為例,我們在每次登錄成功時發射一次數據,該數據將被所以觀察它的**人**的獲取到。 這有點像歷史上節日里的煙花。煙花在點燃后,將一個個彩蛋彈射升空,所有欣賞該煙花的人都會目睹它綻放時的風采。 ```typescript +++ b/first-app/src/app/login/login.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, EventEmitter, OnInit} from '@angular/core'; import {HttpClient, HttpHeaders} from '@angular/common/http'; @Component({ @@ -12,6 +12,8 @@ export class LoginComponent implements OnInit { password: string }; + beLogin = new EventEmitter<void>(); + constructor(private httpClient: HttpClient) { } ``` 需要注意的是當前的環境中存在多個`EventEmitter`類型,而我們在此需要的位于`'@angular/core'`中的`EventEmitter`。 最后我們加入`@Output()`注解,以表明該屬性用于向父組件彈射數據。 ```typescript +++ b/first-app/src/app/login/login.component.ts @@ -1,4 +1,4 @@ -import {Component, EventEmitter, OnInit} from '@angular/core'; +import {Component, EventEmitter, OnInit, Output} from '@angular/core'; import {HttpClient, HttpHeaders} from '@angular/common/http'; @Component({ @@ -12,6 +12,7 @@ export class LoginComponent implements OnInit { password: string }; + @Output() beLogin = new EventEmitter<boolean>(); constructor(private httpClient: HttpClient) { ``` ## 父組件獲取數據 其實我們早早地便學會了在父組中如何獲取子組件彈射的數據了,比如我們在獲取表單點擊事件時使用的`<form (ngSubmit)="onSubmit()">`。在Index組件中可以如下獲取Login組件彈射出的數據: ```html +++ b/first-app/src/app/index/index.component.html @@ -1,2 +1,2 @@ <app-root *ngIf="login"></app-root> -<app-login *ngIf="!login" ></app-login> +<app-login *ngIf="!login" (beLogin)="onLogin()" ></app-login> ``` 沒錯,該方法與前面我們寫過的`(ngSubmit)="onSubmit()">`寫法完全一致。 接下來結合C層的相關方法,便可以在子組件向上彈射數據時執行相關的代碼: ```typescript +++ b/first-app/src/app/index/index.component.ts @@ -15,4 +15,7 @@ export class IndexComponent implements OnInit { ngOnInit(): void { } + onLogin(): void { + console.log(new Date().toTimeString(), '子組件進行了空數據彈射'); + } } ``` ### 彈射測試 為了更清楚的弄清楚數據彈射的過程,我們在Login組件的`ngOnInit()`方法(該方法中的代碼會在組件初始化完畢后被自動執行1次)中增加如下測試代碼: ```typescript +++ b/first-app/src/app/login/login.component.ts @@ -19,6 +19,8 @@ export class LoginComponent implements OnInit { } ngOnInit(): void { + // 每1秒鐘向上彈出一個空數據 + setInterval(() => this.beLogin.emit(), 1000); } onSubmit(): void { ``` ![image-20210305150646626](https://img.kancloud.cn/46/e6/46e6d277f6d216a555c1162b179c9703_1758x342.png) ### 彈射非空數據 既然是數據彈射,必然可以彈射非空值。這取決于我們為彈射器定義的**泛型**。所謂泛型,就說類型比較寬泛,我們指定它是什么它就是什么,同時也只能是什么。 在此我們將請求登錄時獲取到的教師信息作為數據彈射時泛型的類型: ```typescript +++ b/first-app/src/app/login/login.component.ts @@ -13,7 +13,7 @@ export class LoginComponent implements OnInit { }; @Output() - beLogin = new EventEmitter<void>(); + beLogin = new EventEmitter<{ username: string, name: string, email: string, sex: boolean }>(); constructor(private httpClient: HttpClient) { } ``` 然后在模擬數據彈射時,彈射出一個教師: ```typescript +++ b/first-app/src/app/login/login.component.ts @@ -20,7 +20,12 @@ export class LoginComponent implements OnInit { ngOnInit(): void { // 每1秒鐘向上彈出一個空數據 - setInterval(() => this.beLogin.emit(), 1000); + setInterval(() => this.beLogin.emit({ + username: 'zhangsan', + name: '張三', + email: 'zhangsan@yunzhiclub.com', + sex: true + }), 1000); } onSubmit(): void { ``` 在父組件中,接收該彈射值: ```typescript +++ b/first-app/src/app/index/index.component.html @@ -1,2 +1,2 @@ <app-root *ngIf="login"></app-root> -<app-login *ngIf="!login" (beLogin)="onLogin()" ></app-login> +<app-login *ngIf="!login" (beLogin)="onLogin($event)" ></app-login> ``` ?? **注意**:在接收彈射值時,使用`$event`關鍵字。 在父組件的C層中接收該值,并進行打印。 ```typescript +++ b/first-app/src/app/index/index.component.ts @@ -15,7 +15,7 @@ export class IndexComponent implements OnInit { ngOnInit(): void { } - onLogin(): void { - console.log(new Date().toTimeString(), '子組件進行了空數據彈射'); + onLogin(teacher: { username: string, name: string, email: string, sex: boolean }): void { + console.log(new Date().toTimeString(), '子組件進行了數據彈射', teacher); } } ``` ![image-20210305154019725](https://img.kancloud.cn/7e/ba/7ebaa59752e88216daccea86f9a7d5d7_1748x352.png) ## 完成功能 讓我們去除測試的代碼,完成登錄組件向父組件彈出登錄教師的功能: ```typescript +++ b/first-app/src/app/login/login.component.ts @@ -19,13 +19,6 @@ export class LoginComponent implements OnInit { } ngOnInit(): void { - // 每1秒鐘向上彈出一個空數據 - setInterval(() => this.beLogin.emit({ - username: 'zhangsan', - name: '張三', - email: 'zhangsan@yunzhiclub.com', - sex: true - }), 1000); } onSubmit(): void { @@ -38,10 +31,10 @@ export class LoginComponent implements OnInit { httpHeaders = httpHeaders.append('Authorization', 'Basic ' + authToken); this.httpClient - .get( + .get<any>( 'http://angular.api.codedemo.club:81/teacher/login', {headers: httpHeaders}) - .subscribe(teacher => console.log('success'), + .subscribe(teacher => this.beLogin.emit(teacher), error => console.log('發生錯誤, 登錄失敗', error)); } } ``` 父組件: ```typescript +++ b/first-app/src/app/index/index.component.ts @@ -17,5 +17,6 @@ export class IndexComponent implements OnInit { onLogin(teacher: { username: string, name: string, email: string, sex: boolean }): void { console.log(new Date().toTimeString(), '子組件進行了數據彈射', teacher); + this.login = true; } } ``` ?? 測試成功! ![image-20210305121257833](https://img.kancloud.cn/0a/7b/0a7b7f1208bab18e46c08a1e5692296d_1416x542.png) ?? ![image-20210305121333839](https://img.kancloud.cn/0e/a2/0ea2f865f7fc06074f606dc60eb72118_1520x514.png) ?? ![image-20210305154501621](https://img.kancloud.cn/f3/bc/f3bc0799f5726cd2ea7bdc10b1d80beb_1768x406.png) 最后,引入`RouterTestingModule`以消除控制臺關于`router-outlet`的錯誤: ```typescript +++ b/first-app/src/app/index/index.component.spec.ts @@ -5,6 +5,7 @@ import {AppComponent} from '../app.component'; import {LoginComponent} from '../login/login.component'; import {HttpClientModule} from '@angular/common/http'; import {FormsModule} from '@angular/forms'; +import {RouterTestingModule} from '@angular/router/testing'; describe('IndexComponent', () => { let component: IndexComponent; @@ -13,7 +14,7 @@ describe('IndexComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [IndexComponent, AppComponent, LoginComponent], - imports: [HttpClientModule, FormsModule] + imports: [HttpClientModule, FormsModule, RouterTestingModule] }) .compileComponents(); }); ``` ## 作業 自學TypeScript關于[類](https://www.tslang.cn/docs/handbook/classes.html)的一節。建立教師`Teacher`類,并使用該類替換本節中`{ username: string, name: string, email: string, sex: boolean }`相關的代碼。 | 名稱 | 地址 | 備注 | | --------------------------- | ------------------------------------------------------------ | ---- | | @Output()把數據發送給父組件 | [https://angular.cn/guide/inputs-outputs#output](https://angular.cn/guide/inputs-outputs#output) | | | EventEmitter | [https://angular.cn/api/core/EventEmitter](https://angular.cn/api/core/EventEmitter) | | | 高內聚低耦合 | [https://baike.baidu.com/item/%E9%AB%98%E5%86%85%E8%81%9A%E4%BD%8E%E8%80%A6%E5%90%88](https://baike.baidu.com/item/%E9%AB%98%E5%86%85%E8%81%9A%E4%BD%8E%E8%80%A6%E5%90%88) | | | setInterval | [https://www.runoob.com/jsref/met-win-setinterval.html](https://www.runoob.com/jsref/met-win-setinterval.html) | | | Date | [https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date) | | | 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step3.5.zip](https://github.com/mengyunzhi/angular11-guild/archive/step3.5.zip) | |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看