共用方式為


使用 Azure Active Directory B2C 在您自己的 Angular 應用程式中啟用驗證

這很重要

自 2025 年 5 月 1 日起,Azure AD B2C 將不再可供新客戶購買。 在我們的常見問題中深入瞭解

本文說明如何將 Azure Active Directory B2C (Azure AD B2C) 驗證新增至您自己的 Angular 單頁應用程式 (SPA)。 瞭解如何整合 Angular 應用程式與 MSAL for Angular 驗證連結庫。

使用這篇文章與標題為 在 Angular 單頁範例應用程式中設定驗證 的相關文章。 以您自己的 Angular 應用程式取代範例 Angular 應用程式。 完成本文中的步驟之後,您的應用程式會透過 Azure AD B2C 接受登入。

先決條件

完成在 Angular 單頁應用程式範例 中設定驗證一文中的步驟。

建立 Angular 應用程式專案

您可以使用現有的 Angular 應用程式專案,或建立新的專案。 若要建立新的專案,請執行下列命令。

命令:

  1. 使用 npm 套件管理員安裝 Angular CLI
  2. 使用路由模組建立 Angular 工作區。 應用程式名稱稱為 msal-angular-tutorial。 您可以變更為任何有效的 Angular 應用程式名稱, 例如 contoso-car-service
  3. 移至應用程式目錄資料夾。
npm install -g @angular/cli 
ng new msal-angular-tutorial --routing=true --style=css --strict=false
cd msal-angular-tutorial

安裝相依性

若要在應用程式中安裝 MSAL BrowserMSAL Angular 連結庫,請在命令殼層中執行下列命令:

npm install @azure/msal-browser @azure/msal-angular

安裝 Angular Material 元件庫 (選擇性,適用於 UI):

npm install @angular/material @angular/cdk

新增驗證元件

範例程式代碼包含下列元件:

元件 類型 說明
auth-config.ts 常數 此組態檔包含 Azure AD B2C 識別提供者和 Web API 服務的相關信息。 Angular 應用程式會使用此資訊來建立與 Azure AD B2C 的信任關係、登入和註銷使用者、取得令牌,以及驗證令牌。
app.module.ts Angular 模組 此元件描述應用程式元件如何配合在一起。 這是用來啟動程式並開啟應用程式的根模組。 在本逐步解說中,您會將某些元件新增至 app.module.ts 模組,並使用 MSAL 組態對象啟動 MSAL 連結庫。
app-routing.module.ts Angular 路由模組 此元件可藉由解譯瀏覽器 URL 並載入對應的元件來啟用流覽。 在本逐步解說中,您會將一些元件新增至路由模組,並使用 MSAL Guard 來保護元件。 只有授權的使用者才能存取受保護的元件。
app.component.* Angular 元件 命令 ng new 會建立具有根元件的 Angular 專案。 在本演練中,您會將 應用程式 元件變更為用來承載頂端導覽列。 導覽列包含各種按鈕,包括登入和註銷按鈕。 類別 app.component.ts 會處理登入和註銷事件。
home.component.* Angular 元件 在本逐步解說中,您會新增 home 元件來渲染首頁以進行匿名存取。 此元件示範如何檢查使用者是否已登入。
profile.component.* Angular 元件 在此逐步解說中,您會新增 profile 元件,以了解如何讀取識別碼權杖宣告。
webapi.component.* Angular 元件 在本逐步解說中,您會新增 webapi 元件,以瞭解如何呼叫 Web API。

若要將下列元件新增至您的應用程式,請執行下列 Angular CLI 命令。 generate component命令:

  1. 為每個元件建立資料夾。 資料夾包含 TypeScript、HTML、CSS 和測試檔案。
  2. 更新 app.module.tsapp-routing.module.ts 檔案,以包含對新元件的參考。
ng generate component home
ng generate component profile
ng generate component webapi

新增應用程式設定

Azure AD B2C 識別提供者和 Web API 的設定會儲存在 auth-config.ts 檔案中。 在您的 src/app 資料夾中,建立名為 auth-config.ts 的檔案,其中包含下列程式代碼。 然後,如 3.1 設定 Angular 範例中所述變更設定。

import { LogLevel, Configuration, BrowserCacheLocation } from '@azure/msal-browser';

const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1;
 
export const b2cPolicies = {
     names: {
         signUpSignIn: "b2c_1_susi_reset_v2",
         editProfile: "b2c_1_edit_profile_v2"
     },
     authorities: {
         signUpSignIn: {
             authority: "https://your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/b2c_1_susi_reset_v2",
         },
         editProfile: {
             authority: "https://your-tenant-name.b2clogin.com/your-tenant-name.onmicrosoft.com/b2c_1_edit_profile_v2"
         }
     },
     authorityDomain: "your-tenant-name.b2clogin.com"
 };
 
 
export const msalConfig: Configuration = {
     auth: {
         clientId: '<your-MyApp-application-ID>',
         authority: b2cPolicies.authorities.signUpSignIn.authority,
         knownAuthorities: [b2cPolicies.authorityDomain],
         redirectUri: '/', 
     },
     cache: {
         cacheLocation: BrowserCacheLocation.LocalStorage,
         storeAuthStateInCookie: isIE, 
     },
     system: {
         loggerOptions: {
            loggerCallback: (logLevel, message, containsPii) => {
                console.log(message);
             },
             logLevel: LogLevel.Verbose,
             piiLoggingEnabled: false
         }
     }
 }

export const protectedResources = {
  todoListApi: {
    endpoint: "http://localhost:5000/api/todolist",
    scopes: ["https://your-tenant-name.onmicrosoft.com/api/tasks.read"],
  },
}
export const loginRequest = {
  scopes: []
};

啟動驗證程式庫

公用用戶端應用程式不受信任,無法安全地保留應用程式秘密,因此它們沒有客戶端密碼。 在 src/app 資料夾中,開啟 app.module.ts 並進行下列變更:

  1. 匯入 MSAL Angular 和 MSAL Browser 連結庫。
  2. 匯入 Azure AD B2C 組態模組。
  3. 匯入 HttpClientModule。 HTTP 用戶端可用來呼叫 Web API。
  4. 匯入 Angular HTTP 攔截器。 MSAL 會使用攔截器將持有人令牌插入 HTTP 授權標頭。
  5. 新增必要的 Angular 材質。
  6. 使用多帳戶公用客戶端應用程式物件來具現化 MSAL。 MSAL 初始化包括傳遞:
    1. auth-config.ts的組態物件。
    2. 路由防護的組態物件。
    3. MSAL 攔截器的組態物件。 攔截器類別會自動取得傳出要求的權杖,而此權杖使用已知受保護資源的 Angular HttpClient 類別。
  7. 設定 HTTP_INTERCEPTORSMsalGuardAngular 提供者
  8. MsalRedirectComponent 新增至 Angular 啟動程序

src/app 資料夾中,編輯 app.module.ts 並進行下列代碼段所示的修改。 這些變更會標幟為「變更從這裡開始」和「變更在這裡結束」。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

/* Changes start here. */
// Import MSAL and MSAL browser libraries. 
import { MsalGuard, MsalInterceptor, MsalModule, MsalRedirectComponent } from '@azure/msal-angular';
import { InteractionType, PublicClientApplication } from '@azure/msal-browser';

// Import the Azure AD B2C configuration 
import { msalConfig, protectedResources } from './auth-config';

// Import the Angular HTTP interceptor. 
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { ProfileComponent } from './profile/profile.component';
import { HomeComponent } from './home/home.component';
import { WebapiComponent } from './webapi/webapi.component';

// Add the essential Angular materials.
import { MatButtonModule } from '@angular/material/button';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatListModule } from '@angular/material/list';
import { MatTableModule } from '@angular/material/table';
/* Changes end here. */

@NgModule({
  declarations: [
    AppComponent,
    ProfileComponent,
    HomeComponent,
    WebapiComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    /* Changes start here. */
    // Import the following Angular materials. 
    MatButtonModule,
    MatToolbarModule,
    MatListModule,
    MatTableModule,
    // Import the HTTP client. 
    HttpClientModule,

    // Initiate the MSAL library with the MSAL configuration object
    MsalModule.forRoot(new PublicClientApplication(msalConfig),
      {
        // The routing guard configuration. 
        interactionType: InteractionType.Redirect,
        authRequest: {
          scopes: protectedResources.todoListApi.scopes
        }
      },
      {
        // MSAL interceptor configuration.
        // The protected resource mapping maps your web API with the corresponding app scopes. If your code needs to call another web API, add the URI mapping here.
        interactionType: InteractionType.Redirect,
        protectedResourceMap: new Map([
          [protectedResources.todoListApi.endpoint, protectedResources.todoListApi.scopes]
        ])
      })
    /* Changes end here. */
  ],
  providers: [
    /* Changes start here. */
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true
    },
    MsalGuard
    /* Changes end here. */
  ],
  bootstrap: [
    AppComponent,
    /* Changes start here. */
    MsalRedirectComponent
    /* Changes end here. */
  ]
})
export class AppModule { }

設定路由

在本節中,設定 Angular 應用程式的路由。 當使用者選取頁面上的連結以在單頁應用程式中移動,或在網址列中輸入 URL 時,路由會將 URL 對應至 Angular 元件。 Angular 路由 canActivate 介面會使用 MSAL Guard 來檢查使用者是否已登入。 如果使用者未登入,MSAL 會將使用者帶往 Azure AD B2C 進行驗證。

src/app 資料夾中,編輯 app-routing.module.ts 進行下列代碼段中顯示的修改。 這些變更會標幟為「變更從這裡開始」和「變更在這裡結束」。

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { MsalGuard } from '@azure/msal-angular';
import { HomeComponent } from './home/home.component';
import { ProfileComponent } from './profile/profile.component';
import { WebapiComponent } from './webapi/webapi.component';

const routes: Routes = [
  /* Changes start here. */
  {
    path: 'profile',
    component: ProfileComponent,
    // The profile component is protected with MSAL Guard.
    canActivate: [MsalGuard]
  },
  {
    path: 'webapi',
    component: WebapiComponent,
    // The profile component is protected with MSAL Guard.
    canActivate: [MsalGuard]
  },
  {
    // The home component allows anonymous access
    path: '',
    component: HomeComponent
  }
  /* Changes end here. */
];


@NgModule({
  /* Changes start here. */
  // Replace the following line with the next one
  //imports: [RouterModule.forRoot(routes)],
  imports: [RouterModule.forRoot(routes, {
    initialNavigation:'enabled'
  })],
  /* Changes end here. */
  exports: [RouterModule]
})
export class AppRoutingModule { }

新增登入和註銷按鈕

在本節中,您會將登入和註銷按鈕新增至 應用程式 元件。 在 src/app 資料夾中,開啟 app.component.ts 檔案,並進行下列變更:

  1. 匯入必要的元件。

  2. 變更 類別以實作 OnInit 方法OnInit 方法會訂閱 MSAL MsalBroadcastServiceinProgress$ 可觀察事件。 使用此事件來知道用戶互動的狀態,特別是檢查是否已完成互動。

    在與 MSAL 帳戶物件互動之前,請先檢查 InteractionStatus 屬性是否傳回 InteractionStatus.None。 事件 subscribe 會呼叫 setLoginDisplay 方法來檢查使用者是否已通過驗證。

  3. 新增類別變數。

  4. 新增啟動 login 授權流程的方法。

  5. 新增可登出使用者的 logout 方法。

  6. setLoginDisplay新增方法,以檢查使用者是否已通過驗證。

  7. 新增 ngOnDestroy 方法來清除 inProgress$ 訂閱事件。

變更之後,您的程式代碼看起來應該像下列代碼段:

import { Component, OnInit, Inject } from '@angular/core';
import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
import { InteractionStatus, RedirectRequest } from '@azure/msal-browser';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

/* Changes start here. */
export class AppComponent implements OnInit{
  title = 'msal-angular-tutorial';
  loginDisplay = false;
  private readonly _destroying$ = new Subject<void>();

  constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }

  ngOnInit() {

    this.broadcastService.inProgress$
    .pipe(
      filter((status: InteractionStatus) => status === InteractionStatus.None),
      takeUntil(this._destroying$)
    )
    .subscribe(() => {
      this.setLoginDisplay();
    })
  }

  login() {
    if (this.msalGuardConfig.authRequest){
      this.authService.loginRedirect({...this.msalGuardConfig.authRequest} as RedirectRequest);
    } else {
      this.authService.loginRedirect();
    }
  }

  logout() { 
    this.authService.logoutRedirect({
      postLogoutRedirectUri: 'http://localhost:4200'
    });
  }

  setLoginDisplay() {
    this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
  }

  ngOnDestroy(): void {
    this._destroying$.next(undefined);
    this._destroying$.complete();
  }
  /* Changes end here. */
}

src/app 資料夾中,編輯 app.component.html 並進行下列變更:

  1. 新增設定檔和 Web API 元件的連結。
  2. 新增 [登入] 按鈕,並將 click 事件屬性設置為 login() 方法。 只有當loginDisplay類別變數是false時,才會顯示此按鈕。
  3. 新增 [註銷] 按鈕,並將 click 事件屬性設定為 logout() 方法。 只有當loginDisplay類別變數是true時,才會顯示此按鈕。
  4. 新增 router-outlet 元素。

變更之後,您的程式代碼看起來應該像下列代碼段:

<mat-toolbar color="primary">
  <a class="title" href="/">{{ title }}</a>

  <div class="toolbar-spacer"></div>

  <a mat-button [routerLink]="['profile']">Profile</a>
  <a mat-button [routerLink]="['webapi']">Web API</a>

  <button mat-raised-button *ngIf="!loginDisplay" (click)="login()">Login</button>
  <button mat-raised-button *ngIf="loginDisplay" (click)="logout()">Logout</button>

</mat-toolbar>
<div class="container">
  <router-outlet></router-outlet>
</div>

或者,使用下列 CSS 代碼段更新 app.component.css 檔案:

.toolbar-spacer {
    flex: 1 1 auto;
  }

  a.title {
    color: white;
  }

處理應用程式重新導向

當您使用 MSAL 進行重新導向時,必須將 應用程式重新導向 指示詞新增至 index.html。 在 src 資料夾中,編輯 index.html ,如下列代碼段所示:

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>MsalAngularTutorial</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root></app-root>
  <!-- Changes start here -->
  <app-redirect></app-redirect>
  <!-- Changes end here -->
</body>
</html>

設定應用程式 CSS (選擇性 )

/src 資料夾中,使用下列 CSS 代碼段更新 styles.css 檔案:

@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';

html, body { height: 100%; }
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
.container { margin: 1%; }

小提示

此時,您可以執行應用程式並測試登入體驗。 若要執行您的應用程式,請參閱 執行 Angular 應用程式 一節。

檢查使用者是否已通過驗證

home.component 檔案示範如何檢查使用者是否已通過驗證。 在 src/app/home 資料夾中,使用下列代碼段更新 home.component.ts

程式碼:

  1. 訂閱 MSAL MsalBroadcastServicemsalSubject$inProgress$ 可觀察事件。
  2. 確保 msalSubject$ 事件會將驗證結果寫入瀏覽器控制台。
  3. 確保 inProgress$ 事件會檢查使用者是否已通過驗證。 方法 getAllAccounts() 會傳回一或多個物件。
import { Component, OnInit } from '@angular/core';
import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser';
import { filter } from 'rxjs/operators';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  loginDisplay = false;

  constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }

  ngOnInit(): void {
    this.msalBroadcastService.msalSubject$
      .pipe(
        filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS),
      )
      .subscribe((result: EventMessage) => {
        console.log(result);
      });

    this.msalBroadcastService.inProgress$
      .pipe(
        filter((status: InteractionStatus) => status === InteractionStatus.None)
      )
      .subscribe(() => {
        this.setLoginDisplay();
      })
  }

  setLoginDisplay() {
    this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
  }
}

src/app/home 資料夾中,使用下列 HTML 代碼段更新 home.component.html*ngIf 指令會檢查loginDisplay類別變數,以顯示或隱藏歡迎訊息。

<div *ngIf="!loginDisplay">
    <p>Please sign-in to see your profile information.</p>
</div>

<div *ngIf="loginDisplay">
    <p>Login successful!</p>
    <p>Request your profile information by clicking Profile above.</p>
</div>

讀取識別碼權杖宣告

profile.component 檔案示範如何存取使用者的 ID 權杖聲明。 在 src/app/profile 資料夾中,使用下列代碼段更新 profile.component.ts

程式碼:

  1. 匯入必要的元件。
  2. 訂閱 MSAL MsalBroadcastServiceinProgress$ 可觀察事件。 此事件會載入帳戶,並讀取識別碼權杖宣告。
  3. 確保 checkAndSetActiveAccount 方法會檢查並設定使用中帳戶。 當應用程式與多個 Azure AD B2C 使用者流程或自定義原則互動時,此動作很常見。
  4. 確保 getClaims 方法會從使用中 MSAL account 物件取得識別碼權杖宣告。 方法接著會將宣告新增至 dataSource 陣列。 陣列會透過元件的範本繫結呈現給使用者。
import { Component, OnInit } from '@angular/core';
import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser';
import { Subject } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';

@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.css']
})

export class ProfileComponent implements OnInit {
  displayedColumns: string[] = ['claim', 'value'];
  dataSource: Claim[] = [];
  private readonly _destroying$ = new Subject<void>();
  
  constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }

  ngOnInit(): void {

    this.msalBroadcastService.inProgress$
      .pipe(
        filter((status: InteractionStatus) =>  status === InteractionStatus.None || status === InteractionStatus.HandleRedirect),
        takeUntil(this._destroying$)
      )
      .subscribe(() => {
        this.checkAndSetActiveAccount();
        this.getClaims(this.authService.instance.getActiveAccount()?.idTokenClaims)
      })
  }

  checkAndSetActiveAccount() {

    let activeAccount = this.authService.instance.getActiveAccount();

    if (!activeAccount && this.authService.instance.getAllAccounts().length > 0) {
      let accounts = this.authService.instance.getAllAccounts();
      this.authService.instance.setActiveAccount(accounts[0]);
    }
  }

  getClaims(claims: any) {

    let list: Claim[]  =  new Array<Claim>();

    Object.keys(claims).forEach(function(k, v){
      
      let c = new Claim()
      c.id = v;
      c.claim = k;
      c.value =  claims ? claims[k]: null;
      list.push(c);
    });
    this.dataSource = list;

  }

  ngOnDestroy(): void {
    this._destroying$.next(undefined);
    this._destroying$.complete();
  }
}

export class Claim {
  id: number;
  claim: string;
  value: string;
}

src/app/profile 資料夾中,使用下列 HTML 代碼段更新 profile.component.html

<h1>ID token claims:</h1>

<table mat-table [dataSource]="dataSource" class="mat-elevation-z8">

  <!-- Claim Column -->
  <ng-container matColumnDef="claim">
    <th mat-header-cell *matHeaderCellDef> Claim </th>
    <td mat-cell *matCellDef="let element"> {{element.claim}} </td>
  </ng-container>

  <!-- Value Column -->
  <ng-container matColumnDef="value">
    <th mat-header-cell *matHeaderCellDef> Value </th>
    <td mat-cell *matCellDef="let element"> {{element.value}} </td>
  </ng-container>

  <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
  <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>

呼叫 Web API

若要呼叫 令牌型授權 Web API,應用程式必須具有有效的存取令牌。 MsalInterceptor 提供者會自動取得傳出要求的權杖,而此權杖使用已知受保護資源的 Angular HttpClient 類別。

這很重要

MSAL 初始化方法 (在 app.module.ts 類別中) 會使用 protectedResourceMap 物件,將受保護的資源,例如 Web API 對應到所需的應用程式範圍。 如果您的程式代碼需要呼叫另一個 Web API,請將具有對應範圍的 Web API URI 和 Web API HTTP 方法新增至 protectedResourceMap 物件。 如需詳細資訊,請參閱 受保護資源地圖

HttpClient 物件呼叫 Web API 時, MsalInterceptor 提供者會採取下列步驟:

  1. 取得具有 Web API 端點所需許可權(範圍)的存取令牌。

  2. 使用下列格式,將存取令牌作為持有者的憑證傳遞到 HTTP 要求的授權標頭中:

    Authorization: Bearer <access-token>
    

webapi.component 檔案示範如何呼叫Web API。 在 src/app/webapi 資料夾中,使用下列代碼段更新 webapi.component.ts

程式碼:

  1. 使用 Angular HttpClient 類別來呼叫 Web API。
  2. 讀取 auth-config 類別的 protectedResources.todoListApi.endpoint 元素。 這個元素指定了 Web API URI。 MSAL 攔截器會根據 Web API URI 來取得具有對應範圍的存取權杖。
  3. 從 Web API 取得設定檔,並設定 profile 類別變數。
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { protectedResources } from '../auth-config';

type ProfileType = {
  name?: string
};

@Component({
  selector: 'app-webapi',
  templateUrl: './webapi.component.html',
  styleUrls: ['./webapi.component.css']
})
export class WebapiComponent implements OnInit {
  todoListEndpoint: string = protectedResources.todoListApi.endpoint;
  profile!: ProfileType;

  constructor(
    private http: HttpClient
  ) { }

  ngOnInit() {
    this.getProfile();
  }

  getProfile() {
    this.http.get(this.todoListEndpoint)
      .subscribe(profile => {
        this.profile = profile;
      });
  }
}

src/app/webapi 資料夾中,使用下列 HTML 代碼段更新 webapi.component.html 。 元件的範本會轉譯 Web API 傳回的名稱。 在頁面底部,範本會轉譯 Web API 位址。

<h1>The web API returns:</h1>
<div>
    <p><strong>Name: </strong> {{profile?.name}}</p>
</div>

<div class="footer-text">
    Web API: {{todoListEndpoint}}
</div>

或者,使用下列 CSS 代碼段更新 webapi.component.css 檔案:

.footer-text {
    position: absolute;
    bottom: 50px;
    color: gray;
}

執行 Angular 應用程式

執行下列命令:

npm start

主控台視窗會顯示裝載應用程式的埠數目。

Listening on port 4200...

小提示

或者,若要執行 npm start 命令,請使用 Visual Studio Code 調試程式。 調試程式可協助您加速編輯、編譯和偵錯迴圈。

在瀏覽器中移至 http://localhost:4200 以檢視應用程式。

後續步驟