Aktivera autentisering i ditt eget Angular-program med hjälp av Azure Active Directory B2C
Den här artikeln visar hur du lägger till Azure Active Directory B2C-autentisering (Azure AD B2C) i din egen Angular ensidesprogram (SPA). Lär dig hur du integrerar ett Angular-program med MSAL för Angular-autentiseringsbiblioteket.
Använd den här artikeln med den relaterade artikeln Konfigurera autentisering i ett exempel Angular ensidesprogram. Ersätt exempelappen Angular med din egen Angular app. När du har slutfört stegen i den här artikeln accepterar programmet inloggningar via Azure AD B2C.
Förutsättningar
Slutför stegen i artikeln Konfigurera autentisering i ett exempel Angular ensidesprogram.
Skapa ett Angular appprojekt
Du kan använda ett befintligt Angular appprojekt eller skapa ett nytt. Kör följande kommandon för att skapa ett nytt projekt.
Kommandona:
- Installera Angular CLI med hjälp av npm-pakethanteraren.
-
Skapa en Angular arbetsyta med en routningsmodul. Appnamnet är
msal-angular-tutorial
. Du kan ändra den till valfritt giltigt Angular appnamn, till exempelcontoso-car-service
. - Ändra till mappen appkatalog.
npm install -g @angular/cli
ng new msal-angular-tutorial --routing=true --style=css --strict=false
cd msal-angular-tutorial
Installera beroendena
Kör följande kommando i kommandogränssnittet för att installera MSAL Browser och MSAL Angular bibliotek i ditt program:
npm install @azure/msal-browser @azure/msal-angular
Installera komponentbiblioteket Angular Material (valfritt för användargränssnittet):
npm install @angular/material @angular/cdk
Lägg till autentiseringskomponenterna
Exempelkoden består av följande komponenter:
Komponent | Typ | Description |
---|---|---|
auth-config.ts | Konstanter | Den här konfigurationsfilen innehåller information om din Azure AD B2C-identitetsprovider och webb-API-tjänsten. Den Angular appen använder den här informationen för att upprätta en förtroenderelation med Azure AD B2C, logga in och logga ut användaren, hämta token och verifiera token. |
app.module.ts | Angular modul | Den här komponenten beskriver hur programdelarna passar ihop. Det här är rotmodulen som används för att starta och öppna programmet. I den här genomgången lägger du till några komponenter i modulen app.module.ts och startar MSAL-biblioteket med MSAL-konfigurationsobjektet. |
app-routing.module.ts | Angular routningsmodul | Den här komponenten aktiverar navigering genom att tolka en webbläsar-URL och läsa in motsvarande komponent. I den här genomgången lägger du till några komponenter i routningsmodulen och skyddar komponenter med MSAL Guard. Endast behöriga användare kan komma åt de skyddade komponenterna. |
app.component.* | Angular komponent | Kommandot ng new skapade ett Angular projekt med en rotkomponent. I den här genomgången ändrar du appkomponenten så att den är värd för det övre navigeringsfältet. Navigeringsfältet innehåller olika knappar, inklusive inloggnings- och utloggningsknappar. Klassen app.component.ts hanterar inloggnings- och utloggningshändelserna. |
home.component.* | Angular komponent | I den här genomgången lägger du till startkomponenten för att rendera startsidan för anonym åtkomst. Den här komponenten visar hur du kontrollerar om en användare har loggat in. |
profile.component.* | Angular komponent | I den här genomgången lägger du till profilkomponenten för att lära dig hur du läser ID-tokenanspråken. |
webapi.component.* | Angular komponent | I den här genomgången lägger du till webapi-komponenten för att lära dig hur du anropar ett webb-API. |
Om du vill lägga till följande komponenter i din app kör du följande Angular CLI-kommandon. Kommandona generate component
:
- Skapa en mapp för varje komponent. Mappen innehåller TypeScript-, HTML-, CSS- och testfilerna.
-
app.module.ts
Uppdatera filerna ochapp-routing.module.ts
med referenser till de nya komponenterna.
ng generate component home
ng generate component profile
ng generate component webapi
Lägg till appinställningarna
Inställningarna för Azure AD B2C-identitetsprovidern och webb-API:et lagras i filen auth-config.ts. I mappen src/app skapar du en fil med namnet auth-config.ts som innehåller följande kod. Ändra sedan inställningarna enligt beskrivningen i 3.1 Konfigurera Angular exempel.
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: []
};
Starta autentiseringsbiblioteken
Offentliga klientprogram är inte betrodda för att på ett säkert sätt behålla programhemligheter, så de har inga klienthemligheter. I mappen src/app öppnar du app.module.ts och gör följande ändringar:
- Importera biblioteken MSAL Angular och MSAL Browser.
- Importera Azure AD B2C-konfigurationsmodulen.
- Importera
HttpClientModule
. HTTP-klienten används för att anropa webb-API:er. - Importera Angular HTTP-interceptor. MSAL använder interceptorn för att mata in ägartoken i HTTP-auktoriseringshuvudet.
- Lägg till de viktigaste Angular materialen.
- Instansiera MSAL med hjälp av det offentliga klientprogramobjektet för flera konton. MSAL-initieringen omfattar att skicka:
- Konfigurationsobjektet för auth-config.ts.
- Konfigurationsobjektet för routningsskyddet.
- Konfigurationsobjektet för MSAL-interceptorn. Interceptor-klassen hämtar automatiskt token för utgående begäranden som använder klassen Angular HttpClient till kända skyddade resurser.
- Konfigurera providersna
HTTP_INTERCEPTORS
ochMsalGuard
Angular. - Lägg till
MsalRedirectComponent
i Angular bootstrap.
I mappen src/appredigerar du app.module.ts och gör ändringarna som visas i följande kodfragment. Ändringarna flaggas med "Ändringarna börjar här" och "Ändringarna slutar här".
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 { }
Konfigurera vägar
I det här avsnittet konfigurerar du vägarna för ditt Angular-program. När en användare väljer en länk på sidan för att flytta inom ett ensidesprogram eller anger en URL i adressfältet mappar vägarna URL:en till en Angular komponent. Angular-routningsgränssnittet canActivate använder MSAL Guard för att kontrollera om användaren är inloggad. Om användaren inte är inloggad tar MSAL användaren till Azure AD B2C för att autentisera.
Redigera app-routing.module.ts i mappen src/app och gör ändringarna som visas i följande kodfragment. Ändringarna flaggas med "Ändringarna börjar här" och "Ändringarna slutar här".
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 { }
Lägg till inloggnings- och utloggningsknapparna
I det här avsnittet lägger du till inloggnings- och utloggningsknapparna i appkomponenten . I mappen src/app öppnar du filen app.component.ts och gör följande ändringar:
Importera de nödvändiga komponenterna.
Ändra klassen för att implementera metoden OnInit. Metoden
OnInit
prenumererar på msalBroadcastService-händelseninProgress$
som kan observeras. Använd den här händelsen för att känna till status för användarinteraktioner, särskilt för att kontrollera att interaktioner har slutförts.Innan du interagerar med MSAL-kontoobjektet kontrollerar du att egenskapen
InteractionStatus
returnerarInteractionStatus.None
. Händelsensubscribe
anroparsetLoginDisplay
metoden för att kontrollera om användaren är autentiserad.Lägg till klassvariabler.
Lägg till metoden
login
som startar auktoriseringsflödet.Lägg till metoden
logout
som loggar ut användaren.Lägg till den
setLoginDisplay
metod som kontrollerar om användaren är autentiserad.Lägg till metoden ngOnDestroy för att rensa prenumerationshändelsen
inProgress$
.
Efter ändringarna bör koden se ut som följande kodfragment:
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. */
}
I mappen src/app redigerar duapp.component.html och gör följande ändringar:
- Lägg till en länk till profil- och webb-API-komponenterna.
- Lägg till inloggningsknappen med klickhändelseattributet inställt på
login()
metoden. Den här knappen visas bara omloginDisplay
klassvariabeln ärfalse
. - Lägg till utloggningsknappen med klickhändelseattributet inställt på
logout()
metoden. Den här knappen visas bara omloginDisplay
klassvariabeln ärtrue
. - Lägg till ett router-outlet-element .
Efter ändringarna bör koden se ut som följande kodfragment:
<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>
Du kan också uppdatera filen app.component.css med följande CSS-kodavsnitt:
.toolbar-spacer {
flex: 1 1 auto;
}
a.title {
color: white;
}
Hantera appomdirigeringar
När du använder omdirigeringar med MSAL måste du lägga till appomdirigeringsdirektivet i index.html. I mappen src redigerar duindex.html enligt följande kodfragment:
<!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>
Ange css för appen (valfritt)
Uppdatera filen styles.css i mappen /src med följande CSS-kodavsnitt:
@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%; }
Tips
Nu kan du köra din app och testa inloggningen. Om du vill köra din app läser du avsnittet Kör Angular program.
Kontrollera om en användare har autentiserats
Filen home.component visar hur du kontrollerar om användaren är autentiserad. I mappen src/app/home uppdaterar du home.component.ts med följande kodfragment.
Koden:
- Prenumererar på MSAL MsalBroadcastService
msalSubject$
ochinProgress$
observerbara händelser. - Säkerställer att
msalSubject$
händelsen skriver autentiseringsresultatet till webbläsarkonsolen. - Ser till att händelsen kontrollerar om en användare autentiseras
inProgress$
. MetodengetAllAccounts()
returnerar ett eller flera objekt.
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;
}
}
I mappen src/app/home uppdaterar duhome.component.html med följande HTML-kodfragment.
*ngIf-direktivet kontrollerar loginDisplay
klassvariabeln för att visa eller dölja välkomstmeddelandena.
<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>
Läs ID-tokenanspråken
Filen profile.component visar hur du kommer åt användarens ID-tokenanspråk. I mappen src/app/profile uppdaterar du profile.component.ts med följande kodfragment.
Koden:
- Importerar nödvändiga komponenter.
- Prenumererar på händelsen MSAL MsalBroadcastService
inProgress$
observable. Händelsen läser in kontot och läser ID-tokenanspråken. - Säkerställer att
checkAndSetActiveAccount
metoden kontrollerar och anger det aktiva kontot. Den här åtgärden är vanlig när appen interagerar med flera Azure AD B2C-användarflöden eller anpassade principer. - Säkerställer att
getClaims
metoden hämtar ID-tokenanspråken från det aktiva MSAL-kontoobjektet. Metoden lägger sedan till anspråken i matrisendataSource
. Matrisen renderas till användaren med komponentens mallbindning.
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;
}
I mappen src/app/profile uppdaterar duprofile.component.html med följande HTML-kodfragment:
<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>
Anropa en webb-API
Om du vill anropa ett webb-API för tokenbaserad auktorisering måste appen ha en giltig åtkomsttoken. MsalInterceptor-providern hämtar automatiskt token för utgående begäranden som använder klassen Angular HttpClient till kända skyddade resurser.
Viktigt
MSAL-initieringsmetoden (i app.module.ts
klassen) mappar skyddade resurser, till exempel webb-API:er, med nödvändiga appomfång med hjälp protectedResourceMap
av objektet. Om koden behöver anropa ett annat webb-API lägger du till webb-API:ets URI och HTTP-metoden för webb-API:et, med motsvarande omfång, i protectedResourceMap
objektet. Mer information finns i Skyddad resurskarta.
När HttpClient-objektet anropar ett webb-API utför MsalInterceptor-providern följande steg:
Hämtar en åtkomsttoken med nödvändiga behörigheter (omfång) för webb-API-slutpunkten.
Skickar åtkomsttoken som en ägartoken i auktoriseringshuvudet för HTTP-begäran med det här formatet:
Authorization: Bearer <access-token>
Filen webapi.component visar hur du anropar ett webb-API. I mappen src/app/webapi uppdaterar du webapi.component.ts med följande kodfragment.
Koden:
- Använder klassen Angular HttpClient för att anropa webb-API:et.
-
auth-config
Läser klassensprotectedResources.todoListApi.endpoint
element. Det här elementet anger webb-API:ets URI. Baserat på webb-API:ets URI hämtar MSAL-interceptorn en åtkomsttoken med motsvarande omfång. - Hämtar profilen från webb-API:et och anger
profile
klassvariabeln.
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;
});
}
}
I mappen src/app/webapi uppdaterar duwebapi.component.html med följande HTML-kodfragment. Komponentens mall återger det namn som webb-API:et returnerar. Längst ned på sidan renderar mallen webb-API-adressen.
<h1>The web API returns:</h1>
<div>
<p><strong>Name: </strong> {{profile?.name}}</p>
</div>
<div class="footer-text">
Web API: {{todoListEndpoint}}
</div>
Du kan också uppdatera filen webapi.component.css med följande CSS-kodfragment:
.footer-text {
position: absolute;
bottom: 50px;
color: gray;
}
Kör Angular-programmet
Kör följande kommando:
npm start
Konsolfönstret visar antalet portar där programmet finns.
Listening on port 4200...
Tips
Du kan också köra npm start
kommandot genom att använda Felsökningsprogrammet för Visual Studio Code. Felsökningsprogrammet hjälper dig att påskynda redigerings-, kompilerings- och felsökningsloopen.
Gå till http://localhost:4200
i webbläsaren för att visa programmet.