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

                在編輯班級時,我們希望有如下的效果: ![](https://img.kancloud.cn/27/62/2762a482e7c83eabb862baa5767731ef_579x343.gif) 即引用教師組件后,教師組件可以根據傳入的教師信息自動選中某位教師,這就涉及到了組件的**Input 輸入**。 # 靜態的輸入值 所謂靜態的輸入值是指:一旦將數據輸入至組件,該值就不再變更或是無需考慮其變更,此種情況的實現最為簡單。我們仍然啟動前后臺,并在班級編輯組件中啟用選擇教師組件。 klass/edit/edit.component.html ``` <h3>編輯班級</h3> <form (ngSubmit)="onSubmit()" [formGroup]="formGroup"> <label for="name">名稱:<input id="name" type="text" formControlName="name"/></label> <label for="teacherId">教師:<app-teacher-select id="teacherId"></app-teacher-select></label> <button>更新</button> </form> ``` ![](https://img.kancloud.cn/f3/5c/f35c5bc15fdc20d78e66b514f386c359_394x313.png) ## @Input 我們使用了`@Input()`來標識了某個屬性,進而表示該屬性為**輸入型屬性**,它的作用是接收組件的傳入值。 klass/teacher-select/teacher-select.component.ts ``` @Output() selected = new EventEmitter<Teacher>(); @Input()? teacher: { id: number };? constructor(private httpClient: HttpClient) { } ``` * ? 標識teacher為**輸入型屬性** * ? 標識接收的teacher類型為對象,該對象中必須存在`id`屬性,且該屬性的類型為`number` 由于我們只需要根據關鍵字`id`來判斷該組件具體應該選中哪個教師,所以在數據類型上只規定`{ id: number }`,當然你也可以規定傳入的對象類型必須是一個`教師`,比如:`teacher: Teacher`。 ## 選中這個教師 有了傳入的教師ID后,我們便可以根據這個ID來確定應該選中哪個選項了。在上個小節中我們通過發現:當`select`中的某個`option`被選中時,`select`對就在的`fomControl`的值就會對應被設置為哪一個;在前面的小節中,我們還學習了`FormControl`具有雙向數據綁定的特質。也就是說: * [ ] 當`option`被選中時,數據將綁定到`FormControl`值。 * [ ] 反過來:當數據被綁定到`FormControl`的值時,某個對應的`option`則會自動被綁定。 所以要想實現選中某個教師的功能,我們告組件當前的`select`對應綁定了哪個教師即可: klass/teacher-select/teacher-select.component.ts ``` /** * 獲取所有的教師,并傳給V層 */ ngOnInit() { this.teacherSelect = new FormControl(); const url = 'http://localhost:8080/Teacher'; this.httpClient.get(url) .subscribe((teachers: Array<Teacher>) => { this.teachers = teachers; this.teachers.forEach((teacher: Teacher) => { ? if (teacher.id === this.teacher.id) { this.teacherSelect.setValue(teacher); } }); }); ``` * ? 對教師數組進行遍歷,當傳入的教師ID與當前的遍歷項教師ID相同時,則設置`select`的選中值。 ## 測試 要想使用剛剛我們創建的組件,則必須在向該組件中傳入`教師`。按此思想我們對原班級編輯組件進行改造。 klass/edit/edit.component.html ``` <h3>編輯班級</h3> <form (ngSubmit)="onSubmit()" [formGroup]="formGroup"> <label for="name">名稱:<input id="name" type="text" formControlName="name"/></label> <label for="teacherId">教師:<app-teacher-select id="teacherId" [teacher]="teacher"? ></app-teacher-select></label> <button>更新</button> </form> ``` * ? 第一個`teacher`對應選擇教師組件的`@Input() teacher`;第二個`teacher`應該對應班級編輯組件中的C層屬性。 ``` ... formGroup: FormGroup; teacher: Teacher; ? private url: string; ... /** * 加載要編輯的班級數據 */ loadData(): void { this.httpClient.get(this.getUrl()) .subscribe((klass: Klass) => { this.formGroup.setValue({name: klass.name, teacherId: klass.teacher.id}); this.teacher = klass.teacher; ? }, () => { console.error(`${this.getUrl()}請求發生錯誤`); }); } ``` 我們先回到班級列表,然后點擊編輯按鈕觸發該組件,測試結果如下: ![](https://img.kancloud.cn/c4/94/c49482be8b1813652b455489e93fa170_1238x456.png) 該錯誤提示我們:在` (teacher.id === this.teacher.id) {`代碼發生錯誤:不能夠在`undefined`上讀取`id`屬性。這是我們新手在使用angular進行開發時常常會遇到的問題。 ## 異步請求 如果想弄清楚產生這個錯誤的原因還需要從angular進行組件間的調用的流程說起: ![](https://img.kancloud.cn/fd/a5/fda5e636ea0c6b4f157a2bfabb57a431_740x532.png) 如上圖所示,在進行組件的構建過程中。總共發起了兩次資源請求(進行http請求)。而js在進行資源請求(進行http請求)時發起的為異步操作。也就是說:雖然班級編輯組件早于選擇教師組件發起了http請求,但收到請求結果的順序卻不一定早于后者。所以該組件的測試就會有兩種情況發生: * [ ] 如果班級編輯組件的http請求返回**早**于選擇教師組件的,則在選擇教師組件進行teacher是否為undefined判斷時:teacher的值并不為undefined,所以不會發生錯誤. * [ ] 如果班級編輯組件的http請求返回**晚**于選擇教師組件的,則在選擇教師組件進行teacher是否為undefined判斷時:teacher的值仍然為初始化的值undefined,此時便會發生錯誤。 當我們由班級列表中點擊編輯按鈕進入該組件時,網絡請求大概會是這個樣子: ![](https://img.kancloud.cn/21/ad/21ad00a6c45ff54605f5fa641a596520_943x367.png) 由于后發起訪問的teacher**早**于先發起訪問的klass,所以在執行相關語句時,teacher的值為undefined,故而引發了`Cannot read property 'id' of undefined`錯誤。 ## 證真是學習、證偽是提升 更有意思的測試結果是: * [ ] 如果我們在不打開控制臺的前提下,直接刷新編輯班級頁面,那么10之有9會發生該錯誤。 * [ ] 如果我們在打開控制臺的前提下,直接刷新編輯班級頁面,那么又基本上不會發生該錯誤。 ![](https://img.kancloud.cn/d1/9b/d19bb96626568c88079b2770bc8da8c9_547x487.gif) 如果在前面我的理論支持下,是否會自動綁定教師應該與是否打開控制臺無關,那么為什么在打開控制臺的情況下,就正常了呢? ![](https://img.kancloud.cn/33/bc/33bc1ce359a57bba5aa29802bc8d5ab1_1653x209.png) 這主要是由于最后這個請求的存在,我們發現此請求是在klass請求完成后發起的。我們猜測:當**刷新**頁面時angular會進行項目的初始化,過程大概應該是這樣的: * [ ] 掃描整個項目 * [ ] 掃描項目中的當前所用到的組件(班級編輯、選擇教師),進行預請求 * [ ] 構建項目 * [ ] 使用預請求的返回結果構建組件(此時teacher的值并不是undefined,所以構建成功) 在此理論的支持下,如果我們在開啟控制臺的前提下,先打開班級列表組件,然后再點編輯按鈕,那么順序應該是這樣的: * [ ] 掃描整個項目 * [ ] 掃描項目中的當前所用到的組件(班級列表),進行預請求 * [ ] 構建項目 * [ ] 使用預請求的返回結果構建組件(此時teacher的值并不是undefined,所以構建成功) * [ ] 點擊編輯按鈕進行跳轉,構建班級編輯、選擇教師組件 * [ ] 優先返回了teacher數據,而且發生錯誤。 測試: ![](https://img.kancloud.cn/a2/60/a260c1646aea058de6ca131c75cf4d16_547x487.gif) 測試足夠支持我們的猜想。所以最終的結論是: * 當有異步請求時,程序的執行順序會受異步請求返回先后的影響。 * 當打開控制臺時開發進行頁面刷新時,angular會嘗試啟用掃描及加載機制。 * 在正式的開發中,應該適時的關閉控制臺來進行組件的測試。 ## 使用ngIf來規避undefined錯誤 由于異步請求的存在,我們無法預測哪個請求會先返回。這無疑將會降低我們系統在使用中的可靠性。暴露很多類似于`在我電腦上沒問題`、`測試的時候是好好的`這種好像低級、但實際是**"無解"**的問題。 要防止選擇教師組件報這樣的錯誤我們只需要保證:在編輯班級組件成功的獲取到班級數據前,不要渲染選擇教師組件即可。而`*ngIf`恰恰可以實現這個小功能: ``` <h3>編輯班級</h3> <form (ngSubmit)="onSubmit()" [formGroup]="formGroup"> <label for="name">名稱:<input id="name" type="text" formControlName="name"/></label> <label for="teacherId">教師:<app-teacher-select *ngIf="teacher"? id="teacherId" [teacher]="teacher"></app-teacher-select></label> <button>更新</button> </form> ``` * ?當teacher存在時`if (teacher)`,渲染該組件 #### 測試 ![](https://img.kancloud.cn/27/62/2762a482e7c83eabb862baa5767731ef_579x343.gif) 有了`ngIf`的存在,當初始化時teacher為undefined時,該組件就不會渲染了。而當teacher有值時才會渲染該組件,此時傳入選擇教師組件的teacher必然不是undefined,當然也就成功的規避了上述錯誤。 ## 單元測試 # 參考文檔 | 名稱 | 鏈接 | 預計學習時長(分) | | --- | --- | --- | | 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.5.5](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.5.5) | - | | 通過輸入型綁定把數據從父組件傳到子組件 | [https://www.angular.cn/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding](https://www.angular.cn/guide/component-interaction#pass-data-from-parent-to-child-with-input-binding) | 10 | | SelectControlValueAccessor | [https://www.angular.cn/api/forms/SelectControlValueAccessor](https://www.angular.cn/api/forms/SelectControlValueAccessor) | 15 |
                  <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>

                              哎呀哎呀视频在线观看