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

                本節我們完成最核心、最簡單的更新功能。 ## MockApi 更新功能需要調用后臺更新班級的接口,該接口信息如下: ``` PUT /clazz/{id} ``` | **類型Type** | **名稱Name** | **描述Description** | **類型Schema** | | :----------- | :----------- | :------------------ | :----------------------------------------------------------- | | Body | | 班級 | `{name: string, teacher: {id: number}}` | | Response | 成功 | Status Code: 204 | `{id: number, name: string, teacher: {id: number, name: string}}` | 依此建立MockApi信息: ```typescript +++ b/first-app/src/app/mock-api/clazz.mock.api.ts @@ -92,6 +92,22 @@ export class ClazzMockApi implements MockApiInterface { } } as Clazz; } + }, + { + method: 'PUT', + url: `/clazz/(\\d+)`, + result: (urlMatches: string[], options: RequestOptions) => { + const id = +urlMatches[1]; + const clazz = options.body as Clazz; + return { + id, + name: clazz.name, + teacher: { + id: clazz.teacher.id, + name: randomString('測試教師') + } as Teacher + } as Clazz; + } } ]; } ``` ## onSubmit 先V層: ```html +++ b/first-app/src/app/clazz/edit/edit.component.html @@ -1,4 +1,4 @@ -<form class="container-sm"> +<form class="container-sm" (ngSubmit)="onSubmit()"> ``` 在C層: ```typescript +++ b/first-app/src/app/clazz/edit/edit.component.ts @@ -43,4 +43,8 @@ export class EditComponent implements OnInit { console.log('接收到了選擇的teacherId', $event); this.teacherId = $event; } + + onSubmit(): void { + console.log('點擊了提交按鈕'); + } } ``` 測試: ![image-20210402164059299](https://img.kancloud.cn/e5/c5/e5c501fda786a5000dfb9d2b15c57573_1352x388.png) 此時點擊保存按鈕時,頁面會執行刷新操作。這是由于我們未對V層中的`form`元素加以控制的原因。在默認情況下,`form`中的提交按鈕并點擊時,該表單會依照其`action`屬性的值發送數據。在默認情況下`action`的值為空,則當點擊保存按鈕時,數據會發送至當前頁面,即在當前頁的情況下重新打開當前頁,所以點擊保存按鈕時就像是刷新了一樣。 解決的方法有兩種,第一種方法我們已經學習過:加入`FormsModule`。`FormsModule`會禁止表單的默認提交行為,此時再點擊保存按鈕,則將觸發`onSubmit()`方法: ```typescript +++ b/first-app/src/app/clazz/edit/edit.component.spec.ts @@ -17,7 +17,8 @@ describe('EditComponent', () => { imports: [ MockApiTestingModule, ReactiveFormsModule, - RouterTestingModule + RouterTestingModule, + FormsModule ] ``` ![image-20210402165008228](https://img.kancloud.cn/94/78/947871d26b497a61f4a75c9fdbd81ba5_530x64.png) 第二種方案是使用`FormGroup`。 在繼續以前,我們刪除剛剛引入的`FormModule`: ```typescript @@ -17,8 +17,7 @@ describe('EditComponent', () => { imports: [ MockApiTestingModule, ReactiveFormsModule, - RouterTestingModule, - FormsModule + RouterTestingModule ] }) .compileComponents(); ``` ## FormGroup 與`FormControl`類似Angular也提供了用于綁定`form` 元素的`FormGroup`。Group譯為**組**,在使用過程中,會在`FormGroup`中加入多個`FormControl`,以使多個`FormControl`組合在一起。`FormGroup`與其它的對象的初始化方式完全相同: ```typescript +++ b/first-app/src/app/clazz/edit/edit.component.ts @@ -2,7 +2,7 @@ import {Component, OnInit} from '@angular/core'; import {ActivatedRoute} from '@angular/router'; import {HttpClient} from '@angular/common/http'; import {Clazz} from '../../entity/clazz'; -import {FormControl, Validators} from '@angular/forms'; +import {FormControl, FormGroup, Validators} from '@angular/forms'; @Component({ selector: 'app-edit', @@ -15,6 +15,10 @@ export class EditComponent implements OnInit { */ nameFormControl = new FormControl('', Validators.required); teacherId: number | undefined; + /** + * 表單組,用于存放多個formControl + */ + formGroup = new FormGroup({}); ``` 初始過程中`FormGroup`接收一個對象做為參數,在該對象中可以定義多個`FormControl`,比如將`nameFormControl`添加到`FormGroup`中。 ```typescript - formGroup = new FormGroup({}); + formGroup = new FormGroup({ + name: this.nameFormControl + }); ``` ### 綁定FormGroup 在V層中對FormGroup的綁定也與FormControl相似: ```html +++ b/first-app/src/app/clazz/edit/edit.component.html @@ -1,4 +1,4 @@ -<form class="container-sm" (ngSubmit)="onSubmit()"> +<form class="container-sm" (ngSubmit)="onSubmit()" [formGroup]="formGroup"> <div class="mb-3 row"> <label class="col-sm-2 col-form-label">名稱</label> <div class="col-sm-10"> ``` 此時即使我們沒有引入`FormsModule`,在點擊提交按鈕時,`Form`也不會刷新了: ![image-20210402165008228](https://img.kancloud.cn/94/78/947871d26b497a61f4a75c9fdbd81ba5_530x64.png) ## 校驗 使用`FormGroup`會帶來很多好處,比如某個表單有10個字段,每個字段的驗證方式都不一樣。如果不使用`FormGroup`,則在處理是否禁用保存按鈕時,代碼則會又臭又長: ```html *ngIf="a === undefined || b === undefined || c === '' || d < 10" ``` 而有了`FormGroup`以后,無論有多少個驗證的字段,只要有一個字段不符合條件,則都將使`FormGroup`的`invalid` 值為`true`,所以我們利用該特點可以輕構的定義保存按鈕的`disabled`屬性: ```html <div class="col-sm-10 offset-2"> - <button class="btn btn-primary">保存 + <button class="btn btn-primary" [disabled]="formGroup.invalid">保存 </button> </div> ``` 此時當班級名稱不符合規則時,則保存按鈕處于不可點擊狀態: ![image-20210402165954569](https://img.kancloud.cn/83/a1/83a13d0791bc16e9f0250f81aa33c2ea_1136x402.png) ## 完成功能 最后,完成班級更新功能。在更新時需要將更新的班級ID加入到請求的URL中,將班級名稱、對應的班主任ID傳入請求body中。 ### 緩存ID 在C層中可以很簡單的獲取到要更新的班級ID,比如`loadById()`的請求參數則代表了當前要更新的ID。所以可以在C層中建立一個屬性來存儲這個ID: ```typescript +++ b/first-app/src/app/clazz/edit/edit.component.ts @@ -15,6 +15,10 @@ export class EditComponent implements OnInit { */ nameFormControl = new FormControl('', Validators.required); teacherId: number | undefined; + /** + * 要更新的班級ID + */ + clazzId: number | undefined; /** * 表單組,用于存放多個formControl */ @@ -37,6 +41,7 @@ export class EditComponent implements OnInit { */ loadById(id: number): void { console.log('loadById'); + this.clazzId = id; this.httpClient.get<Clazz>('/clazz/' + id.toString()) .subscribe(clazz => { console.log('接收到了clazz', clazz); ``` ### 更新 緩存了班級ID后,在`onSubmit()`方法中便可以獲取到更新班級需要的幾個核心因素了: ```typescript onSubmit(): void { console.log('點擊了提交按鈕'); + const clazzId = this.clazzId; + const name = this.nameFormControl.value; ?? + const teacherId = this.teacherId; } ``` `FormControl`上有個`value`屬性,該屬性的值為`FromControl`的當前值 ?? 最后,使上述變量進行接拼并發起后臺請求: ```typescript +++ b/first-app/src/app/clazz/edit/edit.component.ts @@ -3,6 +3,7 @@ import {ActivatedRoute} from '@angular/router'; import {HttpClient} from '@angular/common/http'; import {Clazz} from '../../entity/clazz'; import {FormControl, FormGroup, Validators} from '@angular/forms'; +import {Teacher} from '../../entity/teacher'; @Component({ selector: 'app-edit', @@ -60,5 +61,13 @@ export class EditComponent implements OnInit { const clazzId = this.clazzId; const name = this.nameFormControl.value; const teacherId = this.teacherId; + const clazz = new Clazz({ + name, + teacher: {id: teacherId} as Teacher + }); + this.httpClient.put<Clazz>(`/clazz/${clazzId}`, clazz) + .subscribe( + () => console.log('更新成功'), ?? + error => console.log(error)); } } ``` 后臺在更新成功后將返回更新后的班級信息,如果我們在后續的代碼中需要這個更新后的班級信息,則可以將此處改寫為` (data) => console.log('更新成功', data); 后續使用data信息`;如果在后續的代碼中不需要這個更新后的信息,則可以在參數中省咯`data`而直接書寫為`()` ??。 這種在回調函數的方法中可寫可不寫的參數的用法,也是JavaScript一個特性。我們在書寫MockApi時也使用到了這個特性,你注意到了嗎? ![image-20210406085228202](https://img.kancloud.cn/60/2d/602d4114ec636f2964d3f79b118bf58c_834x140.png) ## 代碼重構 剛剛的代碼雖然成功的完成了功能,但各個函數之間的鏈接還有不小的提升空間。比如我們在`loadById()`中緩存了班級ID,然后在`onSubmit()`方法中使用到了這個ID。如果我們不在`loadById()`中寫上一些注釋,后續的成員便不清楚在當前方法中為什么要有這么一行代碼。 ```typescript loadById(id: number): void { console.log('loadById'); this.clazzId = id; ?? this.httpClient.get<Clazz>('/clazz/' + id.toString()) .subscribe(clazz => { console.log('接收到了clazz', clazz); this.nameFormControl.patchValue(clazz.name); this.teacherId = clazz.teacher.id; }, error => console.log(error)); } ``` 在團隊開發中,我們最不愿意看到的代碼難懂且沒有注釋的代碼;第二個不愿意看到雖有注釋但邏輯難懂的代碼;最希望看到的是有注釋且邏輯易懂的代碼;如果在有注釋和易懂間只能選擇一個,那么更愿意看到易懂的代碼。 在更新功能中,充分的利用`FormGroup`能使代碼看起來更易懂。`FormGroup`對應著V層的表單,表示更新的班級信息,在`loadById()`對`FormGroup`進行操作便起到告訴隊友該項信息將來用于更新表單;`teacherId`當然也是如此。帶著此思想我們更新代碼如下: ```typescript formGroup = new FormGroup({ - name: this.nameFormControl + id: new FormControl(), + name: this.nameFormControl, + teacherId: new FormControl() }); ``` 如此以下`formGroup`中便存儲了更新班級時所需要的所有數據。此外我們還可以為其設置校驗屬性: ```typescript formGroup = new FormGroup({ - id: new FormControl(), + id: new FormControl(null, Validators.required), name: this.nameFormControl, - teacherId: new FormControl() + teacherId: new FormControl(null, Validators.required) }); ``` 此時一旦我們在開發時不小心忘掉了設置要更新班級的ID,則會使得**保存**銨鈕為不可用狀態。 有了`FormGroup`后,便可以將更新的班級`ID`以及選擇的`teacherId`全部放到`FormGroup`中了: ```typescript - /** - * 要更新的班級ID - */ - clazzId: number | undefined; /** * 表單組,用于存放多個formControl */ @@ -44,25 +40,27 @@ export class EditComponent implements OnInit { */ loadById(id: number): void { console.log('loadById'); - this.clazzId = id; + this.formGroup.get('id')?.setValue(id); ?? this.httpClient.get<Clazz>('/clazz/' + id.toString()) .subscribe(clazz => { console.log('接收到了clazz', clazz); this.nameFormControl.patchValue(clazz.name); this.teacherId = clazz.teacher.id; + this.formGroup.get('teacherId')?.setValue(clazz.teacher.id); ?? }, error => console.log(error)); } onTeacherChange($event: number): void { console.log('接收到了選擇的teacherId', $event); this.teacherId = $event; + this.formGroup.get('teacherId')?.setValue($event); ?? } onSubmit(): void { console.log('點擊了提交按鈕'); - const clazzId = this.clazzId; + const clazzId = this.formGroup.get('id')?.value; ?? const name = this.nameFormControl.value; - const teacherId = this.teacherId; + const teacherId = this.formGroup.get('teacherId')?.value; ?? const clazz = new Clazz({ name, teacher: {id: teacherId} as Teacher ``` `FormGroup`中的`get()`方法可以根據定義時的屬性來獲取其中的`FomControl`。同于該方法的返回值可能為`null`,所以加入`?`來規避相應的語法錯誤。?? 測試功能正常: ![image-20210406085228202](https://img.kancloud.cn/60/2d/602d4114ec636f2964d3f79b118bf58c_834x140.png) 雖然說是看起來成功了吧,但在控制臺中看不到傳遞給后臺的數據總是讓人感覺不踏實。為此在MOAKAPI中打印兩個數據: ```typescript +++ b/first-app/src/app/mock-api/clazz.mock.api.ts @@ -99,6 +99,8 @@ export class ClazzMockApi implements MockApiInterface { result: (urlMatches: string[], options: RequestOptions) => { const id = +urlMatches[1]; const clazz = options.body as Clazz; + console.log('接收到了id', id); + console.log('接收到的clazz', clazz); return { id, name: clazz.name, ``` 最后在控制臺中看到了更新時提交給后臺的數據,這下心里總算是踏實了。 ![image-20210406094813317](https://img.kancloud.cn/81/eb/81eb9fb5ab6a314dd7f1bf87a4fa4736_1090x280.png) ## 進階 我們剛剛使用`FormGroup`移除了組件中`classId`屬性,而在處理`teacherId`時,我們仍然不能夠刪除組件的中`teacherId`。如果能把`teacherId`也移除掉的話`FormGroup`便能夠完全地發揮出其功能了。 此時便不得不停下腳步思索:同樣是編輯,為什么我們并沒有創建一個`name`屬性,卻偏偏創建了`teacherId`屬性了。 這是因為在我們V層中使用了子組件`app-klass-select`,該組件的`@Input()`必須接收了一個`id`,此`id`便是當前組件下的`teacherId`屬性。所以如果子組件`app-klass-select`可以向`input`元素一樣支持`[formControl]`屬性就可以刪除`teacherId` ,轉而使用`FromControl`代替了。 我們在下節中將講述如何自定義一個適用`FormControl`的組件。 ## 本節作業 1. 在MockApi添加`console.log()`,充分了解在進行http請求時,`result`屬性對應的回調函數的參數值都是什么。 2. 請說明為什么在MockApi中有的`result`對應的回調函數上有參數,有的卻沒有。兩種寫法有何不同,什么時候應該帶參數,什么時候又可以不帶參數? 3. 在組件更新成功后,在控制臺打印更新成功后的返回值。請思索:如果不需要打印成功后的返回值,更新方法還可以怎么寫? | 名稱 | 鏈接 | | -------------- | ------------------------------------------------------------ | | 把表單控件分組 | [https://angular.cn/guide/reactive-forms#grouping-form-controls](https://angular.cn/guide/reactive-forms#grouping-form-controls) | | 驗證表單輸入 | [https://angular.cn/guide/reactive-forms#validating-form-input](https://angular.cn/guide/reactive-forms#validating-form-input) | | 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step6.4.4.zip](https://github.com/mengyunzhi/angular11-guild/archive/step6.4.4.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>

                              哎呀哎呀视频在线观看