<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>

                我們在`src/app/clazz`文件夾中,新建klassSelect組件: > 筆者在這犯了一個命名錯誤,組件的名稱應該是`TeacherSelect`,而不是`KlassSelect`。 ```bash panjie@panjies-Mac-Pro clazz % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app/clazz panjie@panjies-Mac-Pro clazz % ng g c klassSelect CREATE src/app/clazz/klass-select/klass-select.component.css (0 bytes) CREATE src/app/clazz/klass-select/klass-select.component.html (27 bytes) CREATE src/app/clazz/klass-select/klass-select.component.spec.ts (662 bytes) CREATE src/app/clazz/klass-select/klass-select.component.ts (298 bytes) UPDATE src/app/clazz/clazz.module.ts (409 bytes) ``` 然后把班級添加組件中的相關的V層代碼復制過來: ```html +++ b/first-app/src/app/clazz/klass-select/klass-select.component.html <select id="teacher" class="form-control" name="teacher" [(ngModel)]="clazz.teacherId"> <option *ngFor="let teacher of teachers" [ngValue]="teacher.id"> {{teacher.name}} </option> </select> ``` 再把班級添加組件中的相關的C層代碼復制過來: ```typescript +++ b/first-app/src/app/clazz/klass-select/klass-select.component.ts export class KlassSelectComponent implements OnInit { teachers = new Array<Teacher>(); teacherId: number | undefined; constructor(private httpClient: HttpClient) { } ngOnInit(): void { // 獲取所有教師 this.httpClient.get<Array<Teacher>>('teacher') .subscribe(teachers => this.teachers = teachers); } } ``` 最后變更當V層中的`ngModel`,并使用`ng t`開啟測試。 ```html +++ b/first-app/src/app/clazz/klass-select/klass-select.component.html @@ -1,6 +1,5 @@ <select id="teacher" class="form-control" - [(ngModel)]="clazz.teacherId"> + [(ngModel)]="teacherId"> name="teacher"> <option *ngFor="let teacher of teachers" [ngValue]="teacher.id"> {{teacher.name}} </option> ``` ## MockApi 在單元測試的動態測試模塊中,引用`MockApiInterceptor`以及`HttpModule`,并加`TeacherMockApi`以提供teacher相關的后臺模塊數據。 ```typescript +++ b/first-app/src/app/clazz/klass-select/klass-select.component.spec.ts @@ -1,6 +1,9 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {KlassSelectComponent} from './klass-select.component'; +import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http'; +import {MockApiInterceptor} from '@yunzhi/ng-mock-api'; +import {TeacherMockApi} from '../../mock-api/teacher.mock.api'; describe('KlassSelectComponent', () => { let component: KlassSelectComponent; @@ -8,7 +11,18 @@ describe('KlassSelectComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [KlassSelectComponent] + declarations: [KlassSelectComponent], + imports: [ + HttpClientModule + ], + providers: [ + { + provide: HTTP_INTERCEPTORS, multi: true, + useClass: MockApiInterceptor.forRoot([ + TeacherMockApi + ]) + } + ] }) .compileComponents(); }); ``` 最后也是最關鍵的,保證整個項目中僅有當前一個 `fit`,并且在其中啟用自動檢測變更機制: ```typescript +++ b/first-app/src/app/clazz/klass-select/klass-select.component.spec.ts @@ -35,5 +35,6 @@ describe('KlassSelectComponent', () => { fit('should create', () => { expect(component).toBeTruthy(); + fixture.autoDetectChanges(); }); }); ``` ![image-20210324084043748](https://img.kancloud.cn/ff/ab/ffab5f83c4049bc5e37438562aa276db_964x188.png) 效果有了,還需要關注控制臺: ![image-20210324085431814](https://img.kancloud.cn/12/28/12282d2af2a7cb945a36030064ba49a7_1130x102.png) 提示說`option`上不能綁定`ngValue`,原因是`ngValue`并不是個已知的屬性。請嘗試解決后繼續學習。 ## @Output() 教師被用戶選擇后,我們需要將被選擇的數據通過`output()`方法將數據彈出。在前面的章節中我們已經學習過`@Output()`的使用方法,在此我們大概可以總結出:如果一個組件需要向外傳送數據,則需要加入`@Output()`注解,該注解的字段類型為`EventEmitter`,用于向父組件彈射數據。 ```typescript +++ b/first-app/src/app/clazz/klass-select/klass-select.component.ts @@ -1,7 +1,8 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnInit, EventEmitter, Output} from '@angular/core'; import {Teacher} from '../../entity/teacher'; import {HttpClient} from '@angular/common/http'; + @Component({ selector: 'app-klass-select', templateUrl: './klass-select.component.html', @@ -11,6 +12,9 @@ import {HttpClient} from '@angular/common/http'; export class KlassSelectComponent implements OnInit { teachers = new Array<Teacher>(); teacherId: number | undefined; + @Output() + beChange = new EventEmitter<any>(); + constructor(private httpClient: HttpClient) { } ``` 而至于誰會成為自己的父組件,當前組件并不關心也不需要關心,因為成為自己父組件的前提僅僅是在組件的V層中引入`app-klass-select`,所以任意組件都可以成為自己的父組件。而無論誰成為自己的父組件,當前組件均是通過`@Output()`向其彈值。 ## 數據監聽 教師能選擇了,`@Output`也有了。當下的問題是如何感知用戶選擇了某個教師并在選擇后通過`@Output()`向外發送通知。Angular最少提供了兩種方案來解決此類問題。 > 這種對數據的感知又被稱為**數據監聽Data Listen**,完成這項功能的又被稱為**數據監聽器 Data Listener**。 ### (change)事件 Angular提供的`(change)`事件可以感知表單項的變化,在變化時被調用一次: ```html +++ b/first-app/src/app/clazz/klass-select/klass-select.component.html @@ -1,6 +1,7 @@ <select id="teacher" class="form-control" name="teacher" - [(ngModel)]="teacherId"> + [(ngModel)]="teacherId" + (change)="onChange()"> <option *ngFor="let teacher of teachers" [ngValue]="teacher.id"> {{teacher.name}} </option> ``` C層創建對應的方法: ```typescript +++ b/first-app/src/app/clazz/klass-select/klass-select.component.ts @@ -24,4 +24,7 @@ export class KlassSelectComponent implements OnInit { .subscribe(teachers => this.teachers = teachers); } + onChange(): void { + console.log('change called'); + } } ``` 此時,我們選擇教師列表中的教師時,C層中的`onChange`方法將被調用一次: ![image-20210324090833599](https://img.kancloud.cn/6a/9b/6a9b9e1cdd8e8251a1e9cc91d01cc148_1428x436.png) 如此以來,選擇教師后調用`onChange()`方法,接著便可以在`onChange()`方法中向外彈出選擇的教師ID了。 ```typescript +++ b/first-app/src/app/clazz/klass-select/klass-select.component.ts @@ -24,4 +24,9 @@ export class KlassSelectComponent implements OnInit { .subscribe(teachers => this.teachers = teachers); } + onChange(): void { + console.log('change called'); + console.log(this.teacherId); + this.beChange.emit(this.teacherId); + } } ``` #### 測試 凡事都不能想當然,除非你有百分百的把握(即使是這樣,實際中也會犯錯?),數據彈射出是否被成功接收,還需要進行測試: ```typescript +++ b/first-app/src/app/clazz/klass-select/klass-select.component.spec.ts @@ -38,5 +38,7 @@ describe('KlassSelectComponent', () => { fit('should create', () => { expect(component).toBeTruthy(); fixture.autoDetectChanges(); + component.beChange + .subscribe((data: any) => console.log('接收到了彈出的數據', data)); }); }); ``` ![image-20210324092704858](https://img.kancloud.cn/37/86/3786cbc6b3944be981b64a928c4ada80_918x168.png) 最后,我們規范一下`beChange`事件彈出器的數據類型: ```typescript +++ b/first-app/src/app/clazz/klass-select/klass-select.component.ts @@ -13,7 +13,7 @@ export class KlassSelectComponent implements OnInit { teacherId: number | undefined; @Output() - beChange = new EventEmitter<any>(); + beChange = new EventEmitter<number>(); constructor(private httpClient: HttpClient) { } ``` 同步修正單元測試: ```typescript +++ b/first-app/src/app/clazz/klass-select/klass-select.component.spec.ts @@ -39,6 +39,6 @@ describe('KlassSelectComponent', () => { expect(component).toBeTruthy(); fixture.autoDetectChanges(); component.beChange - .subscribe((data: any) => console.log('接收到了彈出的數據', data)); + .subscribe((data: number) => console.log('接收到了彈出的數據', data)); }); }); ``` ### 響應式表單 `(beChange)`事件雖然可用,但實際上卻是沿用了AngularJS(Angular的前身)的思路。而Angular則提供了效率更高,更加面向對應的響應式表單來解決此類問題。 由于**響應式表單**是Angular的核心知識點之一,我們單獨在下下節內容中進行講解。 | 名稱 | 鏈接 | | --------- | ------------------------------------------------------------ | | OnChanges | [https://angular.cn/api/core/OnChanges](https://angular.cn/api/core/OnChanges) | | 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step6.2.1.zip](https://github.com/mengyunzhi/angular11-guild/archive/step6.2.1.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>

                              哎呀哎呀视频在线观看