使用 Microsoft Graph 生成 Angular 单页应用

获取日历视图

在此练习中,你将 Microsoft Graph应用程序。 对于此应用程序,你将使用 microsoft-graph-client 库调用 Microsoft Graph。

从 Outlook 获取日历事件

  1. 添加新服务,以保留所有Graph呼叫。 在 CLI 中运行以下命令。

    ng generate service graph
    

    与之前创建的身份验证服务一样,通过为此创建服务,你可以将该服务注入需要访问 Microsoft Graph。

  2. 命令完成后,打开 ./src/app/graph.service.ts ,并将其内容替换为以下内容。

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

    考虑此代码将执行什么工作。

    • 它在服务的Graph中初始化一个客户端。
    • 它实现一getCalendarView个函数,该函数Graph客户端:
      • 将调用的 URL 为 /me/calendarview
      • 方法 header 包括 标头 Prefer: outlook.timezone ,这将导致返回事件的开始时间和结束时间在用户的首选时区。
      • 方法 query 添加 和 startDateTime endDateTime 参数,定义日历视图的时间窗口。
      • 方法 select 将每个事件返回的字段限定为视图将实际使用的字段。
      • 方法 orderby 按开始时间对结果进行排序。
  3. 创建Angular组件以调用此新方法并显示调用的结果。 在 CLI 中运行以下命令。

    ng generate component calendar
    
  4. 命令完成后,将组件添加到 routes ./src/app/app-routing.module.ts 中的数组

    import { CalendarComponent } from './calendar/calendar.component';
    
    const routes: Routes = [
      { path: '', component: HomeComponent },
      { path: 'calendar', component: CalendarComponent },
    ];
    
  5. 打开 ./src/app/calendar/calendar.component.ts ,并将其内容替换为以下内容。

    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));
      }
    }
    

现在,这只是在页面上以 JSON 呈现事件数组。 保存更改并重新启动该应用。 登录并单击导航 栏中 的"日历"链接。 如果一切正常,应在用户日历上看到事件被 JSON 卸载。

显示结果

现在,你可以更新 CalendarComponent 组件,以更用户友好的方式显示事件。

  1. 从 函数中删除添加警报的临时 ngOnInit 代码。 更新的函数应如下所示。

    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;
          });
    }
    
  2. 将函数添加到 类 CalendarComponent 以将对象 DateTimeTimeZone 格式化为 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 '';
      }
    }
    
  3. 打开 "./src/app/calendar/calendar.component.html ",并将其内容替换为以下内容。

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

这将循环访问事件集合,并针对每个事件添加一个表行。 保存更改并重新启动应用。 单击" 日历" 链接,应用现在应呈现一个事件表。

事件表的屏幕截图