了解和配置页面转换模型

页面转换解决方案的核心是馈送转换的模型:该模型告知引擎哪些 Web 部件属性很重要,允许你操作这些属性并为 Web 部件动态选择映射。 页面转换模型以 XML 表示,并附带用于验证模型正确性的架构。

重要

SharePoint PnP 新式化框架是 PnP 框架的一部分,且在不断演进,请查看发行说明了解最新更改的最新信息。 如果遇到问题,请在 PnP 框架 GitHub 问题列表中提出问题。

页面转换高级别体系结构

下图分 4 步说明页面转换:

  1. 在开始时,需要告诉转换引擎你希望如何转换页面,方法是提供页面转换模型。 此模型是一个 XML 文件,它描述每个经典 Web 部件需要如何映射到新式等效部件。 根据经典 Web 部件,该模型包含相关属性和映射信息的列表。 请参阅了解和配置页面转换模型文章来了解详细信息。 如果你想要了解如何比较经典 Web 部件和新式 Web 部件,建议查看经典和新式 Web 部件体验一文。
  2. 下一步是分析想要转换的页面:转换引擎将页面细分为 Web 部件的集合(Wiki 文本细分为一个或多个 Wiki 文本 Web 部件),并且它将尝试检测使用的布局。
  3. 从步骤 2 中的分析检索到的信息通常不足以将 Web 部件映射到新式等效部件,因此,在步骤 3 中,我们将通过调用函数来增强信息:这些函数采用步骤 2 中检索到的属性,并基于步骤 2 输入的属性生成新的属性。 步骤 3 之后,我们已获得映射 Web 部件所需的所有信息...除了我们(可选)需要调用定义的选择器,以了解在一个经典 Web 部件可以映射到多个新式配置时,我们需要哪个映射。
  4. 最后一步是创建和配置新式页面,然后向其添加映射的新式 Web 部件。

页面转换

页面转换模型结构

打开页面转换模型时,将显示以下顶级元素:

  • BaseWebPart:此元素包含应用到所有 Web 部件的配置,例如,该元素描述将为所有 Web 部件提取的属性“Title”。 它也是定义默认 Web 部件映射的位置:如果 Web 部件未定义映射,引擎将回退到此映射,以表示目标页面上的 Web 部件。

  • AddOns:作为页面转换的用户,可能需要应用自定义逻辑来实现你的需求,例如,你需要以一种可与自定义 SPFX Web 部件配合使用的方式来转换给定属性。 该框架支持此功能,方法是允许向程序集添加函数和选择器...只需在 AddOn 部分定义它们,然后稍后通过给它们加上给定名称作为前缀来引用自定义函数和选择器,将使页面转换使用自定义代码。

  • WebParts:此元素包含要转换的每个 Web 部件的信息。 对于每个 Web 部件,你将找到要使用的属性的定义、要在这些属性上执行的函数、定义转换目标的可能映射以及动态选择所需映射的选择器。

接下来的章节将提供更多详细信息。

页面转换模型中的 WebPart 定义

让我们分析如何在页面转换模型中配置 Web 部件,最好根据 XsltListViewWebPart 定义的简化示例进行此配置:

      <!-- XsltListView web part -->
      <WebPart Type="Microsoft.SharePoint.WebPartPages.XsltListViewWebPart, Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c">
        <Properties>
          <Property Name="XmlDefinitionLink" Type="string" />
          <Property Name="ListUrl" Type="string" />
          <Property Name="ListId" Type="guid" Functions="{ListWebRelativeUrl} = ListAddWebRelativeUrl({ListId}); {ListServerRelativeUrl} = ListAddServerRelativeUrl({ListId})"/>
          <Property Name="Direction" Type="string"/>
          <Property Name="GhostedXslLink" Type="string" />
          <Property Name="DisableViewSelectorMenu" Type="bool"/>
          <Property Name="XmlDefinition" Type="string" Functions="{ListViewId} = ListDetectUsedView({ListId},{XmlDefinition})"/>
          <Property Name="SelectParameters" Type="string"/>
        </Properties>
        <!-- This selector outputs: Library, List  -->
        <Mappings Selector="ListSelectorListLibrary({ListId})">
          <Mapping Name="List" Default="true">
            <ClientSideText Text="You can map a source web part ({Title}) to a combination of modern web parts :-)" Order="10" />
            <ClientSideWebPart Type="List" Order="20" JsonControlData="&#123;&quot;serverProcessedContent&quot;&#58;&#123;&quot;htmlStrings&quot;&#58;&#123;&#125;,&quot;searchablePlainTexts&quot;&#58;&#123;&#125;,&quot;imageSources&quot;&#58;&#123;&#125;,&quot;links&quot;&#58;&#123;&#125;&#125;,&quot;dataVersion&quot;&#58;&quot;1.0&quot;,&quot;properties&quot;&#58;&#123;&quot;isDocumentLibrary&quot;&#58;false,&quot;selectedListId&quot;&#58;&quot;{ListId}&quot;,&quot;listTitle&quot;&#58;&quot;{Title}&quot;,&quot;selectedListUrl&quot;&#58;&quot;{ListServerRelativeUrl}&quot;,&quot;webRelativeListUrl&quot;&#58;&quot;{ListWebRelativeUrl}&quot;,&quot;webpartHeightKey&quot;&#58;4,&quot;selectedViewId&quot;&#58;&quot;{ListViewId}&quot;&#125;&#125;" />
          </Mapping>
          <Mapping Name="Library" Default="false">
            <ClientSideWebPart Type="List" Order="10" JsonControlData="&#123;&quot;serverProcessedContent&quot;&#58;&#123;&quot;htmlStrings&quot;&#58;&#123;&#125;,&quot;searchablePlainTexts&quot;&#58;&#123;&#125;,&quot;imageSources&quot;&#58;&#123;&#125;,&quot;links&quot;&#58;&#123;&#125;&#125;,&quot;dataVersion&quot;&#58;&quot;1.0&quot;,&quot;properties&quot;&#58;&#123;&quot;isDocumentLibrary&quot;&#58;true,&quot;selectedListId&quot;&#58;&quot;{ListId}&quot;,&quot;listTitle&quot;&#58;&quot;{Title}&quot;,&quot;selectedListUrl&quot;&#58;&quot;{ListServerRelativeUrl}&quot;,&quot;webRelativeListUrl&quot;&#58;&quot;{ListWebRelativeUrl}&quot;,&quot;webpartHeightKey&quot;&#58;4,&quot;selectedViewId&quot;&#58;&quot;{ListViewId}&quot;&#125;&#125;" />
          </Mapping>
        </Mappings>
      </WebPart>

Properties 元素

对于每个 Web 部件,模型定义可能对配置目标新式 Web 部件 () 有用的属性。 如果遗漏了某些属性,只需在此模型中扩展它们。 在某些属性上,你将看到 Functions 属性:此属性包含一个或多个函数 (单独的函数,这些函数通过 ;) 在源 Web 部件映射到目标新式 Web 部件时执行。 函数的解析如下:

{Output} = FunctionName({Input1}, {Input2})

函数可以有一个或多个输入值,这些值可以是:

  • 此 Web 部件上定义的属性(例如,{ListId})
  • 基本 Web 部件上定义的属性(例如,{Title})
  • 作为前一个函数执行的输出的属性(例如,{ListWebRelativeUrl})
  • 默认网站范围内的属性:{Host}、{Web}、{Site}、{WebId}、{SiteId}

当函数运行时,其输出将为:

  • 单个字符串值:此值(提供模型中的 {Output})将添加到名为“Output”的 Web 部件属性的列表中,并为从运行 FunctionName 返回的值赋值。
  • 字典字符串、string>) (<键/值对的列表:在这种情况下,每个返回的键/值对都添加到 Web 部件属性列表

如果该函数未定义输出参数,则函数结果将覆盖定义该函数的 Web 部件属性的值。

让我们用一个示例来阐明:

<Property Name="ListId" Type="guid" Functions="FormatGuid({ListId})"/>

假设 Web 部件属性最初包含格式类似于 {AAFAD7D0-D57A-4BB1-8706-969A608C686B} 的 guid。 执行 FormatGuid 后,会将该值设置为 FormatGuid 的输出(例如,不带括号的 guid AAFAD7D0-D57A-4BB1-8706-969A608C686B)。

如果函数返回多个值,则已经作为 Web 部件属性存在的每个返回的键/值对将覆盖该属性值。

注意

页面转换功能和选择器中描述了所有开箱即用功能

Mappings 元素

此元素定义给定源 Web 部件的一个或多个可能目标配置。 由于可以定义多个目标,因此需要一个机制来确定要使用的映射:

  • 如果映射元素包含填充的选择器属性,则使用选择器执行的输出按名称查找正确的映射(例如,选择器函数 ListSelectorListLibrary 返回字符串 “Library”,这样将导致使用名为“Library”的库)。 选择器与返回单个值的函数相同,因此,可以将任意 Web 部件属性指定为选择器函数的输入
  • 如果只有一个映射且没有选择器结果,则采用该映射
  • 如果没有选择器结果并且定义了多个映射,则采用标记为默认的映射

注意

页面转换功能和选择器中描述了所有开箱即用选择器

接下来介绍 Mapping 元素本身。

Mapping 元素

在 Mapping 元素中,可以有一个或多个 ClientSideText 或 ClientSideWebPart 元素,如以下代码片段所示。 请注意,可以在映射上运行函数,这在仅当选择了特定映射时才需要执行处理时使用。

<Mapping Name="List" Default="true" Functions="{SampleVariable} = SampleFunction({ListId})>
  <ClientSideText Text="You can map a source web part ({Title}) to a combination of modern web parts :-)" Order="10" />
  <ClientSideWebPart Type="List" Order="20" JsonControlData="&#123;&quot;serverProcessedContent&quot;&#58;&#123;&quot;htmlStrings&quot;&#58;&#123;&#125;,&quot;searchablePlainTexts&quot;&#58;&#123;&#125;,&quot;imageSources&quot;&#58;&#123;&#125;,&quot;links&quot;&#58;&#123;&#125;&#125;,&quot;dataVersion&quot;&#58;&quot;1.0&quot;,&quot;properties&quot;&#58;&#123;&quot;isDocumentLibrary&quot;&#58;false,&quot;selectedListId&quot;&#58;&quot;{ListId}&quot;,&quot;listTitle&quot;&#58;&quot;{Title}&quot;,&quot;selectedListUrl&quot;&#58;&quot;{ListServerRelativeUrl}&quot;,&quot;webRelativeListUrl&quot;&#58;&quot;{ListWebRelativeUrl}&quot;,&quot;webpartHeightKey&quot;&#58;4,&quot;selectedViewId&quot;&#58;&quot;{ListViewId}&quot;&#125;&#125;" />
</Mapping>

在上面的示例中,源 XSLTListView Web 部件映射到新式文本部件 () ClientSideText 和新式 Web 部件 (ClientSideWebPart) :

  • 使用 Order 属性确定先映射到哪一个部件
  • 对于 ClientSideWebPart,需要指定 Web 部件的 Type,如果选择 Custom 作为类型,则还需要指定 ControlId 属性来标识所需的自定义 Web 部件
  • TextJsonControlDataControlId 属性可以包含采用 {Token} 格式的令牌,在运行时这些令牌将替换为实际值。 如前所述,每个定义的令牌都必须能够作为 Web 部件属性或函数结果

页面转换模型中的 AddOns 定义

Add-ons 可使你通过执行以下 2 个步骤将自定义逻辑插入到映射模型:

  • 创建托管自定义函数/选择器的自定义程序集
  • 在 AddOns 元素中声明此自定义程序集

创建自定义函数/选择器程序集

若要创建自己的函数,将需要在项目中引用 SharePoint.Modernization.Framework 程序集,然后创建继承 SharePointPnP.Modernization.Framework.Functions.FunctionsBase 类的类:

using Microsoft.SharePoint.Client;
using SharePointPnP.Modernization.Framework.Functions;
using System;

namespace Contoso.Modernization
{
    public class MyCustomFunctions: FunctionsBase
    {
        public MyCustomFunctions(ClientContext clientContext) : base(clientContext)
        {
        }

        public string MyListAddServerRelativeUrl(Guid listId)
        {
            if (listId == Guid.Empty)
            {
                return "";
            }
            else
            {
                var list = this.clientContext.Web.GetListById(listId);
                list.EnsureProperties(p => p.RootFolder.ServerRelativeUrl);
                return list.RootFolder.ServerRelativeUrl;
            }
        }

    }
}

声明自定义程序集

在可以使用自定义函数之前,需要在模型中声明它们,方法是将每个库/类的一个引用添加到 AddOns 元素中:

<AddOn Name="Custom" Type="Contoso.Modernization.MyCustomFunctions" Assembly="Contoso.Modernization.dll" />

或(注意完全限定的路径)

<AddOn Name="Custom" Type="Contoso.Modernization.MyCustomFunctions" Assembly="c:\transform\Contoso.Modernization.dll" />

请注意,每个声明都具有一个名称,在上述示例中,都拥有名称“Custom”。

使用自定义函数/选择器

现在已经定义了程序集,可以使用函数和选择器按照名称来引用它们,如下面示例中所示的“Custom”前缀:

<Property Name="ListId" Type="guid" Functions="{ListServerRelativeUrl} = Custom.MyListAddServerRelativeUrl({ListId})"/>