Compartilhar via


Transformar o suplemento do SharePoint App Parts e web parts clássicos em web parts Estrutura do SharePoint web parts

No modelo de Suplemento do SharePoint, você está acostumado a criar web parts personalizadas para páginas clássicas do SharePoint criando App Parts. No novo Estrutura do SharePoint, você pode obter o mesmo resultado para páginas clássicas e modernas criando web parts do lado do cliente.

Neste artigo, você pode encontrar informações detalhadas sobre como transformar uma Parte de Aplicativo já existente em uma Web Part moderna do lado do cliente.

Se preferir, você pode watch o vídeo a seguir, em vez de ler o artigo inteiro, que ainda pode considerar como uma referência muito mais detalhada.

Transformar o suplemento do SharePoint App Parts e web parts clássicos em web parts Estrutura do SharePoint web parts

Observação

Você pode encontrar mais detalhes sobre como criar web parts Estrutura do SharePoint lendo o documento Compilar sua primeira web part do lado do cliente do SharePoint.

Transformando uma Parte do Aplicativo em uma Web Part do lado do cliente

Imagine que você tenha uma Parte do Aplicativo para renderizar a lista de documentos na biblioteca "Documentos Compartilhados" de um site. A Parte do Aplicativo é configurável e permite definir um filtro de pesquisa em itens. Na captura de tela a seguir, você pode ver a interface do usuário da Parte do Aplicativo.

A interface do usuário da Parte de Aplicativo, mostrando uma lista de documentos recuperados da biblioteca

Agora você deseja transformar a Parte do Aplicativo em uma web part moderna do lado do cliente criada com Estrutura do SharePoint.

A solução de modelo de suplemento do SharePoint para migrar

No trecho de código a seguir, você pode ver a implementação real do código JavaScript da Parte de Aplicativo.

var hostweburl;
var appweburl;
var clientContext;
var hostweb;
var documentsLibrary;
var docs;

// This code runs when the DOM is ready and creates a context object which is
// needed to use the SharePoint object model
$(document).ready(function () {
    hostweburl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
    appweburl = decodeURIComponent(getQueryStringParameter("SPAppWebUrl"));
    searchfilter = decodeURIComponent(getQueryStringParameter("SearchFilter"));

    clientContext = new SP.ClientContext.get_current();
    hostWebContext = new SP.AppContextSite(clientContext, hostweburl);
    hostweb = hostWebContext.get_web();
    listDocuments(searchfilter);
});

// This function retrieves the documents in the "Shared Documents" library of the parent site
function listDocuments(searchfilter) {
    documentsLibrary = hostweb.get_lists().getByTitle("Documents");
    if (searchfilter === undefined || searchfilter === '') {
        var camlQuery = SP.CamlQuery.createAllItemsQuery();
        docs = documentsLibrary.getItems(camlQuery);
    } else {
        var camlQuery = new SP.CamlQuery();
        var q = '<View><Query><Where><Contains><FieldRef ' +
            'Name="Title" /><Value Type="Text">' + searchfilter +
            '</Value></Contains></Where></Query></View>';
        camlQuery.set_viewXml(q);

        docs = documentsLibrary.getItems(camlQuery);
    }

    clientContext.load(docs);
    clientContext.executeQueryAsync(onListDocumentsSucceded, onListDocumentsFailed);
}

// In case of successful retrieval of the docs
function onListDocumentsSucceded(sender, args) {
    $("#listDocuments").empty();

    if (docs.get_count() > 0) {
        var docsEnumerator = docs.getEnumerator();

        $("#listDocuments").append('<ul>');
        while (docsEnumerator.moveNext()) {
            var doc = docsEnumerator.get_current();

            var docId = doc.get_item("ID");
            var docServerRedirectedEmbedUri = doc.get_serverRedirectedEmbedUri();
            var docTitle = doc.get_item("Title");

            $("#listDocuments").append('<li><a href="' + docServerRedirectedEmbedUri + '">[' + docId + '] ' + docTitle + '</a></li>');
        }
        $("#listDocuments").append('</ul>');
    }
}

// In case of failed retrieval of the docs
function onListDocumentsFailed(sender, args) {
    alert('Request failed ' + args.get_message() + '\n' + args.get_stackTrace());
}

function getQueryStringParameter(paramToRetrieve) {
    var params =
        document.URL.split("?")[1].split("&");
    var strParams = "";
    for (var i = 0; i < params.length; i = i + 1) {
        var singleParam = params[i].split("=");
        if (singleParam[0] == paramToRetrieve)
            return singleParam[1];
    }
}

Como você pode ver, o código é totalmente baseado no código do lado do cliente (JavaScript) e conta com o Modelo de Objeto JavaScript do SharePoint para recuperar uma referência ao site atual, para a biblioteca de "Documentos Compartilhados" e para consultar seus documentos com base no filtro selecionado pelo usuário.

A Parte do Aplicativo é definida em um arquivo de manifesto XML, onde também é definido o parâmetro "Filtro de Pesquisa".

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <ClientWebPart Name="ListDocumentsWebPart" Title="ListDocumentsWebPart Title" Description="ListDocumentsWebPart Description" DefaultWidth="300" DefaultHeight="200">

    <!-- Content element identifies the location of the page that will render inside the client web part
         Properties are referenced on the query string using the pattern _propertyName_
         Example: Src="~appWebUrl/Pages/ClientWebPart1.aspx?Property1=_property1_" -->
    <Content Type="html" Src="~appWebUrl/Pages/ListDocumentsWebPart.aspx?{StandardTokens}&amp;SearchFilter=_SearchFilter_" />

    <!-- Define properties in the Properties element.
         Remember to put Property Name on the Src attribute of the Content element above. -->
    <Properties>
      <Property xmlns="http://schemas.microsoft.com/sharepoint/"
        Name="SearchFilter"
        DefaultValue=""
        Multilingual="true"
        PersonalizationScope="user"
        PersonalizableIsSensitive="true"
        Type="string"
        RequiresDesignerPermission="false"
        WebBrowsable= "true"
        WebCategory="Search"
        WebDescription="Define a custom search filter"
        WebDisplayName="Search filter">
      </Property>
    </Properties>

  </ClientWebPart>
</Elements>

Para funcionar, a solução de modelo de suplemento do SharePoint requer a permissão de leitura para bibliotecas (BaseTemplate=101) definida em seu arquivoAppManifest.xml . Na captura de tela a seguir, você pode ver a configuração do arquivo AppManifest.xml.

As solicitações de permissões configuradas para a solução de modelo de suplemento do SharePoint. Há uma permissão para escopo

Criando uma solução Estrutura do SharePoint

Agora vamos criar uma nova solução Estrutura do SharePoint que você usa para transformar a solução de modelo de suplemento do SharePoint anterior.

Em primeiro lugar, você precisa fazer o scaffold da solução Estrutura do SharePoint, portanto, inicie um prompt de comando ou uma janela de terminal, crie uma pasta e, de dentro da pasta recém-criada, execute o comando a seguir.

Importante

Para poder seguir o procedimento ilustrado, você precisa ter Estrutura do SharePoint instalados em seu ambiente de desenvolvimento. Você pode encontrar instruções detalhadas sobre como configurar seu ambiente lendo o documento Configurar seu ambiente de desenvolvimento Estrutura do SharePoint.

yo @microsoft/sharepoint

A interface do usuário da ferramenta de scaffolding em uma janela do PowerShell, ao mesmo tempo em que cria um novo projeto para uma web part Estrutura do SharePoint moderna.

Siga os prompts para criar uma solução para uma Web Part moderna. Especificamente, faça as seguintes opções quando solicitado pela ferramenta:

  • Qual é o nome da sua solução? spo-sp-fx-web-part
  • Que tipo de componente do cliente será criado? WebPart
  • Qual é o nome da Web Part? ListDocuments
  • Qual modelo você deseja usar? React

Com as respostas acima, você decidiu criar uma solução com nome spo-sp-fx-web-part, na qual há uma Web Part com o nome ListDocuments e que se baseia em React para renderizar sua Experiência de Usuário.

A ferramenta de scaffolding gera para você uma nova solução de Estrutura do SharePoint. Quando terminar, você pode abrir a pasta atual usando seu editor de código favorito. No entanto, antes de abrir a solução, você precisa adicionar um pacote para ter uma renderização fácil e mais bonita da lista de arquivos na biblioteca de destino. Na verdade, você vai referenciar a biblioteca de componentes do Microsoft Graph Toolkit (MGT) executando o seguinte comando:

npm install @microsoft/mgt-spfx @microsoft/mgt-react --save

Observação

O Microsoft Graph Toolkit é um conjunto de componentes para acelerar a renderização da interface do usuário de suas soluções do lado do cliente, incluindo Estrutura do SharePoint soluções. Não é obrigatório usá-la nesta solução de exemplo, mas é uma maneira fácil de acelerar seu processo de aprendizado e desenvolvimento. Você pode encontrar informações detalhadas sobre o MGT lendo a visão geral do Kit de Ferramentas do Microsoft Graph e aprender a integrar o MGT ao Estrutura do SharePoint lendo o documento Estrutura do SharePoint biblioteca para o Microsoft Graph Toolkit.

Agora você pode abrir a solução no editor de código favorito. Se o editor de código favorito for o Microsoft Visual Studio Code, execute o seguinte comando:

code .

Na imagem a seguir, você pode ver o contorno da solução de Estrutura do SharePoint gerada.

O esboço da solução gerada Estrutura do SharePoint.

O arquivo main, a ser iniciado é o ListDocumentsWebPart.ts, na pasta src\webparts\listDocuments. O arquivo é baseado na sintaxe TypeScript, que é a usada por Estrutura do SharePoint.


// Here we intentionally removed some of the auto-generated code, for the sake of simplicity ...

export interface IListDocumentsWebPartProps {
  description: string;
}

export default class ListDocumentsWebPart extends BaseClientSideWebPart<IListDocumentsWebPartProps> {

  private _isDarkTheme: boolean = false;
  private _environmentMessage: string = '';

  public render(): void {
    const element: React.ReactElement<IListDocumentsProps> = React.createElement(
      ListDocuments,
      {
        description: this.properties.description,
        isDarkTheme: this._isDarkTheme,
        environmentMessage: this._environmentMessage,
        hasTeamsContext: !!this.context.sdks.microsoftTeams,
        userDisplayName: this.context.pageContext.user.displayName
      }
    );

    ReactDom.render(element, this.domElement);
  }

  protected onInit(): Promise<void> {
    return this._getEnvironmentMessage().then(message => {
      this._environmentMessage = message;
    });
  }

  // Here we intentionally removed some of the auto-generated code, for the sake of simplicity ...

  protected onDispose(): void {
    ReactDom.unmountComponentAtNode(this.domElement);
  }

  protected get dataVersion(): Version {
    return Version.parse('1.0');
  }

  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: {
            description: strings.PropertyPaneDescription
          },
          groups: [
            {
              groupName: strings.BasicGroupName,
              groupFields: [
                PropertyPaneTextField('description', {
                  label: strings.DescriptionFieldLabel
                })
              ]
            }
          ]
        }
      ]
    };
  }
}

O trecho de código ilustrado acima mostra os trechos de código main do arquivo ListDocumentsWebPart.ts.

Em primeiro lugar, você pode notar que a Web Part é declarada como uma classe TypeScript com o nome ListDocumentsWebPart, que herda do tipo BaseClientSideWebPart<IListDocumentsWebPartProps>base .

O tipo BaseClientSideWebPart é fornecido pelas bibliotecas base de Estrutura do SharePoint, enquanto a interface IListDocumentsWebPartProps é definida pouco antes da declaração da classe web part e define as propriedades de configuração para sua Web Part personalizada.

O que costumava ser a lista de <Property /> elementos no arquivo de manifesto XML de uma solução de modelo de suplemento do SharePoint, agora é uma lista de propriedades definidas em uma interface TypeScript.

O painel de propriedades para configurar as propriedades da Web Part é renderizado graças ao método getPropertyPaneConfiguration , que renderiza um campo para cada propriedade. A renderização dos campos depende também de cadeias de caracteres de recurso definidas em arquivos externos declarados dentro da solução Estrutura do SharePoint, na pasta src\webparts\listDocuments\loc. O idioma padrão gerado pela ferramenta de scaffolding é o inglês dos EUA (en-us).

Como tal, se você quiser fornecer a mesma experiência de filtragem de documentos da Parte de Aplicativo, poderá substituir a definição de interface pela seguinte:

export interface IListDocumentsWebPartProps {
  searchFilter: string;
}

Em seguida, você também precisará atualizar a implementação do método getPropertyPaneConfiguration , como no seguinte trecho de código:

  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: {
            description: strings.PropertyPaneDescription
          },
          groups: [
            {
              groupName: strings.BasicGroupName,
              groupFields: [
                PropertyPaneTextField('searchFilter', {
                  label: strings.SearchFilterFieldLabel
                })
              ]
            }
          ]
        }
      ]
    };
  }

Observe que o PropertyPaneTextField objeto no groupFields foi atualizado para lidar com a nova propriedade searchFilter . Há também uma nova cadeia de recursos para o rótulo do campo, que foi criado no arquivo src\webparts\listDocuments\loc\mystrings.d.ts e cujo valor está configurado no arquivo de recurso src\webparts\listDocuments\loc\en-us.js.

Outra parte interessante do código da Web Part é o método renderizado, em que a solução scaffolded simplesmente cria uma instância de um componente React que é definido na pasta src\webparts\listDocuments\components. Como você pode ver, o componente React recebe um conjunto de propriedades como argumentos de entrada para configurar seu comportamento.

Como você substituiu a propriedade de descrição na interface IListDocumentsWebPartProps , você também precisa atualizar o método renderizado de acordo, como você pode ver no trecho de código a seguir. Além disso, para poder consultar a lista de arquivos usando o Microsoft Graph, o método renderiza o componente React também a ID do Site atual, a ID da Web e o nome do locatário.

public render(): void {
const element: React.ReactElement<IListDocumentsProps> = React.createElement(
    ListDocuments,
    {
    searchFilter: this.properties.searchFilter,
    tenantName: this.context.pageContext.site.absoluteUrl.substring(8,
        this.context.pageContext.site.absoluteUrl.indexOf('/', 8)),
    siteId: this.context.pageContext.site.id.toString(),
    webId: this.context.pageContext.web.id.toString(),
    isDarkTheme: this._isDarkTheme,
    environmentMessage: this._environmentMessage,
    hasTeamsContext: !!this.context.sdks.microsoftTeams,
    userDisplayName: this.context.pageContext.user.displayName
    }
);

ReactDom.render(element, this.domElement);
}

Mais uma coisa que você precisa fazer no código da Web Part é inicializar a biblioteca MGT. Em primeiro lugar, você precisará referenciar a biblioteca MGT no arquivo de código, adicionando a linha a seguir na seção superior do arquivo, logo após todas as instruções de importação .

import { Providers, SharePointProvider } from '@microsoft/mgt-spfx';

Em seguida, substitua o método onInit da Web Part pelo seguinte trecho de código.

protected onInit(): Promise<void> {

if (!Providers.globalProvider) {
    Providers.globalProvider = new SharePointProvider(this.context);
}

return this._getEnvironmentMessage().then(message => {
    this._environmentMessage = message;
});
}

Como você pode ver, o novo método onInit depende Providers.globalProvider para definir uma instância do SharePointProvider MGT que você referenciou antes. O resultado da sintaxe acima é que o MGT será inicializado e pronto para usar o contexto de segurança Estrutura do SharePoint para consumir o Microsoft Graph.

Agora, para que seu código funcione, você precisa atualizar o componente React para dar suporte à nova propriedade searchFilter e às propriedades tenantName, siteId e WebId. Abra o arquivo src\webparts\listDocuments\components\IListDocumentsProps.ts e substitua a propriedade de descrição pelo searchFilter um. Em seguida, adicione três novas propriedades chamadas tenantName, siteId e webId como ela é ilustrada no trecho de código a seguir.

export interface IListDocumentsProps {
  searchFilter: string;
  tenantName: string;
  siteId: string;
  webId: string;
  isDarkTheme: boolean;
  environmentMessage: string;
  hasTeamsContext: boolean;
  userDisplayName: string;
}

Agora abra o arquivo src\webparts\listDocuments\components\ListDocuments.tsx, que representa o componente React que renderiza a Experiência do Usuário da Web Part moderna. Importe o componente FileList da biblioteca MGT e atualize o método renderizado para substituir a propriedade de descrição pelo novo searchFilter um.

Observação

Você pode encontrar mais informações sobre o componente FileList lendo o componente da lista de arquivos do documento no Microsoft Graph Toolkit.

Por fim, substitua todo o valor retornado do método renderizado, a fim de mostrar o valor da propriedade searchFilter e a lista real de arquivos usando o componente MgtFileList .

import * as React from 'react';
import styles from './ListDocuments.module.scss';
import { IListDocumentsProps } from './IListDocumentsProps';
import { escape } from '@microsoft/sp-lodash-subset';
import { FileList } from '@microsoft/mgt-react/dist/es6/spfx';

export default class ListDocuments extends React.Component<IListDocumentsProps, {}> {
  public render(): React.ReactElement<IListDocumentsProps> {
    const {
      searchFilter,
      tenantName,
      siteId,
      webId,
      isDarkTheme,
      environmentMessage,
      hasTeamsContext,
      userDisplayName
    } = this.props;

    // If we have a value for searchFilter, let's use it, otherwise get the whole list of files
    const fileListQuery: string = searchFilter ?
      `/sites/${tenantName},${siteId},${webId}/drive/root/search(q='${escape(searchFilter)}')` :
      `/sites/${tenantName},${siteId},${webId}/drive/root/children`;

    return (
      <section className={`${styles.listDocuments} ${hasTeamsContext ? styles.teams : ''}`}>
        <div className={styles.welcome}>
          <img alt="" src={isDarkTheme ? require('../assets/welcome-dark.png') : require('../assets/welcome-light.png')} className={styles.welcomeImage} />
          <h2>Well done, {escape(userDisplayName)}!</h2>
          <div>{environmentMessage}</div>
          <div>Current search filter: <strong>{escape(searchFilter)}</strong></div>
        </div>
        <div>
          <FileList fileListQuery={fileListQuery} />
        </div>
      </section>
    );
  }
}

Como você pode ver, o código cria dinamicamente a URL de uma consulta do Microsoft Graph para recuperar a lista de arquivos na pasta "Documentos Compartilhados" do site atual. Caso haja um valor para a propriedade searchFilter , ela depende de uma consulta de pesquisa. Se não houver valor para a propriedade searchFilter, ela simplesmente recuperará toda a lista de arquivos.

Em seguida, dentro da instrução de retorno do método renderizado, há uma instância do FileList componente React do MGT, para renderizar a lista real de arquivos, fornecendo a consulta dinâmica como o valor da propriedade fileListQuery.

Assim como ocorreu com o modelo de suplemento App Part, também em Estrutura do SharePoint você precisa configurar as permissões necessárias pela web part para consumir o Microsoft Graph. Você pode fazer isso editando o arquivo /config/package-solution.json e criando uma seção WebApiPermissionRequests , como no trecho de código a seguir.

{
  "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
  "solution": {
    "name": "spo-sp-fx-web-part-client-side-solution",
    "id": "06b2a772-deaa-4b4b-855b-e50bd8e935f0",
    "version": "1.0.0.0",
    "includeClientSideAssets": true,
    "skipFeatureDeployment": true,
    "isDomainIsolated": false,
    "developer": {
      "name": "",
      "websiteUrl": "",
      "privacyUrl": "",
      "termsOfUseUrl": "",
      "mpnId": "Undefined-1.16.1"
    },
    "webApiPermissionRequests": [
      {
        "resource": "Microsoft Graph",
        "scope": "Files.Read"
      }
    ],

Depois de terminar suas alterações, você pode criar a solução Estrutura do SharePoint e executá-la em depuração. Para fazer isso, você precisa atualizar o conteúdo do arquivo /config/serve.json para direcionar seu site online do SharePoint real onde deseja testar a Web Part.

{
  "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json",
  "port": 4321,
  "https": true,
  "initialPage": "https://enter-your-SharePoint-site/_layouts/workbench.aspx"
}

Substitua o valor da propriedade InitialPage pela URL da coleção de sites de destino.

Em seguida, você pode executar o seguinte comando na janela do terminal:

gulp serve

Uma janela do navegador é iniciada e você vê o Estrutura do SharePoint Workbench, que é uma página fornecida pelo SharePoint Online para depurar seus componentes Estrutura do SharePoint. Selecione o botão Adicionar e escolha adicionar a Web Part listDocuments personalizada à página.

A interface do usuário de Estrutura do SharePoint Workbench para testar Estrutura do SharePoint componentes. A imagem mostra como adicionar uma web part personalizada ao workbench.

Você vê a saída a seguir.

A interface do usuário de Estrutura do SharePoint Workbench para testar Estrutura do SharePoint componentes. A imagem mostra a Web Part renderizada.

Se você selecionar no lápis, ao lado da Web Part, poderá mostrar o painel de propriedades e configurar um filtro de pesquisa, que é aplicado à lista de arquivos renderizados pelo controle FileList .

Você pode encontrar informações adicionais sobre este tópico lendo os seguintes documentos: