Crear aplicaciones de Angular de página única con Microsoft Graph
Este tutorial te enseña a crear una aplicación Angular de una sola página que use Microsoft Graph para recuperar información de calendario para un usuario.
Sugerencia
Si prefiere descargar el tutorial completado, puede descargar o clonar el repositorio GitHub archivo.
Requisitos previos
Antes de iniciar este tutorial, debería haberNode.js instalado en el equipo de desarrollo. Si no tiene Node.js, visite el vínculo anterior para ver las opciones de descarga.
También debe tener una cuenta personal de Microsoft con un buzón en Outlook.com, o una cuenta de Trabajo o escuela de Microsoft. Si no tienes una cuenta de Microsoft, hay un par de opciones para obtener una cuenta gratuita:
- Puedes suscribirte a una nueva cuenta personal de Microsoft.
- Puedes suscribirte al programa Microsoft 365 desarrolladores para obtener una suscripción Office 365 gratuita.
Nota
Este tutorial se escribió con Node versión 14.15.0. Los pasos de esta guía pueden funcionar con otras versiones, pero eso no se ha probado.
Comentarios
Proporcione cualquier comentario sobre este tutorial en el repositorio GitHub usuario.
Crear una aplicación de Angular de página única
En esta sección, creará un nuevo proyecto Angular proyecto.
Abra la interfaz de línea de comandos (CLI), vaya a un directorio donde tenga derechos para crear archivos y ejecute los siguientes comandos para instalar la herramienta de cli de Angular y crear una nueva aplicación Angular.
npm install -g @angular/cli ng new graph-tutorial
La Angular CLI pedirá más información. Responda a los mensajes de la siguiente manera.
? Would you like to add Angular routing? Yes ? Which stylesheet format would you like to use? CSS
Cuando finalice el comando, cambie
graph-tutorial
al directorio de la CLI y ejecute el siguiente comando para iniciar un servidor web local.ng serve --open
El explorador predeterminado se abre con https://localhost:4200/ una página Angular predeterminada. Si el explorador no se abre, ábralo y busque https://localhost:4200/ para comprobar que la nueva aplicación funciona.
Agregar paquetes de nodo
Antes de seguir adelante, instale algunos paquetes adicionales que usará más adelante:
- ng-bootstrap para usar componentes de Bootstrap desde Angular.
- momento para dar formato a fechas y horas.
- windows-iana
- msal-angular para autenticar a Azure Active Directory y recuperar tokens de acceso.
- microsoft-graph-client para realizar llamadas a Microsoft Graph.
Ejecute los siguientes comandos en la CLI.
ng add @ng-bootstrap/ng-bootstrap npm install @azure/msal-browser@2.16.1 @azure/msal-angular@2.0.2 npm install date-fns@2.23.0 date-fns-tz@1.1.6 windows-iana@5.0.2 npm install @microsoft/microsoft-graph-client@3.0.0 npm install @microsoft/microsoft-graph-types --save-dev
Diseñar la aplicación
En esta sección, crearás la interfaz de usuario para la aplicación.
Abra ./src/styles.css y agregue las siguientes líneas.
@import "~bootstrap/dist/css/bootstrap.css"; /* Add padding for the nav bar */ body { padding-top: 4.5rem; } /* Style debug info in alerts */ .alert-pre { word-wrap: break-word; word-break: break-all; white-space: pre-wrap; } /* Add padding to user avatar link */ .avatar-link { padding-top: .25em; padding-bottom: .25em; }
Cree un nuevo archivo en la carpeta ./src/app denominada user.ts y agregue el siguiente código.
export class User { displayName!: string; email!: string; avatar!: string; timeZone!: string; }
Genere un Angular para la navegación superior de la página. En la CLI, ejecute el siguiente comando.
ng generate component nav-bar
Una vez completado el comando, abra ./src/app/nav-bar/nav-bar.component.ts y reemplace su contenido por lo siguiente.
import { Component, OnInit } from '@angular/core'; import { User } from '../user'; @Component({ selector: 'app-nav-bar', templateUrl: './nav-bar.component.html', styleUrls: ['./nav-bar.component.css'] }) export class NavBarComponent implements OnInit { // Should the collapsed nav show? showNav: boolean = false; // Is a user logged in? authenticated: boolean = false; // The user user?: User = undefined; constructor() { } ngOnInit() { } // Used by the Bootstrap navbar-toggler button to hide/show // the nav in a collapsed state toggleNavBar(): void { this.showNav = !this.showNav; } signIn(): void { // Temporary this.authenticated = true; this.user = { displayName: 'Adele Vance', email: 'AdeleV@contoso.com', avatar: '/assets/no-profile-photo.png', timeZone: '' }; } signOut(): void { // Temporary this.authenticated = false; this.user = undefined; } }
Abra ./src/app/nav-bar/nav-bar.component.html y reemplace su contenido por lo siguiente.
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark"> <div class="container"> <a routerLink="/" class="navbar-brand">Angular Graph Tutorial</a> <button class="navbar-toggler" type="button" (click)="toggleNavBar()" [attr.aria-expanded]="showNav" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" [class.show]="showNav" id="navbarCollapse"> <ul class="navbar-nav mr-auto"> <li class="nav-item"> <a routerLink="/" class="nav-link" routerLinkActive="active">Home</a> </li> <li *ngIf="authenticated" class="nav-item"> <a routerLink="/calendar" class="nav-link" routerLinkActive="active">Calendar</a> </li> </ul> <ul class="navbar-nav justify-content-end"> <li class="nav-item"> <a class="nav-link" href="https://docs.microsoft.com/graph/overview" target="_blank">Docs</a> </li> <li *ngIf="authenticated" ngbDropdown placement="bottom-right" class="nav-item"> <a ngbDropdownToggle id="userMenu" class="nav-link avatar-link" role="button" aria-haspopup="true" aria-expanded="false"> <img src="{{user != null ? user.avatar : ''}}" class="rounded-circle align-self-center mr-2" style="width: 32px;"> </a> <div ngbDropdownMenu aria-labelledby="userMenu" class="dropdown-menu-right"> <h5 class="dropdown-item-text mb-0">{{user != null ? user.displayName : ''}}</h5> <p class="dropdown-item-text text-muted mb-0">{{user != null ? user.email : ''}}</p> <div class="dropdown-divider"></div> <button class="dropdown-item" role="button" (click)="signOut()">Sign Out</button> </div> </li> <li *ngIf="!authenticated" class="nav-item"> <button class="btn btn-link nav-link border-0" role="button" (click)="signIn()">Sign In</button> </li> </ul> </div> </div> </nav>
Crea una página principal para la aplicación. Ejecute el siguiente comando en la CLI.
ng generate component home
Una vez completado el comando, abra ./src/app/home/home.component.ts y reemplace su contenido por lo siguiente.
import { Component, OnInit } from '@angular/core'; import { User } from '../user'; @Component({ selector: 'app-home', templateUrl: './home.component.html', styleUrls: ['./home.component.css'] }) export class HomeComponent implements OnInit { // Is a user logged in? authenticated: boolean = false; // The user user?: User = undefined; constructor() { } ngOnInit() { } signIn(): void { // Temporary this.authenticated = true; this.user = { displayName: 'Adele Vance', email: 'AdeleV@contoso.com', avatar: '', timeZone: '' }; } }
Abra ./src/app/home/home.component.html y reemplace su contenido por lo siguiente.
<div class="jumbotron"> <h1>Angular Graph Tutorial</h1> <p class="lead">This sample app shows how to use the Microsoft Graph API from Angular</p> <div *ngIf="authenticated; then welcomeUser else signInPrompt"></div> <ng-template #welcomeUser> <h4>Welcome {{ user != null ? user.displayName : '' }}!</h4> <p>Use the navigation bar at the top of the page to get started.</p> </ng-template> <ng-template #signInPrompt> <button class="btn btn-primary btn-large" role="button" (click)="signIn()">Click here to sign in</button> </ng-template> </div>
Cree una clase
Alert
sencilla. Cree un nuevo archivo en el directorio ./src/app denominado alert.ts y agregue el siguiente código.export class Alert { type!: string; message!: string; debug!: string; }
Crea un servicio de alertas que la aplicación puede usar para mostrar mensajes al usuario. En la CLI, ejecute el siguiente comando.
ng generate service alerts
Abra ./src/app/alerts.service.ts y reemplace su contenido por lo siguiente.
import { Injectable } from '@angular/core'; import { Alert } from './alert'; @Injectable({ providedIn: 'root' }) export class AlertsService { alerts: Alert[] = []; addError(message: string, debug?: string) { this.alerts.push({message: message, debug: debug ?? '', type: 'danger'}); } addSuccess(message: string, debug?: string) { this.alerts.push({message: message, debug: debug ?? '', type: 'success'}); } remove(alert: Alert) { this.alerts.splice(this.alerts.indexOf(alert), 1); } }
Generar un componente de alertas para mostrar alertas. En la CLI, ejecute el siguiente comando.
ng generate component alerts
Una vez completado el comando, abra ./src/app/alerts/alerts.component.ts y reemplace su contenido por lo siguiente.
import { Component, OnInit } from '@angular/core'; import { AlertsService } from '../alerts.service'; import { Alert } from '../alert'; @Component({ selector: 'app-alerts', templateUrl: './alerts.component.html', styleUrls: ['./alerts.component.css'] }) export class AlertsComponent implements OnInit { constructor(public alertsService: AlertsService) { } ngOnInit() { } close(alert: Alert) { this.alertsService.remove(alert); } }
Abra ./src/app/alerts/alerts.component.html y reemplace su contenido por lo siguiente.
<div *ngFor="let alert of alertsService.alerts"> <ngb-alert type="{{alert.type}}" (close)="close(alert)"> {{alert.message}} <pre *ngIf="alert.debug" class="alert-pre border bg-light p-2 mt-2"><code>{{alert.debug}}</code></pre> </ngb-alert> </div>
Abra ./src/app/app-routing.module.ts y reemplace la
const routes: Routes = [];
línea por el código siguiente.import { HomeComponent } from './home/home.component'; const routes: Routes = [ { path: '', component: HomeComponent }, ];
Abra ./src/app/app.component.html y reemplace todo su contenido por lo siguiente.
<app-nav-bar></app-nav-bar> <main role="main" class="container"> <app-alerts></app-alerts> <router-outlet></router-outlet> </main>
Agregue un archivo de imagen de su elección denominado no-profile-photo.png en el directorio ./src/assets . Esta imagen se usará como foto del usuario cuando el usuario no tenga ninguna foto en Microsoft Graph.
Guarde todos los cambios y actualice la página. Ahora, la aplicación debe tener un aspecto muy diferente.
Registrar la aplicación en el portal
En este ejercicio, creará un nuevo registro Azure AD aplicación web mediante el Centro Azure Active Directory administración.
Abra un explorador y vaya al centro de administración de Azure Active Directory. Inicie sesión con una cuenta personal (también conocida como: cuenta Microsoft) o una cuenta profesional o educativa.
Seleccione Azure Active Directory en el panel de navegación izquierdo y, a continuación, seleccione Registros de aplicaciones en Administrar.
Seleccione Nuevo registro. En la página Registrar una aplicación, establezca los valores siguientes.
- Establezca Nombre como
Angular Graph Tutorial
. - Establezca Tipos de cuenta admitidos en Cuentas en cualquier directorio de organización y cuentas personales de Microsoft.
- En URI de redirección, establezca la primera lista desplegable en
Single-page application (SPA)
y establezca el valorhttp://localhost:4200
.
- Establezca Nombre como
Seleccione Registrar. En la Angular Graph tutorial, copie el valor del identificador de aplicación (cliente ) y guárdelo, lo necesitará en el paso siguiente.
Agregar autenticación de Azure AD
En este ejercicio, extenderá la aplicación desde el ejercicio anterior para admitir la autenticación con Azure AD. Esto es necesario para obtener el token de acceso OAuth necesario para llamar a Microsoft Graph. En este paso, integrará la Biblioteca de autenticación de Microsoft Angular en la aplicación.
Cree un nuevo archivo en el directorio ./src denominado oauth.ts y agregue el siguiente código.
export const OAuthSettings = { appId: 'YOUR_APP_ID_HERE', redirectUri: 'http://localhost:4200', scopes: [ "user.read", "mailboxsettings.read", "calendars.readwrite" ] };
Reemplace
YOUR_APP_ID_HERE
por el identificador de aplicación del Portal de registro de aplicaciones.Importante
Si usas el control de código fuente como git, ahora sería un buen momento para excluir el archivo oauth.ts del control de código fuente para evitar la pérdida involuntaria del identificador de la aplicación.
Abre ./src/app/app.module.ts y agrega las
import
siguientes instrucciones a la parte superior del archivo.import { FormsModule } from '@angular/forms'; import { IPublicClientApplication, PublicClientApplication, BrowserCacheLocation } from '@azure/msal-browser'; import { MsalModule, MsalService, MSAL_INSTANCE } from '@azure/msal-angular'; import { OAuthSettings } from '../oauth';
Agregue la siguiente función debajo de las
import
instrucciones.let msalInstance: IPublicClientApplication | undefined = undefined; export function MSALInstanceFactory(): IPublicClientApplication { msalInstance = msalInstance ?? new PublicClientApplication({ auth: { clientId: OAuthSettings.appId, redirectUri: OAuthSettings.redirectUri, postLogoutRedirectUri: OAuthSettings.redirectUri }, cache: { cacheLocation: BrowserCacheLocation.LocalStorage, } }); return msalInstance; }
Agregue
MsalModule
yFormsModule
a laimports
matriz dentro de la@NgModule
declaración.imports: [ BrowserModule, FormsModule, AppRoutingModule, NgbModule, MsalModule ],
Agregue el
MSALInstanceFactory
yMsalService
a laproviders
matriz dentro de la@NgModule
declaración.providers: [ { provide: MSAL_INSTANCE, useFactory: MSALInstanceFactory }, MsalService ],
Implementar el inicio de sesión
En esta sección, creará un servicio de autenticación e implementará el inicio de sesión y la salida.
Ejecute el siguiente comando en la CLI.
ng generate service auth
Al crear un servicio para esto, puede insertarlo fácilmente en cualquier componente que necesite acceso a los métodos de autenticación.
Una vez que finalice el comando, abra ./src/app/auth.service.ts y reemplace su contenido por el código siguiente.
import { Injectable } from '@angular/core'; import { MsalService } from '@azure/msal-angular'; import { InteractionType, PublicClientApplication } from '@azure/msal-browser'; import { AlertsService } from './alerts.service'; import { OAuthSettings } from '../oauth'; import { User } from './user'; @Injectable({ providedIn: 'root' }) export class AuthService { public authenticated: boolean; public user?: User; constructor( private msalService: MsalService, private alertsService: AlertsService) { this.authenticated = false; this.user = undefined; } // Prompt the user to sign in and // grant consent to the requested permission scopes async signIn(): Promise<void> { const result = await this.msalService .loginPopup(OAuthSettings) .toPromise() .catch((reason) => { this.alertsService.addError('Login failed', JSON.stringify(reason, null, 2)); }); if (result) { this.msalService.instance.setActiveAccount(result.account); this.authenticated = true; // Temporary placeholder this.user = new User(); this.user.displayName = 'Adele Vance'; this.user.email = 'AdeleV@contoso.com'; this.user.avatar = '/assets/no-profile-photo.png'; // Temporary to display token in an error box this.alertsService.addSuccess('Token acquired', result.accessToken); } } // Sign out async signOut(): Promise<void> { await this.msalService.logout().toPromise(); this.user = undefined; this.authenticated = false; } }
Abra ./src/app/nav-bar/nav-bar.component.ts y reemplace su contenido por lo siguiente.
import { Component, OnInit } from '@angular/core'; import { AuthService } from '../auth.service'; import { User } from '../user'; @Component({ selector: 'app-nav-bar', templateUrl: './nav-bar.component.html', styleUrls: ['./nav-bar.component.css'] }) export class NavBarComponent implements OnInit { // Should the collapsed nav show? showNav: boolean = false; // Is a user logged in? get authenticated(): boolean { return this.authService.authenticated; } // The user get user(): User | undefined { return this.authService.user; } constructor(private authService: AuthService) { } ngOnInit() { } // Used by the Bootstrap navbar-toggler button to hide/show // the nav in a collapsed state toggleNavBar(): void { this.showNav = !this.showNav; } async signIn(): Promise<void> { await this.authService.signIn(); } signOut(): void { this.authService.signOut(); } }
Abra ./src/app/home/home.component.ts y reemplace su contenido por lo siguiente.
Guarde los cambios y actualice el explorador. Haga clic en el botón Hacer clic aquí para iniciar sesión y se le redirigirá a https://login.microsoftonline.com
. Inicie sesión con su cuenta de Microsoft y consiente los permisos solicitados. La página de la aplicación debe actualizarse y mostrar el token.
Obtener detalles del usuario
En este momento, el servicio de autenticación establece valores constantes para el nombre para mostrar y la dirección de correo electrónico del usuario. Ahora que tiene un token de acceso, puede obtener detalles de usuario de Microsoft Graph para que esos valores correspondan al usuario actual.
Abra ./src/app/auth.service.ts y agregue
import
las siguientes instrucciones a la parte superior del archivo.import { Client } from '@microsoft/microsoft-graph-client'; import { AuthCodeMSALBrowserAuthenticationProvider } from '@microsoft/microsoft-graph-client/authProviders/authCodeMsalBrowser'; import * as MicrosoftGraph from '@microsoft/microsoft-graph-types';
Agregue una propiedad a la clase
AuthService
denominadagraphClient
.public graphClient?: Client;
Agregue una nueva función llamada
AuthService
a la clasegetUser
.private async getUser(): Promise<User | undefined> { if (!this.authenticated) return undefined; const graphClient = Client.init({ // Initialize the Graph client with an auth // provider that requests the token from the // auth service authProvider: async(done) => { const token = await this.getAccessToken() .catch((reason) => { done(reason, null); }); if (token) { done(null, token); } else { done("Could not get an access token", null); } } }); // Get the user from Graph (GET /me) const graphUser: MicrosoftGraph.User = await graphClient .api('/me') .select('displayName,mail,mailboxSettings,userPrincipalName') .get(); const user = new User(); user.displayName = graphUser.displayName ?? ''; // Prefer the mail property, but fall back to userPrincipalName user.email = graphUser.mail ?? graphUser.userPrincipalName ?? ''; user.timeZone = graphUser.mailboxSettings?.timeZone ?? 'UTC'; // Use default avatar user.avatar = '/assets/no-profile-photo.png'; return user; }
Busque y quite el siguiente código del
signIn
método.// Temporary placeholder this.user = new User(); this.user.displayName = "Adele Vance"; this.user.email = "AdeleV@contoso.com"; this.user.avatar = '/assets/no-profile-photo.png'; // Temporary to display token in an error box this.alertsService.addSuccess('Token acquired', result);
En su lugar, agregue el siguiente código.
this.user = await this.getUser();
Este nuevo código usa el SDK de Microsoft Graph para obtener los detalles del usuario y, a
User
continuación, crea un objeto con los valores devueltos por la llamada api.Cambie la
constructor
clase paraAuthService
comprobar si el usuario ya ha iniciado sesión y cargue sus detalles si es así. Reemplace el existenteconstructor
por lo siguiente.constructor( private msalService: MsalService, private alertsService: AlertsService) { this.authenticated = this.msalService.instance .getAllAccounts().length > 0; this.getUser().then((user) => {this.user = user}); }
Ahora, si guarda los cambios e inicia la aplicación, después del inicio de sesión debe volver a la página principal, pero la interfaz de usuario debe cambiar para indicar que ha iniciado sesión.
Haga clic en el avatar del usuario en la esquina superior derecha para obtener acceso al vínculo Cerrar sesión . Al hacer clic en Cerrar sesión, se restablece la sesión y se devuelve a la página principal.
Almacenar y actualizar tokens
En este momento, la aplicación tiene un token de acceso, que se envía en el encabezado Authorization
de las llamadas API. Este es el token que permite a la aplicación tener acceso al Graph microsoft en nombre del usuario.
Sin embargo, este token es de corta duración. El token expira una hora después de su emisión. Dado que la aplicación usa la biblioteca MSAL, no es necesario implementar ninguna lógica de actualización o almacenamiento de tokens. El MsalService
token se almacena en caché en el almacenamiento del explorador. El acquireTokenSilent
método primero comprueba el token almacenado en caché y, si no ha expirado, lo devuelve. Si ha expirado, realiza una solicitud silenciosa para obtener una nueva.
Obtener vista de calendario
En este ejercicio, incorporará el Graph Microsoft en la aplicación. Para esta aplicación, usará la biblioteca de microsoft-graph-client para realizar llamadas a Microsoft Graph.
Obtener eventos del calendario desde Outlook
Agregue un nuevo servicio para contener todas las Graph llamadas. Ejecute el siguiente comando en la CLI.
ng generate service graph
Al igual que con el servicio de autenticación que creó anteriormente, la creación de un servicio para esto le permite insertarlo en los componentes que necesitan acceso a Microsoft Graph.
Una vez completado el comando, abra ./src/app/graph.service.ts y reemplace su contenido por lo siguiente.
import { Injectable } from '@angular/core'; import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; import { AuthService } from './auth.service'; import { AlertsService } from './alerts.service'; @Injectable({ providedIn: 'root' }) export class GraphService { constructor( private authService: AuthService, private alertsService: AlertsService) {} async getCalendarView(start: string, end: string, timeZone: string): Promise<MicrosoftGraph.Event[] | undefined> { if (!this.authService.graphClient) { this.alertsService.addError('Graph client is not initialized.'); return undefined; } try { // GET /me/calendarview?startDateTime=''&endDateTime='' // &$select=subject,organizer,start,end // &$orderby=start/dateTime // &$top=50 const result = await this.authService.graphClient .api('/me/calendarview') .header('Prefer', `outlook.timezone="${timeZone}"`) .query({ startDateTime: start, endDateTime: end }) .select('subject,organizer,start,end') .orderby('start/dateTime') .top(50) .get(); return result.value; } catch (error) { this.alertsService.addError('Could not get events', JSON.stringify(error, null, 2)); } return undefined; } }
Tenga en cuenta lo que está haciendo este código.
- Inicializa un cliente Graph en el constructor del servicio.
- Implementa una función
getCalendarView
que usa el Graph cliente de la siguiente manera:- La dirección URL a la que se llamará es
/me/calendarview
. - El
header
método incluye elPrefer: outlook.timezone
encabezado, lo que hace que las horas de inicio y finalización de los eventos devueltos se incluyan en la zona horaria preferida del usuario. - El
query
método agrega los parámetrosstartDateTime
yendDateTime
, definiendo la ventana de tiempo para la vista de calendario. - El
select
método limita los campos devueltos para cada evento a solo aquellos que la vista usará realmente. - El
orderby
método ordena los resultados por hora de inicio.
- La dirección URL a la que se llamará es
Cree un Angular para llamar a este nuevo método y mostrar los resultados de la llamada. Ejecute el siguiente comando en la CLI.
ng generate component calendar
Una vez completado el comando, agregue el componente a
routes
la matriz en ./src/app/app-routing.module.ts.import { CalendarComponent } from './calendar/calendar.component'; const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'calendar', component: CalendarComponent }, ];
Abra ./src/app/calendar/calendar.component.ts y reemplace su contenido por lo siguiente.
import { Component, OnInit } from '@angular/core'; import { parseISO } from 'date-fns'; import { endOfWeek, startOfWeek } from 'date-fns/esm'; import { zonedTimeToUtc } from 'date-fns-tz'; import { findIana } from 'windows-iana'; import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; import { AuthService } from '../auth.service'; import { GraphService } from '../graph.service'; import { AlertsService } from '../alerts.service'; @Component({ selector: 'app-calendar', templateUrl: './calendar.component.html', styleUrls: ['./calendar.component.css'] }) export class CalendarComponent implements OnInit { public events?: MicrosoftGraph.Event[]; constructor( private authService: AuthService, private graphService: GraphService, private alertsService: AlertsService) { } async ngOnInit() { // Convert the user's timezone to IANA format const ianaName = findIana(this.authService.user?.timeZone ?? 'UTC'); const timeZone = ianaName![0].valueOf() || this.authService.user?.timeZone || 'UTC'; // Get midnight on the start of the current week in the user's timezone, // but in UTC. For example, for Pacific Standard Time, the time value would be // 07:00:00Z const now = new Date(); const weekStart = zonedTimeToUtc(startOfWeek(now), timeZone); const weekEnd = zonedTimeToUtc(endOfWeek(now), timeZone); this.events = await this.graphService.getCalendarView( weekStart.toISOString(), weekEnd.toISOString(), this.authService.user?.timeZone ?? 'UTC'); // Temporary to display raw results this.alertsService.addSuccess('Events from Graph', JSON.stringify(events, null, 2)); } }
Por ahora, esto simplemente representa la matriz de eventos en JSON en la página. Guarde los cambios y reinicie la aplicación. Inicie sesión y haga clic en el vínculo Calendario de la barra de navegación. Si funciona todo, debería ver un volcado JSON de eventos en el calendario del usuario.
Mostrar los resultados
Ahora puede actualizar el componente CalendarComponent
para mostrar los eventos de una manera más fácil de usar.
Quite el código temporal que agrega una alerta de la
ngOnInit
función. La función actualizada debe tener este aspecto.ngOnInit() { // Convert the user's timezone to IANA format const ianaName = findIana(this.authService.user?.timeZone ?? 'UTC'); const timeZone = ianaName![0].valueOf() || this.authService.user?.timeZone || 'UTC'; // Get midnight on the start of the current week in the user's timezone, // but in UTC. For example, for Pacific Standard Time, the time value would be // 07:00:00Z var startOfWeek = moment.tz(timeZone).startOf('week').utc(); var endOfWeek = moment(startOfWeek).add(7, 'day'); this.graphService.getCalendarView( startOfWeek.format(), endOfWeek.format(), this.authService.user?.timeZone ?? 'UTC') .then((events) => { this.events = events; }); }
Agregue una función a la clase
CalendarComponent
para dar formato a unDateTimeTimeZone
objeto en una cadena ISO.formatDateTimeTimeZone(dateTime: MicrosoftGraph.DateTimeTimeZone | undefined | null): string { if (dateTime == undefined || dateTime == null) { return ''; } try { // Pass UTC for the time zone because the value // is already adjusted to the user's time zone return moment.tz(dateTime.dateTime, 'UTC').format(); } catch(error) { this.alertsService.addError('DateTimeTimeZone conversion error', JSON.stringify(error)); return ''; } }
Abra ./src/app/calendar/calendar.component.html y reemplace su contenido por lo siguiente.
<h1>Calendar</h1> <a class="btn btn-light btn-sm mb-3" routerLink="/newevent">New event</a> <table class="table"> <thead> <th scope="col">Organizer</th> <th scope="col">Subject</th> <th scope="col">Start</th> <th scope="col">End</th> </thead> <tbody> <tr *ngFor="let event of events"> <td>{{event.organizer?.emailAddress?.name}}</td> <td>{{event.subject}}</td> <!-- Use 'UTC' in the pipe to date so Angular will not convert the already converted time to local time. See https://angular.io/api/common/DatePipe --> <td>{{formatDateTimeTimeZone(event.start) | date:'short' : 'UTC' }}</td> <td>{{formatDateTimeTimeZone(event.end) | date: 'short' : 'UTC' }}</td> </tr> </tbody> </table>
Esto recorre la colección de eventos y agrega una fila de tabla para cada uno. Guarda los cambios y reinicia la aplicación. Haz clic en el vínculo Calendario y la aplicación ahora debe representar una tabla de eventos.
Crear un nuevo evento
En esta sección, agregará la capacidad de crear eventos en el calendario del usuario.
Abra ./src/app/graph.service.ts y agregue la siguiente función a la
GraphService
clase.async addEventToCalendar(newEvent: MicrosoftGraph.Event): Promise<void> { try { // POST /me/events await this.graphClient .api('/me/events') .post(newEvent); } catch (error) { throw Error(JSON.stringify(error, null, 2)); } }
Crear un formulario de evento nuevo
Cree un Angular para mostrar un formulario y llamar a esta nueva función. Ejecute el siguiente comando en la CLI.
ng generate component new-event
Una vez completado el comando, agregue el componente a
routes
la matriz en ./src/app/app-routing.module.ts.import { NewEventComponent } from './new-event/new-event.component'; const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'calendar', component: CalendarComponent }, { path: 'newevent', component: NewEventComponent }, ];
Cree un nuevo archivo en el directorio ./src/app/new-event denominado new-event.ts y agregue el siguiente código.
import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; // Model for the new event form export class NewEvent { subject?: string; attendees?: string; start?: string; end?: string; body?: string; // Generate a MicrosoftGraph.Event from the model getGraphEvent(timeZone: string): MicrosoftGraph.Event { const graphEvent: MicrosoftGraph.Event = { subject: this.subject, start: { dateTime: this.start, timeZone: timeZone }, end: { dateTime: this.end, timeZone: timeZone } }; // If there are attendees, convert to array // and add them if (this.attendees && this.attendees.length > 0) { graphEvent.attendees = []; const emails = this.attendees.split(';'); emails.forEach(email => { graphEvent.attendees?.push({ type: 'required', emailAddress: { address: email } }); }); } // If there is a body, add it as plain text if (this.body && this.body.length > 0) { graphEvent.body = { contentType: 'text', content: this.body }; } return graphEvent; } }
Esta clase servirá como modelo para el nuevo formulario de evento.
Abra ./src/app/new-event/new-event.component.ts y reemplace su contenido por el código siguiente.
import { Component, OnInit } from '@angular/core'; import { AuthService } from '../auth.service'; import { GraphService } from '../graph.service'; import { AlertsService } from '../alerts.service'; import { NewEvent } from './new-event'; @Component({ selector: 'app-new-event', templateUrl: './new-event.component.html', styleUrls: ['./new-event.component.css'] }) export class NewEventComponent implements OnInit { model = new NewEvent(); constructor( private authService: AuthService, private graphService: GraphService, private alertsService: AlertsService) { } ngOnInit(): void { } onSubmit(): void { const timeZone = this.authService.user?.timeZone ?? 'UTC'; const graphEvent = this.model.getGraphEvent(timeZone); this.graphService.addEventToCalendar(graphEvent) .then(() => { this.alertsService.addSuccess('Event created.'); }).catch(error => { this.alertsService.addError('Error creating event.', error.message); }); } }
Abra ./src/app/new-event/new-event.component.html y reemplace su contenido por el código siguiente.
<h1>New event</h1> <form (ngSubmit)="onSubmit()" #newEventForm="ngForm"> <div class="form-group"> <label for="subject">Subject</label> <input type="text" class="form-control" id="subject" required [(ngModel)]="model.subject" name="subject" #name="ngModel"> <div [hidden]="name.valid || name.pristine" class="alert alert-danger">Subject is required</div> </div> <div class="form-group"> <label for="attendees">Attendees</label> <input type="text" class="form-control" id="attendees" placeholder="Enter one or more email addresses (separated with a ;) to add attendees" [(ngModel)]="model.attendees" name="attendees" #name="ngModel"> </div> <div class="form-row"> <div class="col"> <div class="form-group"> <label for="start">Start</label> <input type="datetime-local" class="form-control" id="start" required [(ngModel)]="model.start" name="start" #name="ngModel"> <div [hidden]="name.valid || name.pristine" class="alert alert-danger">Start is required</div> </div> </div> <div class="col"> <div class="form-group"> <label for="end">End</label> <input type="datetime-local" class="form-control" id="end" required [(ngModel)]="model.end" name="end" #name="ngModel"> <div [hidden]="name.valid || name.pristine" class="alert alert-danger">End is required</div> </div> </div> </div> <div class="form-group"> <label for="body">Body</label> <textarea class="form-control" id="body" rows="4" [(ngModel)]="model.body" name="body" #name="ngModel"></textarea> </div> <button type="submit" class="btn btn-primary mr-2" [disabled]="!newEventForm.form.valid">Create</button> <a class="btn btn-secondary" routerLink="/calendar">Cancel</a> </form>
Guarda los cambios y actualiza la aplicación. Seleccione el botón Nuevo evento en la página calendario y, a continuación, use el formulario para crear un evento en el calendario del usuario.
¡Enhorabuena!
Ha completado el tutorial Angular microsoft Graph microsoft. Ahora que tienes una aplicación de trabajo que llama a Microsoft Graph, puedes experimentar y agregar nuevas características. Visite la información general de Microsoft Graph para ver todos los datos a los que puede acceder con Microsoft Graph.
Comentarios
Proporcione cualquier comentario sobre este tutorial en el repositorio GitHub usuario.
¿Tiene algún problema con esta sección? Si es así, envíenos sus comentarios para que podamos mejorarla.