练习 - 创建显示图像轮播的 SPFx 图像卡 ACE

已完成

在本练习中,你将使用图像卡模板创建一个SharePoint 框架 (SPFx) 自适应卡扩展 (ACE) ,该模板显示所选火星车上某个相机拍摄的图像。

先决条件

开发用于Viva Connections的 ACE 需要在租户中设置 Microsoft 365 租户、SharePoint Online 和Viva Connections。 使用以下资源准备租户:

还需要在工作站上安装必要的开发人员工具:

重要

在大多数情况下,安装以下工具的最新版本是最佳选择。 此模块在发布和最后测试过程中,使用了此处列出的版本。

创建 SPFx 项目

打开命令提示符,移动到要在其中创建 SPFx 项目的文件夹。 然后,执行以下命令来运行 SharePoint Yeoman 生成器:

yo @microsoft/sharepoint

使用以下命令完成显示的提示:

  • 解决方案名称是什么?:AceImageViewer
  • 要创建哪种类型的客户端组件?:自适应卡片扩展
  • 要使用哪个模板?:映像卡模板
  • 自适应卡片扩展名称是什么?: AceImageViewer

预配项目所需的文件夹后,生成器将通过自动运行 npm install 来安装所有依赖项包。 在 NPM 完成下载所有依赖项后,在 Visual Studio Code 中打开项目。

向 ACE 组件添加公共属性

你将在本练习中创建的 ACE 组件将检索和显示火星车使用 美国宇航局开放 API 终结点之一拍摄的图像。

若要调用 API,需要设置三个值:

  • API 密钥
  • 火星车检索照片
  • 火星太阳,火星上的太阳日,用于检索图像

所有这些属性都是 ACE 组件上的 公共和可配置属性。

注意

NASA 开放 API 支持使用演示 API 密钥或创建免费 API 密钥。 本练习假定你使用的是演示密钥。

演示密钥具有速率限制,例如一小时内和一天中每个 IP 地址的最大请求数。 如果超出演示 API 密钥限制,可以创建一个链接到电子邮件地址的密钥。 若要了解详细信息,请参阅 NASA 开放 API 站点

首先,将属性添加到 ACE 组件。

  1. 在文件 ./src/adaptiveCardExtensions/aceImageViewer/AceImageViewerAdaptiveCardExtension.ts 中找到 ACE 类,并在 VS Code 中打开它。

  2. 找到 接口并将其 IAceImageViewerAdaptiveCardExtensionProps 更新为包含以下属性:

    export interface IAceImageViewerAdaptiveCardExtensionProps {
      title: string;
      nasa_api_key: string;
      nasa_rover: string;
      mars_sol: number;
    }
    

接下来,将属性添加到属性窗格:

  1. 在文件 ./src/adaptiveCardExtensions/aceImageViewer/AceImageViewerPropertyPane.ts 中找到 类,并在 VS Code 中打开它。

  2. 找到 import 导入 方法的 PropertyPaneTextField 语句。 将此语句添加到 PropertyPaneDropdown 方法 import 的列表。

    import {
      IPropertyPaneConfiguration,
      PropertyPaneTextField,
      PropertyPaneDropdown    // << add
    } from '@microsoft/sp-property-pane';
    
  3. 更新现有 getPropertyPaneConfiguration() 方法以接受指定所选漫游车的单个参数:

    public getPropertyPaneConfiguration(selectedRover: string = 'curiosity'): IPropertyPaneConfiguration { .. }
    
  4. 将以下字段添加到 方法中返回的 对象的 数组groupFieldsgetPropertyPaneConfiguration()

    PropertyPaneTextField('nasa_api_key', {
      label: 'NASA API key'
    }),
    PropertyPaneDropdown('nasa_rover', {
      label: 'NASA Mars rover',
      options: [
        { index: 0, key: 'curiosity', text: 'Curiosity' },
        { index: 1, key: 'opportunity', text: 'Opportunity' },
        { index: 2, key: 'spirit', text: 'Spirit' }
      ],
      selectedKey: selectedRover
    }),
    PropertyPaneTextField('mars_sol', {
      label: 'Display photos from Mars day (Sol)'
    })
    

让我们向属性窗格添加一个小的增强功能:火星车的下拉选择器应默认为当前所选的火星车。 方法的 getPropertyPaneConfiguration() 签名接受可用于设置它的输入参数:

  1. 返回 类AceImageViewerAdaptiveCardExtension并找到 getPropertyPaneConfiguration() 方法。 将现有 return 语句替换为以下内容:

    return this._deferredPropertyPane?.getPropertyPaneConfiguration(this.properties.nasa_rover);
    

最后,在将 ACE 组件添加到页面时,添加属性的一些默认值:

  1. 在文件 ./src/adaptiveCardExtensions/aceImageViewer/AceImageViewerAdaptiveCardExtension.manifest.json 中找到 ACE 类,并在 VS Code 中打开它。

  2. 将以下属性添加到 对象中的 preconfiguredEntries.properties 现有属性列表:

    "nasa_api_key": "DEMO_KEY",
    "nasa_rover": "curiosity",
    "nasa_sol": 1000
    

添加 NASA REST API 服务帮助程序

让我们向项目添加一个服务,以处理从美国宇航局 REST 开放 API 进行的所有读取。

在项目中创建一个新文件 ./src/adaptiveCardExtensions/aceImageViewer/nasa.service.ts ,并向其添加以下代码:

import { AdaptiveCardExtensionContext } from '@microsoft/sp-adaptive-card-extension-base';
import { HttpClient } from '@microsoft/sp-http';

export interface IMarsRoverCamera {
  id: number;
  name: string;
  rover_id: number;
  full_name: string;
}

export interface IMarsRoverVehicle {
  id: number;
  name: string;
  landing_date: Date;
  launch_date: Date;
  status: string;
}

export interface IMarsRoverPhoto {
  id: number;
  sol: number;
  camera: IMarsRoverCamera;
  rover: IMarsRoverVehicle;
  img_src: string;
  earth_date: Date;
}

export const fetchRoverPhotos = async (
  spContext: AdaptiveCardExtensionContext,
  apiKey: string,
  rover: string,
  mars_sol: number): Promise<IMarsRoverPhoto[]> => {
  const results: { photos: IMarsRoverPhoto[] } = await (
    await spContext.httpClient.get(
      `https://api.nasa.gov/mars-photos/api/v1/rovers/${rover}/photos?sol=${mars_sol}&page=1&api_key=${apiKey}`,
      HttpClient.configurations.v1
    )
  ).json();

  return Promise.resolve(results.photos);
}

更新 ACE 组件的状态

创建公共属性和帮助程序服务后,现在让我们更新组件的状态,该状态用于显示组件中的数据。

  1. 在文件 ./src/adaptiveCardExtensions/aceImageViewer/AceImageViewerAdaptiveCardExtension.ts 中找到 ACE 类,并在 VS Code 中打开它。

  2. 在现有 import 语句后面添加以下 import 语句:

    import { isEmpty } from '@microsoft/sp-lodash-subset'
    import {
      fetchRoverPhotos,
      IMarsRoverPhoto
    } from './nasa.service';
    
  3. 找到 接口并将其 IAceImageViewerAdaptiveCardExtensionState 更新为包含以下属性:

    export interface IAceImageViewerAdaptiveCardExtensionState {
      currentIndex: number;
      roverPhotos: IMarsRoverPhoto[];
    }
    
  4. 接下来,更新 onInit() 类中的 AceImageViewerAdaptiveCardExtension 方法,以将这两个属性初始化为空值:

    this.state = {
      currentIndex: 0,
      roverPhotos: []
    };
    
  5. 在 中 onInit(),添加以下代码,以在设置了最小属性的情况下从 NASA API 检索图像。 这应紧挨在现有 return Promise.resolve();

    if (!isEmpty(this.properties.nasa_api_key) &&
        !isEmpty(this.properties.nasa_rover) &&
        !isEmpty(this.properties.mars_sol)){
      this.setState({ roverPhotos: await fetchRoverPhotos(
        this.context,
        this.properties.nasa_api_key,
        this.properties.nasa_rover,
        this.properties.mars_sol)
      });
    }
    
  6. 最后一个语句使用 await 关键字 (keyword) ,因此你需要将async关键字 (keyword) 添加到onInit()方法的声明中:

    public async onInit(): Promise<void> {
    

让我们再处理一个方案:如果用户在属性窗格中更改了选定的 nasa_rovermars_sol ,我们希望更新处于 状态的图像。 为此,请向 类添加以下代码 AceImageViewerAdaptiveCardExtension 。 当属性在属性窗格中发生更改时,它将运行:

protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: any, newValue: any): void {
  if (propertyPath === 'nasa_rover' && newValue !== oldValue) {
    (async () => {
      this.setState({ roverPhotos: await fetchRoverPhotos(
        this.context,
        this.properties.nasa_api_key,
        newValue,
        this.properties.mars_sol)
      });
    })
  }

  if (propertyPath === 'mars_sol' && newValue !== oldValue) {
    (async () => {
      this.setState({ roverPhotos: await fetchRoverPhotos(
        this.context,
        this.properties.nasa_api_key,
        this.properties.nasa_rover,
        newValue)
      });
    })
  }
}

更新 CardView

现在,组件已设置为从 REST API 获取照片并将其存储在状态中,可以更新呈现以显示照片。 首先更新 CardView。

  1. 在 VS Code 中找到并打开 ./src/adaptiveCardExtensions/aceImageViewer/cardView/CardView.ts 文件。

  2. 添加对从包导入的 IActionArguments 接口的 @microsoft/sp-adaptive-card-extension-base 引用:

    import {
      BaseImageCardView,
      IImageCardParameters,
      IExternalLinkCardAction,
      IQuickViewCardAction,
      ICardButton,
      IActionArguments  // << add
    } from '@microsoft/sp-adaptive-card-extension-base';
    
  3. 接下来,更新 CardView 上显示的按钮。 CardView 可以返回零个、一个或两个按钮。 当不在照片集合的开头或末尾时,你想要显示两个按钮(上一个和下一个按钮)。 通过将 访问器方法的内容 cardButtons() 替换为以下代码来添加此项:

    const cardButtons: ICardButton[] = [];
    
    if (this.state.currentIndex !== 0) {
      cardButtons.push(<ICardButton>{
        title: '<',
        id: '-1',
        action: {
          type: 'Submit',
          parameters: {}
        }
      });
    }
    if (this.state.currentIndex !== (this.state.roverPhotos.length - 1)) {
      cardButtons.push(<ICardButton>{
        title: '>',
        id: '1',
        action: {
          type: 'Submit',
          parameters: {}
        }
      });
    }
    
    return (cardButtons.length === 0)
      ? undefined
      : (cardButtons.length === 1)
        ? [cardButtons[0]]
        : [cardButtons[0], cardButtons[1]];
    
  4. 接下来,将 访问器方法的内容 data() 替换为以下代码。 如果未指定火星车或火星索尔,这将返回火星的默认图像,其中包含一些说明。 否则,将显示当前图像:

    if (!this.properties.nasa_rover || !this.properties.mars_sol) {
      return {
        primaryText: `Select Mars rover & sol to display photos...`,
        imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/0e/Tharsis_and_Valles_Marineris_-_Mars_Orbiter_Mission_%2830055660701%29.png/240px-Tharsis_and_Valles_Marineris_-_Mars_Orbiter_Mission_%2830055660701%29.png',
        imageAltText: `Select Mars rover & sol to display photos...`,
        title: this.properties.title
      }
    } else {
      const rover = `${this.properties.nasa_rover.substring(0, 1).toUpperCase()}${this.properties.nasa_rover.substring(1)}`;
      const roverImage = this.state.roverPhotos[this.state.currentIndex];
      if (roverImage) {
        return {
          primaryText: `Photos from the Mars rover ${rover} on sol ${this.properties.mars_sol}`,
          imageUrl: roverImage.img_src,
          imageAltText: `Image ${roverImage.id} taken on ${roverImage.earth_date} from ${rover}'s ${roverImage.camera.full_name} camera.`,
          title: this.properties.title
        };
      } else {
        return {
          primaryText: `Please refresh the page to reload the rover photos`,
          imageUrl: '',
          imageAltText: '',
          title: this.properties.title
        }
      }
    }
    
  5. 接下来,将 访问器方法的内容 onCardSelection() 替换为以下代码。 这将打开 QuickView,在选择卡后,你将在一会儿更新该视图。

    return {
      type: 'QuickView',
      parameters: {
        view: QUICK_VIEW_REGISTRY_ID
      }
    };
    
  6. 接下来,通过将以下代码添加到 类来实现 onAction()CardView 方法。 每当 CardView 的实现中发生提交操作时,都会运行该操作。 回想一下, cardButtons() 在 方法中,将按钮上的 属性设置为 id 正数或负数,以在图像数组中导航:

    public onAction(action: IActionArguments): void {
      if (action.type !== 'Submit') { return; }
    
      let currentIndex = this.state.currentIndex;
      this.setState({ currentIndex: currentIndex + Number(action.id) });
    }
    
  7. 最后,注释掉或删除对 对象的以下引用 strings

    import * as strings from 'AceImageViewerAdaptiveCardExtensionStrings';
    

更新 QuickView

测试 ACE 组件之前的最后一步是更新 QuickView。 在此方案中,QuickView 将显示有关当前照片的更多详细信息。

  1. 在 VS Code 中找到并打开 ./src/adaptiveCardExtensions/aceImageViewer/quickView/QuickView.ts 文件。

  2. 将以下 import 语句添加到现有导入:

    import { IMarsRoverPhoto } from '../nasa.service';
    
  3. 删除现有 IQuickViewData 接口。

  4. 将 类IMarsRoverPhotoQuickView对 的所有剩余引用IQuickViewData替换为 。

  5. 将 访问器方法的内容 data() 替换为以下内容:

    return this.state.roverPhotos[this.state.currentIndex];
    
  6. 最后,注释掉或删除对 对象的以下引用 strings

    import * as strings from 'AceImageViewerAdaptiveCardExtensionStrings';
    

现在,更新自适应卡片模板:

  1. 在 VS Code 中找到并打开 ./src/adaptiveCardExtensions/aceImageViewer/quickView/template/QuickViewTemplate.json 文件。

  2. 将模板的内容替换为以下代码:

    {
      "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
      "version": "1.2",
      "type": "AdaptiveCard",
      "body": [
        {
          "type": "Image",
          "url": "${img_src}"
        },
        {
          "type": "TextBlock",
          "text": "${rover.name} rover image #${id}",
          "horizontalAlignment": "Center"
        },
        {
          "type": "TextBlock",
          "text": "Photo Details",
          "spacing": "Medium",
          "separator": true,
          "size": "Large",
          "weight": "Bolder"
        },
        {
          "type": "FactSet",
          "facts": [
            {
              "title": "Rover:",
              "value": "${rover.name}"
            },
            {
              "title": "Camera:",
              "value": "${camera.full_name}"
            },
            {
              "title": "Date taken:",
              "value": "${earth_date} (sol ${sol})"
            }
          ]
        }
      ]
    }
    

测试动态 ACE

让我们测试 ACE 以查看我们的图像浏览器!

在控制台中,执行以下语句:

gulp serve --nobrowser

在浏览器中,导航到要在其中测试 ACE 的站点中的 SharePoint 托管工作台。 例如,如果网站的 URL 为 https://contoso.sharepoint.com/sites/MSLearningTeam,则托管工作台的 URL 为 https://contoso.sharepoint.com/sites/MSLearningTeam/_layouts/15/workbench.aspx

选择图标, + 然后从工具箱中选择 AceImageViewer

SPFx 工具箱的屏幕截图。

请注意,组件的默认体验是使用火星图像。 那是因为我们没有火星索尔集:

默认 ACE CardView 呈现的屏幕截图。

与 SPFx Web 部件一样,可以将鼠标悬停在 ACE 组件上,然后选择铅笔图标以打开属性窗格:

ACE 的编辑体验的屏幕截图。

将 Mars sol 设置为 1000 并关闭属性窗格,方法是选择右上角的 X ,然后选择页面顶部导航右上角的 “预览” 链接,将页面置于显示模式。

使用 CardView 上提供的按钮滚动浏览图像。 图像的裁剪版本显示在图像卡的卡片视图中

CardView 中所选图像的屏幕截图。

使用鼠标,选择卡上的任意位置,而无需选择任一按钮。 QuickView 将显示未裁剪的照片,其中包含有关拍摄时间的更多详细信息:

QuickView 中所选图像的屏幕截图。

在本练习中,你创建了一个SharePoint 框架 (SPFx) 自适应卡片扩展 (ACE) ,该模板显示所选火星车上某个相机拍摄的图像。

知识检验

1.

开发人员如何处理自适应卡片中提交的操作?

2.

以下哪一个关于 方法的 cardButtons() 语句不正确?

3.

仅当想要将视图链接在一起时,才需要将 CardView 或 QuickView 注册为其关联的视图导航器。