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

                在前面的章節中,我們學習父子組件傳值的兩種方法。當前的需求是將請求發生401的錯誤信息及時地通知`Index`組件。而`UnAuthInterceptor`本身就不是個組件,所以前面學習過的組件傳值的方法并不適用于當前需求。 在Angular中如果需要將值在不同的單元(組件、指令、過濾器、攔截器等)傳遞,應該使用單例模式的Service。 我們當前大概在實現這么一張圖: ![image-20210409175249812](https://img.kancloud.cn/27/da/27da648a7f3fe6c283a73c101596bdd3_1770x248.png) ## 單例模式 單例模式是繼觀察者模式、依賴注入模式后我們學習的又一新的模式。簡單來說如果某個單元是單例的,則表式在整個應用,這個單元至多存在一個。 程序中的單例,往往具有**共享**的特性,比如同班同時共享著一個C語言老師,所以對于該班同學而言,C語言老師是單例的;程序中的單例,在同一時間僅能服務一個對象,比如當張三問C語言老師問題時,李四則只能等待;程序中的單例,將被所有的對象共享狀態,比如張三惹老師生氣后,老師在與李四接觸時,還保持著生氣的狀態。 單例模式被豐富地應用于各種框架中,其最大的優點就是節約資源。比如我們被一個班級配備一個班主任,那個這個班主任就是在節約資源下的單例(試想下為每位同學配備一位班主任將是怎么的情景)。 在Angular中,有一個單元被稱為Service,該Serivce在整個應用中便是單例的。它符合單例模式的所有特性: - 有且至多有一個 - 被其它單元共享 - 共享其狀態 - 節約資源 ## 初始化 初始化Service與初始化其它的Angular單元相同,同樣是使用`ng g`命令,為此我們在`src/app`文件夾中新建service文件夾,并初始化一個`AuthService`: ```bash panjie@panjies-iMac app % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app panjie@panjies-iMac app % mkdir service panjie@panjies-iMac app % cd service panjie@panjies-iMac service % pwd /Users/panjie/github/mengyunzhi/angular11-guild/first-app/src/app/service panjie@panjies-iMac service % ng g s auth CREATE src/app/service/auth.service.spec.ts (347 bytes) CREATE src/app/service/auth.service.ts (133 bytes) ``` 如此我們便快速的創建第一個service: ```typescript import {Injectable} from '@angular/core'; @Injectable({ providedIn: 'root' }) export class AuthService { constructor() { } } ``` ## 注入Service 有了service,便可以向注入HttpClient一樣,將其注入到任意我們想注入的位置了,比如當前想交其注入到`UnAuthInterceptor`中: ```typescript +++ b/first-app/src/app/un-auth.interceptor.ts @@ -7,11 +7,13 @@ import { } from '@angular/common/http'; import {Observable, throwError} from 'rxjs'; import {catchError} from 'rxjs/operators'; +import {AuthService} from './service/auth.service'; @Injectable() export class UnAuthInterceptor implements HttpInterceptor { - constructor() { + constructor(private authService: AuthService) { + console.log('authService注入成功', authService); } intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { ``` 在控制臺中成功的打印了注入的`AuthService` ![image-20210409173247874](https://img.kancloud.cn/55/34/553449de94d3f93e8975528de1347bf0_990x78.png) 此時回想一下注入`HttpClient`的過程,上述注入是不是顯得過于簡單了? 同樣,在組件中也可以非常輕松的注入服務: ```typescript +++ b/first-app/src/app/index/index.component.ts @@ -1,5 +1,6 @@ import {Component, OnInit} from '@angular/core'; import {Teacher} from '../entity/teacher'; +import {AuthService} from '../service/auth.service'; @Component({ selector: 'app-index', @@ -10,7 +11,8 @@ export class IndexComponent implements OnInit { login = false; - constructor() { + constructor(private authService: AuthService) { + console.log('index組件成功注入authService', authService); } ngOnInit(): void { ``` ![image-20210409175755650](https://img.kancloud.cn/36/b1/36b1faa4ef52d14715c420ba0b0b96d2_1106x82.png) 當前服務與組件的配合情況如下圖所示: ![image-20210409175617703](https://img.kancloud.cn/3e/02/3e0284bea41997f106638c4e6feff8c4_1748x362.png) 為了證明攔截器與組件中的`AuthService`的確是一個,我們在`AuthService`中增加一個屬性,并在構造函數中使用隨機值來設置該屬性: ```typescript +++ b/first-app/src/app/service/auth.service.ts @@ -4,7 +4,8 @@ import {Injectable} from '@angular/core'; providedIn: 'root' }) export class AuthService { - + private key; constructor() { + this.key = Math.random(); } } ``` 查看控制臺: ![image-20210409180228511](https://img.kancloud.cn/41/ca/41caaa6de90b0c3db4d83734d218a5d5_1800x444.png) 構造函數僅被調用1次,在組件及攔截器中打印的`key`值是相同的,這充分的說明了攔截器及組件中的AuthService的確就是一個。 ## 再談DI 為什么就這么簡單的注入成功了呢?再徹底的弄明白這個問題,還需要復習一下這張圖: ![image-20210228173649880](https://img.kancloud.cn/03/03/03032b8d25dc3ab8bf395f7c3c1dc124_2126x656.png) 上圖展現了`App組件`成功注入`HttpClient`的過程。之所以注入成功,是由于當前模塊的`imports`中引入了`HttpClientModule` ,而`HttpClientModule`有提供`HttpClient`的能力。 而當前代碼并未為任何模塊聲明任何能力,為何能注入成功呢?這是由于Angular有個叫做`root`的根模塊,而所有的模塊原則上都屬于根模塊的子模塊,所以所有的子模塊都可以無條件的使用`root`根模塊上的資源: ![image-20210409180550615](https://img.kancloud.cn/ec/b1/ecb134829c80e95e8b65b862ec9e168c_1864x392.png) AuthService之所有能夠成為`root`根模塊可以提供的資源,本質上是由以下代碼決定的: ```typescript import {Injectable} from '@angular/core'; @Injectable①({ providedIn②: 'root' ?? }) export class AuthService { constructor() { } } ``` - ① 表示該服務可被用于注入(本服務是個資源) - ②`providedIn`標識了可被用于注入的范圍 - `root`表示根模塊。由于所有的模塊都屬于根模塊,所以`root`也可理解為全部范圍。即在當前應用的任意位置上均可注入當前服務 ## 觀察者模式 服務被直接的注入到兩個單元之內后,如何實現:攔截器發送的通知能被組件及時獲取呢?我想如果當前三者的關系如下,肯定難不倒你: ![image-20210409180925060](https://img.kancloud.cn/c7/d2/c7d26fcdf360ee0580e03ae4f2ffdcb5_922x368.png) 此時只需要在攔截器調用AuthService的相關方法,然后在AuthService中再調用IndexComponent的相關方法即可。但當前三者的關系卻如下圖: ![image-20210409175617703](https://img.kancloud.cn/3e/02/3e0284bea41997f106638c4e6feff8c4_1748x362.png) 這時候就需要自定義一個觀察者了,我們將上面各個單元簡單換個名字,相信你此時應該明了接下來需要做什么了。 ![image-20210409181215128](https://img.kancloud.cn/a3/c8/a3c8fee9f323ebe1828c151d6783c99d_1690x356.png) 沒錯,我們需要將AuthService打造成一個大V,然后使用粉絲一關注這個大V。此時公關公司便可以通過大V向粉絲發送數據了。 ### 打造大V 打造在V的方式主要有兩種,我們在此使用較簡單的。 ```typescript +++ b/first-app/src/app/service/auth.service.ts @@ -1,10 +1,17 @@ import {Injectable} from '@angular/core'; +import {Subject} from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AuthService { private key; + /** + * Subject是個大V. + * 本大V只需要發送一個未認證的通知,并不需要傳遞具體數據,所以泛型為void + */ + public unAuthSubject = new Subject<void>(); ?? + constructor() { console.log('AuthService構造函數被調用'); this.key = Math.random(); ``` - `Subject`是個數據源,該數據源可供訂閱,同時也提供了發送新數據的方法。 在了大V以后,開始訂閱大V。 ### 訂閱大V 在`Index`組件中訂閱大V,當接收到未認證的通知時,如果當前未顯示登錄界面,則顯示登錄界面。 ```typescript +++ b/first-app/src/app/index/index.component.ts @@ -20,6 +20,13 @@ export class IndexComponent implements OnInit { if (window.sessionStorage.getItem('login') !== null) { this.login = true; } + this.authService.unAuthSubject + .subscribe(() => { + console.log('接收到未認證的通知'); + if (this.login) { + this.login = false; + } + }); } ``` ### 通知大V 此時在攔截器發現401時,及時的通知在V,則可以在`Index`組件中獲取到一個通知: ```typescript +++ b/first-app/src/app/un-auth.interceptor.ts @@ -21,6 +21,7 @@ export class UnAuthInterceptor implements HttpInterceptor { .pipe(catchError(error => { if (error.status === 401) { console.log('發生了401錯誤, 通知應用顯示登錄界面', error); + this.authService.unAuthSubject.next(); } // 使用throwError()繼續向上拋出異常 return throwError(error); ``` 此時在模擬半小時后被后臺主動注銷的情況下刷新頁面,則可以在控制臺查看到如下信息: ![image-20210409182827213](https://img.kancloud.cn/4b/4a/4b4ac527f99bbce48d182ddd088287cb_1668x184.png) 同時應用跳轉到登錄界面: ![image-20210305121257833](https://img.kancloud.cn/0a/7b/0a7b7f1208bab18e46c08a1e5692296d_1416x542.png) 至此,一個單例的AuthService便可以的起到了`UnAuthInterceptor`與`IndexComponent`的信息橋梁的作用。以后有類似的需求時,當然也可以通過此模式來實現了。 | 名稱 | 鏈接 | | ------------------ | ------------------------------------------------------------ | | 創建可注入的服務類 | [https://angular.cn/guide/dependency-injection#create-an-injectable-service-class](https://angular.cn/guide/dependency-injection#create-an-injectable-service-class) | | RxJS Subject | [https://cn.rx.js.org/manual/overview.html#h15](https://cn.rx.js.org/manual/overview.html#h15) | | 本節源碼 | [https://github.com/mengyunzhi/angular11-guild/archive/step6.7.2.zip](https://github.com/mengyunzhi/angular11-guild/archive/step6.7.2.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>

                              哎呀哎呀视频在线观看