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

                屬性型指令的運用需要有一些HTML DOM知識,本節我們以loading指令為例拋磚引玉,初步認識一下屬性型指令。 在開始之前我們先為保存按鈕引入個保存圖標: ```html +++ b/first-app/src/app/student/add/add.component.html @@ -47,7 +47,8 @@ </div> <div class="mb-3 row"> <div class="col-sm-10 offset-2"> - <button class="btn btn-primary" [disabled]="formGroup.invalid || formGroup.pending">保存 + <button class="btn btn-primary" [disabled]="formGroup.invalid || formGroup.pending"> + <i class="fa fa-save"></i>保存 </button> </div> </div> ``` 此時將所有的字段都符合要求時,將如下顯示保存按鈕: ![image-20210414101547572](https://img.kancloud.cn/c9/0e/c90ee9c91ef995c0da8f7b4ffb646fae_1152x224.png) 現在我們想實現一個加載中效果,即:當后臺發起請求時,保存的這個小圖標變成一個loading動畫: ```html <button class="btn btn-primary" [disabled]="formGroup.invalid || formGroup.pending"> - <i class="fa fa-save"></i>保存 + <i class="fas fa-cog fa-spin"></i>保存 </button> ``` ![image-20210414101907128](https://img.kancloud.cn/95/8a/958ac82d3fb9d4f1c4d98521076ca12f_1448x198.png) 同時,為保存按鈕為添加`disable`屬性,使其不能再次被點擊: ![image-20210414102036368](https://img.kancloud.cn/4b/ad/4bad3fd8cb4751b1e817b6fc0465c1d9_1364x218.png) 好的,預實現的效果有了,讓我們恢復一下V層,最后保存按鈕相關的`html`如下: ```html <div class="mb-3 row"> <div class="col-sm-10 offset-2"> <button class="btn btn-primary" [disabled]="formGroup.invalid || formGroup.pending"> <i class="fa fa-save"></i>保存 </button> </div> </div> ``` ## 指令初始化 前面我們多次提過模塊的三個元素:組件、指令和管道,所以按模塊化的思想,建立loading指令前需要先建立一個loading模塊。同時為了更好找,我們在根目錄下建立一個`directive`目錄專門來存放公用指令: ```bash panjie@panjies-iMac app % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app panjie@panjies-iMac app % mkdir directive panjie@panjies-iMac app % cd directive panjie@panjies-iMac directive % ``` 然后在該目錄下建立`loading`模塊: ```bash panjie@panjies-iMac directive % ng g m loading CREATE src/app/directive/loading/loading.module.ts (193 bytes) ``` 接著進入`loading`文件夾,使用`ng g directive loading`創建loading指令,該指令將自動加入到directive模塊中: ```bash panjie@panjies-iMac directive % cd loading panjie@panjies-iMac loading % ng g directive loading CREATE src/app/directive/loading/loading.directive.spec.ts (228 bytes) CREATE src/app/directive/loading/loading.directive.ts (143 bytes) UPDATE src/app/directive/loading/loading.module.ts (259 bytes) ``` 最終建立的文件如下: ```bash panjie@panjies-iMac app % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app panjie@panjies-iMac app % tree directive directive └── loading ├── loading.directive.spec.ts ├── loading.directive.ts └── loading.module.ts 1 directory, 3 files ``` 如果將上述指令添加到當前的學生增加組件中,則每次測試的時候都需要依次填充學生增加組件中的必填字段,填寫合法的手機號等,這明顯是一份重復的勞動。做為懶人的我們,怎么能允許這種事情發生呢? 為此,我們在指令的單元測試中新建一個測試專用組件,然后在這個測試專用組件中引入loading指令,以協助我們完成指令的開發工作: ```typescript +++ b/first-app/src/app/directive/loading/loading.directive.spec.ts import {LoadingDirective} from './loading.directive'; import {Component} from '@angular/core'; import {FormGroup, ReactiveFormsModule} from '@angular/forms'; import {ComponentFixture, TestBed} from '@angular/core/testing'; @Component({ template: ` <form [formGroup]="formGroup" (ngSubmit)="onSubmit()"> <button class="btn btn-primary" appLoading><i class="fa fa-save"></i>保存</button> </form> ` }) class TestComponent { formGroup = new FormGroup({}); onSubmit(): void { console.log('submit'); } } describe('LoadingDirective', () => { let component: TestComponent; let fixture: ComponentFixture<TestComponent>; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [TestComponent, LoadingDirective], imports: [ReactiveFormsModule] }).compileComponents(); fixture = TestBed.createComponent(TestComponent); component = fixture.componentInstance; fixture.detectChanges(); }); fit('should create an instance', () => { expect(component).toBeTruthy(); }); }); ``` 沒錯,正如你看的一樣,我們完全在使用測試組件代碼來輔助我們進行指令開發。啟用單元測試,效果如下: ![image-20210414105319307](https://img.kancloud.cn/e9/d2/e9d23152c9352a278b622683a1c8de59_718x174.png) ## 使用指令 屬性性指令可以輕構的加入到組件上: ```html <form [formGroup]="formGroup" (ngSubmit)="onSubmit()"> - <button class="btn btn-primary"><i class="fa fa-save"></i>保存</button> + <button class="btn btn-primary" ??appLoading><i class="fa fa-save"></i>保存</button> </form> ``` ?? 關鍵字被聲明在指令對應的`selector`上 ```typescript import {Directive} from '@angular/core'; @Directive({ selector: '[??appLoading]' }) export class LoadingDirective { constructor() { } } ``` ## 宿主元素 由于`appLoading`指令必須依賴于某個元素才能正常工作,所以我們把它依賴的元素稱為其宿主元素。比如上述代碼中`button`元素即為指令`appLoading`的宿主元素。 指令若要在`button`點擊時改變`button`中的圖標,則需要首先在指令中獲取這個`button`元素。在指令中,可以非常輕松的獲取到指令的宿主元素: ```typescript +++ b/first-app/src/app/directive/loading/loading.directive.ts @@ -1,11 +1,12 @@ -import {Directive} from '@angular/core'; +import {Directive, ElementRef} from '@angular/core'; @Directive({ selector: '[appLoading]' }) export class LoadingDirective { - constructor() { + constructor(private elementRef: ElementRef) { + console.log(elementRef); } } ``` 控制臺信息如下: ![image-20210414111710023](https://img.kancloud.cn/4a/88/4a880f0df7c35b3cb9f48c75163e926e_1424x108.png) 控制臺中信息顯示在指令中注入的`ElementRef`有個`nativeElement`屬性,該屬性即是宿主元素`button`對應的DOM對象。有了這個宿主元素后,我們大概需要做如下幾件事: - 獲取宿主元素的點擊事件,也宿主元素被點擊時,能夠執行我們設定的方法。 - 點宿主被點擊時,隱藏其內置的圖標。 - 點宿主被點擊時,向其內添加一個loading圖標。 - 當宿主被點擊時,設置宿主的disabled屬性。 下面我們們依次完成上述幾個功能。 ## 監聽點擊 Html DOM全稱為Html Document Object Modle,簡單來說就是把Html中的每個元素都可以看做對象來處理。 比如我們獲取當前Button對象: ```typescript constructor(private elementRef: ElementRef) { console.log(elementRef); const htmlButtonElement = elementRef.nativeElement as HTMLButtonElement①; console.log(htmlButtonElement); ``` - ① 使用用`as`將其聲明為`HTMLButtonElement`的好處是可以在后面的代碼中快速使用`HTMLButtonElement`中的屬性與方法;壞處是該指令日后可能僅能夠應用到`Button`類型的宿主元素上。 ![image-20210414121125049](https://img.kancloud.cn/1a/5e/1a5ef6d2dc862b08d5492629cb60757d_994x92.png) Html DOM中的`addEventListener()`可以方便的設置事件的監聽,比如我們在此想監聽按鈕被點擊的事件: ```typescript constructor(private elementRef: ElementRef) { console.log(elementRef); const htmlButtonElement = elementRef.nativeElement as HTMLButtonElement; console.log(htmlButtonElement); htmlButtonElement.addEventListener('click', () => { console.log('宿主被點擊'); }); } ``` 此時,點按鈕被點擊時,則會觸發相應的回調方法: ![image-20210414121303204](https://img.kancloud.cn/41/10/411002a12a1aa93ca0cc6eb7dd85bb45_1366x178.png) 除此以外`addEventListener`還支持監聽其它的類型,有興趣的同學可以通過點擊本小節最后的資源列表查看學習。 ## 隱藏內置圖標 隱藏內置圖標前,需要找到這個要隱藏的圖標。初始比較簡單的方法是借助瀏覽器的檢查功能。 我們當前項目引入了fontawesome圖標庫,該圖標庫把`<i class="fa fa-save"></i>`類似的代碼最終轉換成了`svg`失量圖顯示,所以最終使用瀏覽器在檢查圖標時,得到的是`svg`代碼。這也是在前面的章節中我們定義圖標與文字間的距離時為什么要使用`.btn > svg.svg-inline--fa `的原因。 ![image-20210414133818309](https://img.kancloud.cn/85/90/85900109bdc04cd22d4d13622589096b_2342x496.png) 此時我們需要做的便是獲取到這個`svg`元素,然后將它隱藏掉。 ```typescript +++ b/first-app/src/app/directive/loading/loading.directive.ts @@ -11,6 +11,8 @@ export class LoadingDirective { console.log(htmlButtonElement); htmlButtonElement.addEventListener('click', () => { console.log('宿主被點擊'); + const svgElement = htmlButtonElement.querySelector('svg') as SVGElement; + console.log(svgElement); }); } ``` ![image-20210414134208253](https://img.kancloud.cn/81/e9/81e95feb1af8e0cf82aa70651fbdd6f7_2770x214.png) 再然后隱藏該元素就很簡單了,為該元素添加一個`display: none`的樣式即可: ```typescript +++ b/first-app/src/app/directive/loading/loading.directive.ts @@ -13,6 +13,7 @@ export class LoadingDirective { console.log('宿主被點擊'); const svgElement = htmlButtonElement.querySelector('svg') as SVGElement; console.log(svgElement); + svgElement.style.display = 'none'; }); } ``` 此時button按鈕并點擊時,小圖標就不見了。 ![image-20210414134444312](https://img.kancloud.cn/31/be/31be82777e904c19d40ed8458ac3bc0c_1222x562.png) ## 追加loading圖標 接下來再追加一個loading的圖標。想追加一個loading圖標進行,首先我們需要有一個loading圖標。 ```typescript +++ b/first-app/src/app/directive/loading/loading.directive.ts @@ -15,7 +15,7 @@ export class LoadingDirective { console.log(svgElement); svgElement.style.display = 'none'; - + const loadingElement = document.createElement('i') as HTMLElement; }); ``` 然后為這個創建的元素添加3個class值: ```typescript const loadingElement = document.createElement('i') as HTMLElement; + loadingElement.classList.add('fas'); + loadingElement.classList.add('fa-cog'); + loadingElement.classList.add('fa-spin'); ``` 這樣一來,我們便使用代碼構造了一個這樣的html元素: `<i class="fas fa-cog fa-spin"></i>`,而該元素將被fontawesome處理成動起來的小齒輪。 最后,讓我們把這個動起來的小齒輪添加到原來已經隱藏掉的保存按鈕之前: ```typescript const loadingElement = document.createElement('i') as HTMLElement; loadingElement.classList.add('fas'); loadingElement.classList.add('fa-cog'); loadingElement.classList.add('fa-spin'); + htmlButtonElement.insertBefore(loadingElement, svgElement); }); ``` 此時當我們當擊保存按鈕時,動態的小齒輪便會替換原來的保存圖標了。 ![image-20210414140055143](https://img.kancloud.cn/1e/60/1e60757dfe416a23b40c11d66a251feb_490x158.png) ## disabled 有了前面的經驗,相信設置disabled屬性應該難不到你了吧。 ```typescript +++ b/first-app/src/app/directive/loading/loading.directive.ts @@ -20,6 +20,8 @@ export class LoadingDirective { loadingElement.classList.add('fa-cog'); loadingElement.classList.add('fa-spin'); htmlButtonElement.insertBefore(loadingElement, svgElement); + + htmlButtonElement.disabled = true; }); } ``` ![image-20210414140213686](https://img.kancloud.cn/d8/ee/d8eeee02332d5e8ccf1298c0527879eb_740x154.png) 此時當我們當擊按鈕時,按鈕前的保存圖標將變更為一個動態的小齒輪,同時按鈕變更為不可用狀態。 ## 重構 功能完成了只是開始,最終還需要一個看起來過得去,更容易修正的代碼。 ```typescript import {Directive, ElementRef} from '@angular/core'; @Directive({ selector: '[appLoading]' }) export class LoadingDirective { constructor(private elementRef: ElementRef) { const htmlButtonElement = elementRef.nativeElement as HTMLButtonElement; htmlButtonElement.addEventListener('click', () => { this.buttonOnClick(htmlButtonElement); }); } buttonOnClick(htmlButtonElement: HTMLButtonElement): void { // 隱藏原圖標 const svgElement = htmlButtonElement.querySelector('svg') as SVGElement; svgElement.style.display = 'none'; // 生成小齒輪,并添加到button中 const loadingElement = document.createElement('i') as HTMLElement; loadingElement.classList.add('fas'); loadingElement.classList.add('fa-cog'); loadingElement.classList.add('fa-spin'); htmlButtonElement.insertBefore(loadingElement, svgElement); // 禁用按鈕 htmlButtonElement.disabled = true; } } ``` 最終指令代碼如上。 ## export 前面我們學習過,默認情況下組件、指令和管道都是模塊的私有元素。它們若想在模塊外被使用,則需要將其在模塊中拋出: ```typescript +++ b/first-app/src/app/directive/loading/loading.module.ts @@ -6,6 +6,9 @@ import {LoadingDirective} from './loading.directive'; declarations: [LoadingDirective], imports: [ CommonModule + ], + exports: [ + LoadingDirective ] }) export class LoadingModule { ``` 此時,若其它組件想使用Loading指令,則只需要在自己所在的模塊中引入LoadingModule即可。 ## 使用 指令完成后,我們嘗試將其用在學生新增組件中。 第一步,在動態測試模塊中引入Loading模塊: ```typescript +++ b/first-app/src/app/student/add/add.component.spec.ts beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [AddComponent], imports: [ ReactiveFormsModule, ClazzSelectModule, MockApiTestingModule, ?? LoadingModule ] }) .compileComponents(); }); ``` 第二步,在組件的V層中使用loading指令: ```html +++ b/first-app/src/app/student/add/add.component.html @@ -47,7 +47,7 @@ </div> <div class="mb-3 row"> <div class="col-sm-10 offset-2"> - <button class="btn btn-primary" [disabled]="formGroup.invalid || formGroup.pending"> + <button appLoading class="btn btn-primary" [disabled]="formGroup.invalid || formGroup.pending"> <i class="fa fa-save"></i>保存 </button> </div> ``` 測試效果: ![image-20210414102036368](https://img.kancloud.cn/4b/ad/4bad3fd8cb4751b1e817b6fc0465c1d9_1364x218.png) ## 本節作業 完成新增組件中的`onSubmit`方法。 | 名稱 | 鏈接 | | ----------------- | ------------------------------------------------------------ | | HTMLElement | [https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) | | HTMLButtonElement | [https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement) | | 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step7.2.5.zip](https://github.com/mengyunzhi/angular11-guild/archive/step7.2.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>

                              哎呀哎呀视频在线观看