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

                按ER圖首先生成基礎的教師實體以備用:使用編輯器打開前臺,并在shell中來到src/app/norm/entity文件夾,使用`ng g class course`生成課程實體。 ```javascript panjiedeMac-Pro:entity panjie$ ng g class course CREATE src/app/norm/entity/course.spec.ts (154 bytes) CREATE src/app/norm/entity/course.ts (24 bytes) ``` 參考ER圖完成字段及構造函數初始化工作: ```javascript import {Teacher} from './Teacher'; /** * 課程 */ export class Course { id: number; name: string; teacher: Teacher; constructor(data?: { id?: number, name?: string, teacher?: Teacher }) { if (data) { if (data.id !== undefined) { this.id = data.id; } if (data.name !== undefined) { this.name = data.name; } if (this.teacher) { this.teacher = data.teacher; } } } } ``` # 組件初始化 本組件為course模塊的第一個組件,在新建組件前進行模塊的初始化,然后在course模塊下完成本組件的初始化。 使用shell進行src/app文件夾,使用命令生成一個帶有路由模塊的Course模塊。 ``` panjiedeMac-Pro:app panjie$ ng g m course --routing CREATE src/app/course/course-routing.module.ts (250 bytes) CREATE src/app/course/course.module.ts (280 bytes) ``` 進入自動生成的course文件夾,進一步生成add組件: ``` panjiedeMac-Pro:app panjie$ cd course/ panjiedeMac-Pro:course panjie$ ng g c add CREATE src/app/course/add/add.component.sass (0 bytes) CREATE src/app/course/add/add.component.html (18 bytes) CREATE src/app/course/add/add.component.spec.ts (607 bytes) CREATE src/app/course/add/add.component.ts (258 bytes) UPDATE src/app/course/course.module.ts (344 bytes) ``` ## V層 src/app/course/add/add.component.html ```html <div class="row justify-content-center"> <div class="col-4"> <h2>添加課程</h2> <form (ngSubmit)="onSubmit()" [formGroup]="formGroup"> <div class="form-group"> <label for="name">名稱</label> <input type="text" class="form-control" id="name" placeholder="名稱" formControlName="name"> </div> <div class="form-group"> <label>任課教師</label> <app-teacher-select (selected)="course.teacher"></app-teacher-select> </div> <div class="form-group"> <label>綁定班級</label> <div> <label><input type="checkbox"> 班級1</label> <label><input type="checkbox"> 班級2</label> </div> </div> <div class="form-group"> <button>保存</button> </div> </form> </div> </div> ``` ## C層 src/app/course/add/add.component.ts ```typescript import { Component, OnInit } from '@angular/core'; import {FormGroup} from '@angular/forms'; import {Course} from '../../norm/entity/course'; @Component({ selector: 'app-add', templateUrl: './add.component.html', styleUrls: ['./add.component.sass'] }) export class AddComponent implements OnInit { formGroup: FormGroup; course: Course; constructor() { } ngOnInit() { } onSubmit() { } } ``` ## 單元測試 引用ReactiveFormsModule。 src/app/course/add/add.component.spec.ts ```typescript beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [AddComponent], imports: [ ReactiveFormsModule ] }) .compileComponents(); })); ``` ![](https://img.kancloud.cn/c3/38/c338185eaa131bad1102b5a6f2739e12_688x96.png) 解析模板中的app-teacher-select已經學習過了三種方法: 1. 在測試模塊中直接引入`AppTeacherSelect`組件。 2. 新建一個`selector`同為`app-teacher-select`的新組件做為AppTeacherSelect組件的替身,并在測試模塊中引入該替身組件。 3. 在`TestModule`新建`selector`同為`app-teacher-select`的`AppTeacherSelect`組件,做為原`StudentModule`中的`AppTeacherSelect`組件的替身。 從筆者的實際使用經驗來看,第3種方案雖然會面臨一定的組件`selector`沖突風險,但仍然不失為當前的"最佳實踐"。 ![](https://img.kancloud.cn/40/45/404585830ced4878f4ad100539f8d680_844x666.png) 打開shell并來到項目src/app/test/component文件夾,使用如下命令建立測試專用的同名`AppTeacherSelect`組件。 ``` panjiedeMac-Pro:component panjie$ ng g c TeacherSelect CREATE src/app/test/component/teacher-select/teacher-select.component.sass (0 bytes) CREATE src/app/test/component/teacher-select/teacher-select.component.html (29 bytes) CREATE src/app/test/component/teacher-select/teacher-select.component.spec.ts (678 bytes) CREATE src/app/test/component/teacher-select/teacher-select.component.ts (301 bytes) UPDATE src/app/test/test.module.ts (633 bytes) ``` 并將此組件聲明到`TestModule`中的`exports`以將其作為輸出項向其它模塊輸出。 src/app/test/test.module.ts ```typescript exports: [ LoginComponent, TeacherSelectComponent ? ], ``` * ? 輸出(暴露)組件 測試結果: ![](https://img.kancloud.cn/5b/c2/5bc25744aa4b82b89f0f5b9f60127d39_535x240.png) 提示說需要一個FormGroup實例(這說明在C層沒有對formGroup進行實例化),同時于報錯信息中給出了解決方案,沒有比這種報錯內容更貼心的了。 ## FormGroupBuild 前面已經學經過使用`new FormGroup`的方法實例化`FormGroup`。 src/app/course/add/add.component.ts ```typescript ngOnInit() { this.formGroup = new FormGroup({ name: new FormControl('') }); } ``` 本節展示另一個更為簡單的`FormBuilder`來實例化`FormGroup`,當表單項較多時使用`FormBuilder`能夠降低一些創建表單的復雜度,在生產項目中是一種較常用的形式。 src/app/course/add/add.component.ts ```typescript constructor(private formBuilder: FormBuilder) { } ngOnInit() { this.formGroup = new FormGroup({ ? name: new FormControl('') ? }); ? this.formGroup = this.formBuilder.group({ name: [''] }); } ``` ![](https://img.kancloud.cn/8c/98/8c98e0523635a7a3992f91a64bd10682_427x333.png) # 名稱驗證 按需求,課程的名稱不能為空,最少為2個字符。 ## C層 src/app/course/add/add.component.ts ```typescript ngOnInit() { this.formGroup = this.formBuilder.group({ name: ['', [Validators.required, Validators.minLength(2)]] }); } ``` ## V層初始化 src/app/course/add/add.component.html ```html <input type="text" class="form-control" id="name" placeholder="名稱" formControlName="name" required?> <small id="nameRequired" *ngIf="formGroup.get('name').dirty && formGroup.get('name').errors && formGroup.get('name').errors.required" class="form-text text-danger">請輸入課程名</small> ? <small id="nameMinLength" *ngIf="formGroup.get('name').errors && formGroup.get('name').errors.minlength" class="form-text text-danger">課程名稱不得少于2個字符</small> ? </div> <button type="submit" [disabled]="formGroup.invalid" ?>保存</button> ``` ## 單元測試 參考單元測試代碼如下: src/app/course/add/add.component.spec.ts ```typescript import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {AddComponent} from './add.component'; import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; import {TestModule} from '../../test/test.module'; import {FormTest} from '../../testing/FormTest'; import {By} from '@angular/platform-browser'; describe('course -> AddComponent', () => { let component: AddComponent; let fixture: ComponentFixture<AddComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [AddComponent], imports: [ ReactiveFormsModule, TestModule ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(AddComponent); component = fixture.componentInstance; fixture.detectChanges(); }); fit('should create', () => { expect(component).toBeTruthy(); }); fit('requried校驗', () => { // 初次渲染頁面時,不顯示校驗信息 expect(fixture.debugElement.query(By.css('#nameRequired'))).toBeNull(); // 輸入了長度為1的名稱,顯示校驗信息 const formTest: FormTest<AddComponent> = new FormTest(fixture); formTest.setInputValue('#name', '1'); fixture.detectChanges(); expect(fixture.debugElement.query(By.css('#nameRequired'))).toBeNull(); // 刪除輸入,顯示required formTest.setInputValue('#name', ''); fixture.detectChanges(); expect(fixture.debugElement.query(By.css('#nameRequired'))).toBeDefined(); }); fit('minLength校驗', () => { // 輸入長度小于2的,顯示 const formTest: FormTest<AddComponent> = new FormTest(fixture); formTest.setInputValue('#name', '1'); expect(fixture.debugElement.query(By.css('#nameMixLength'))).toBeDefined(); }); fit('button校驗', () => { // 初始化時,不能點擊 let button: HTMLButtonElement = fixture.debugElement.query(By.css('button[type="submit"]')).nativeElement; expect(button.disabled).toBeTruthy(); // 輸入合格的內容后可點擊 component.formGroup.get('name').setValue('1234'); fixture.detectChanges(); button = fixture.debugElement.query(By.css('button[type="submit"]')).nativeElement; expect(button.disabled).toBeFalsy(); }); }); ``` ## 保存按鈕點擊測試 由于本組件的表單啟用了字段合規性校驗功能,所以在進行保存按鈕測試時需要表單的值是有效的。 src/app/course/add/add.component.spec.ts ```typescript fit('點擊保存按鈕', () => { component.formGroup.get('name').setValue('1234'); fixture.detectChanges(); spyOn(component, 'onSubmit'); FormTest.clickButton(fixture, 'button[type="submit"]'); expect(component.onSubmit).toHaveBeenCalled(); }); ``` ![](https://img.kancloud.cn/84/80/8480c73ef07df5dbede66c73d72df9bf_193x97.png) # 參考文檔 | 名稱 | 鏈接 | 預計學習時長(分) | | --- | --- | --- | | 源碼地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step6.1.1](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step6.1.1) | - |
                  <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>

                              哎呀哎呀视频在线观看