Criar sua primeira extensão do Personalizador de Formulários
Os personalizadores de formulário são Estrutura do SharePoint componentes que lhe dão a opção de substituir a experiência do formulário em um nível de lista ou biblioteca associando o componente ao tipo de conteúdo usado. Os componentes do personalizador de formulário podem ser usados no SharePoint Online e você os cria usando ferramentas e bibliotecas javaScript modernas.
Importante
O personalizador de formulários foi lançado como parte do Estrutura do SharePoint 1.15, portanto, verifique se você está usando a versão certa em seu ambiente. Consulte notas de versão v1.15 para obter detalhes.
Dica
Você pode encontrar a saída deste tutorial no GitHub.
Criar um projeto de extensão
Crie um novo diretório de projeto em seu local favorito.
md form-customizer
Vá para o diretório do projeto.
cd form-customizer
Crie uma nova extensão HelloWorld executando o gerador Yeoman do SharePoint.
yo @microsoft/sharepoint
Quando solicitado, insira os seguintes valores (selecione a opção padrão para todos os avisos omitidos abaixo):
- Qual é o nome da sua solução?: personalizador de formulários
- Que tipo de componente do lado do cliente deve ser criado?: Extensão
- Que tipo de extensão do lado do cliente criar? Personalizador de Formulários
- Qual é o nome do Personalizador de Formulários? HelloWorld
- Qual modelo você gostaria de usar?: Nenhuma Estrutura JavaScript
Neste ponto, o Yeoman instala as dependências necessárias e manterá a estrutura dos arquivos da solução juntamente com a extensão HelloWorld. Isso pode levar alguns minutos.
Digite o seguinte no console para iniciar o Visual Studio Code.
code .
Observação
Como a solução do lado do cliente do SharePoint é baseada em HTML/TypeScript, você pode usar qualquer editor de código que dê suporte ao desenvolvimento do lado do cliente para compilar sua extensão.
Abra o arquivo ./src/extensions/helloWorld/HelloWorldFormCustomizer.manifest.json .
Este arquivo define seu tipo de extensão e um identificador
id
exclusivo para sua extensão que pode ser usado para definir para ser usado no nível de tipo de conteúdo para habilitar uma renderização personalizada com esse componente.
Codificar seu Personalizador de Formulários
Abra o arquivo ./src/extensions/helloWorld/HelloWorldFormCustomizer.ts .
Observe que a classe base do Personalizador de Formulários é importada do pacote sp-listview-extensibility, que contém Estrutura do SharePoint código exigido pelo Personalizador de Formulários.
import { Log } from '@microsoft/sp-core-library';
import {
BaseFormCustomizer
} from '@microsoft/sp-listview-extensibility';
A lógica do Personalizador de Formulário está contida nos onInit()
métodos , render()
e onDispose()
.
onInit()
é onde você executará a configuração necessária para sua extensão. Esse evento ocorre apósthis.context
ethis.properties
serem atribuídos, mas antes que a página DOM esteja pronta. Assim como acontece com web parts,onInit()
retorna uma promessa que você pode usar para fazer operações assíncronas;render()
não é chamada até que sua promessa seja resolvida. Se você não precisar dele, basta retornarPromise.resolve<void>();
.render()
ocorre quando o componente é renderizado. Ele fornece um elemento HTMLevent.domElement
onde seu código pode escrever seu conteúdo.onDispose()
ocorre imediatamente antes que o elemento host de formulário seja excluído. Ele pode ser usado para liberar todos os recursos que foram alocados durante a renderização de formulários. Por exemplo, serender()
tiver mondado um elemento React,onDispose()
deverá ser usado para liberá-lo; caso contrário, ocorrerá uma perda de recursos.
Veja a seguir o conteúdo de render()
e onDispose()
na solução padrão:
public render(): void {
// Use this method to perform your custom rendering.
this.domElement.innerHTML = `<div class="${ styles.helloWorld }"></div>`;
}
public onDispose(): void {
// This method should be used to free any resources that were allocated during rendering.
super.onDispose();
}
Como por padrão, o componente personalizado do formulário não renderiza nenhuma informação, vamos atualizar o método de renderização da seguinte maneira.
public render(): void {
// Use this method to perform your custom rendering.
this.domElement.innerHTML = `<div class="${ styles.helloWorld }"><h1>Super cool custom form component</h1></div>`;
}
Depurar seu Personalizador de Formulários
Você pode testar e depurar seu Personalizador de Formulários em um site do SharePoint Online ao vivo. Você não precisa implantar suas personalizações no catálogo de aplicativos de locatário para fazer isso, o que torna a experiência de depuração simples e eficiente.
Para testar sua extensão, primeiro você precisará criar uma lista para testar o personalizador. Portanto, mova-se para o site em seu locatário do SharePoint Online, onde deseja testar o personalizador de formulários.
Na barra de ferramentas, selecione Novo e então selecione Lista.
Escolha Lista em branco na nova experiência de criação de lista
Crie uma nova lista chamada Business e selecione Criar.
Em Visual Studio Code, abra o arquivo ./config/serve.json.
Atualize os
pageUrl
atributos para corresponder a uma URL da lista que criamos nas etapas de visualização. Após as alterações, seu serve.json deve se parecer com o seguinte código:{ "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json", "port": 4321, "https": true, "serveConfigurations": { "default": { "pageUrl": "https://yourtenant.sharepoint.com/sites/demo/_layouts/15/SPListForm.aspx", "formCustomizer": { "componentId": "fb3e7dee-a6fa-4e80-add1-e06081167bb5", "PageType": 8, "RootFolder": "/sites/demo/Lists/business", "properties": { "sampleText": "Value" } }
Vamos chamar alguns tópicos específicos do arquivo serve.json
- Você pode ver várias configurações diferentes que podem ser usadas para depurar formulários novos, editar e exibir com diferenças de parâmetro de consulta específicas. Você pode definir a configuração usada no comando gulp serve, por exemplo, como
gulp serve --config=helloWorld_EditForm
- componentId é automaticamente associado para ser o primeiro componente de formatação de lista em sua solução (se você tiver vários componentes)
- Para simplificar a depuração, você não precisa definir a ID do tipo de conteúdo de destino à qual o componente está associado, mas no runtime a associação é executada no nível do tipo de conteúdo atualizando pelo menos uma das seguintes propriedades no tipo de conteúdo:
- Contenttype. NewFormClientSideComponentId - id de componente para novo formulário
- Contenttype. NewFormClientSideComponentProperties - detalhes de configuração opcionais
- Contenttype. DispFormClientSideComponentId - id de componente para formulário de edição
- Contenttype. DispFormClientSideComponentProperties – detalhes de configuração opcionais
- Contenttype. EditFormClientSideComponentId - formulário de exibição de ID do componente
- Contenttype. EditFormClientSideComponentProperties - detalhes de configuração opcionais
- Você pode ver várias configurações diferentes que podem ser usadas para depurar formulários novos, editar e exibir com diferenças de parâmetro de consulta específicas. Você pode definir a configuração usada no comando gulp serve, por exemplo, como
Compile o código e hospede os arquivos compilados do computador local executando este comando:
gulp serve
Quando o código for compilado sem erros, ele atenderá ao manifesto resultante de https://localhost:4321.
Isso iniciará seu navegador padrão e carregará a página definida no arquivo serve.json .
Aceite o carregamento de manifestos de depuração clicando em Carregar scripts de depuração quando solicitado.
Observe como o componente personalizado é renderizado na página com base no conteúdo personalizado que atualizamos para o método renderizado.
Adicionar recursos de edição de item de formulário ao exemplo
Agora que criamos o componente de linha de base e testamos que ele funciona corretamente. Criaremos uma lógica de renderização separada para exibição, edição e novos formulários e para dar suporte a salvar novos itens na lista.
Abra o arquivo ./src/extensions/helloWorld/loc/myStrings.d.ts e adicione novo Título à interface IHelloWorldFormCustomizerStrings . A interface deve ser a seguinte após suas edições..
declare interface IHelloWorldFormCustomizerStrings { Save: string; Cancel: string; Close: string; Title: string; }
Abra o arquivo ./src/extensions/helloWorld/loc/en-us.js e adicione uma nova cadeia de caracteres Title ao arquivo. O conteúdo do arquivo deve ser o seguinte após suas edições.
define([], function() { return { "Save": "Save", "Cancel": "Cancel", "Close": "Close", "Title": "Title" } });
Abra o arquivo ./src/extensions/helloWorld/HelloWorldFormCustomizer.module.scss e atualize a definição de estilo da seguinte maneira. Estamos adicionando estilo de erro para o componente.
.helloWorld { background-color: "[theme:white, default:#ffffff]"; color: "[theme:themePrimary, default:#0078d4]"; padding: 0.5rem; .error { color: red; } }
Mova para a parte superior do arquivo HelloWorldFormCustomizer.ts .
Localize a linha
import styles from './HelloWorldFormCustomizer.module.scss';
e adicione as seguintes linhas imediatamente após ela:import { FormDisplayMode } from '@microsoft/sp-core-library'; import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
Inclua _item e _etag tipos privados dentro da classe HelloWorldFormCustomizer , conforme mostrado neste snippet de código. Observe que a definição de classe já existe em seu código.
// This already exists in YOUR code export default class HelloWorldFormCustomizer extends BaseFormCustomizer<IHelloWorldFormCustomizerProperties> { // Added for the item to show in the form; use with edit and view form private _item: { Title?: string; }; // Added for item's etag to ensure integrity of the update; used with edit form private _etag?: string;
Atualize o método onInit() da seguinte maneira. Esse código está usando este.displayMode para determinar o status da renderização e, em seguida, busca o item de lista selecionado, se necessário.
public onInit(): Promise<void> { if (this.displayMode === FormDisplayMode.New) { // we're creating a new item so nothing to load return Promise.resolve(); } // load item to display on the form return this.context.spHttpClient .get(this.context.pageContext.web.absoluteUrl + `/_api/web/lists/getbytitle('${this.context.list.title}')/items(${this.context.itemId})`, SPHttpClient.configurations.v1, { headers: { accept: 'application/json;odata.metadata=none' } }) .then(res => { if (res.ok) { // store etag in case we'll need to update the item this._etag = res.headers.get('ETag'); return res.json(); } else { return Promise.reject(res.statusText); } }) .then(item => { this._item = item; return Promise.resolve(); }); }
Atualize o método render() da seguinte maneira. Renderize o formulário somente em exibição ou no modo de edição, dependendo do modo de exibição do formulário. Nesse caso, usamos a mesma renderização para uma experiência nova e de edição, mas você poderia facilmente ter a opção dedicada, se necessário.
public render(): void { // render view form if (this.displayMode === FormDisplayMode.Display) { this.domElement.innerHTML = `<div class="${styles.basics}"> <label for="title">${strings.Title}</label> <br /> ${this._item?.Title} <br /> <br /> <input type="button" id="cancel" value="${strings.Close}" /> </div>`; document.getElementById('cancel').addEventListener('click', this._onClose.bind(this)); } // render new/edit form else { this.domElement.innerHTML = `<div class="${styles.basics}"> <label for="title">${strings.Title}</label><br /> <input type="text" id="title" value="${this._item?.Title || ''}"/> <br /> <br /> <input type="button" id="save" value="${strings.Save}" /> <input type="button" id="cancel" value="${strings.Cancel}" /> <br /> <br /> <div class="${styles.error}"></div> </div>`; document.getElementById('save').addEventListener('click', this._onSave.bind(this)); document.getElementById('cancel').addEventListener('click', this._onClose.bind(this)); } }
Atualize os métodos _onSave na classe HelloWorldFormCustomizer da seguinte maneira.
private _onSave = async (): Promise<void> => { // disable all input elements while we're saving the item this.domElement.querySelectorAll('input').forEach(el => el.setAttribute('disabled', 'disabled')); // reset previous error message if any this.domElement.querySelector(`.${styles.error}`).innerHTML = ''; let request: Promise<SPHttpClientResponse>; const title: string = (document.getElementById('title') as HTMLInputElement).value; switch (this.displayMode) { case FormDisplayMode.New: request = this._createItem(title); break; case FormDisplayMode.Edit: request = this._updateItem(title); } const res: SPHttpClientResponse = await request; if (res.ok) { // You MUST call this.formSaved() after you save the form. this.formSaved(); } else { const error: { error: { message: string } } = await res.json(); this.domElement.querySelector(`.${styles.error}`).innerHTML = `An error has occurred while saving the item. Please try again. Error: ${error.error.message}`; this.domElement.querySelectorAll('input').forEach(el => el.removeAttribute('disabled')); } }
Adicione o novo método _createItem à classe HelloWorldFormCustomizer .
private _createItem(title: string): Promise<SPHttpClientResponse> { return this.context.spHttpClient .post(this.context.pageContext.web.absoluteUrl + `/_api/web/lists/getByTitle('${this.context.list.title}')/items`, SPHttpClient.configurations.v1, { headers: { 'content-type': 'application/json;odata.metadata=none' }, body: JSON.stringify({ Title: title }) }); }
Adicione o novo método _updateItem à classe HelloWorldFormCustomizer .
private _updateItem(title: string): Promise<SPHttpClientResponse> { return this.context.spHttpClient .post(this.context.pageContext.web.absoluteUrl + `/_api/web/lists/getByTitle('${this.context.list.title}')/items(${this.context.itemId})`, SPHttpClient.configurations.v1, { headers: { 'content-type': 'application/json;odata.metadata=none', 'if-match': this._etag, 'x-http-method': 'MERGE' }, body: JSON.stringify({ Title: title }) }); }
Agora, o código é concluído para dar suporte a experiências mínimas de Nova, Edição e Exibição e você pode testar as diferentes experiências usando diferentes configurações para depuração.
Implantação de sua extensão
Sempre que você estiver pronto para começar a usar seu componente, há poucas etapas a serem consideradas relacionadas na associação de componentes ao tipo de conteúdo. As etapas para implantação são as seguintes:
- Implantar solução no Catálogo de Aplicativos do SharePoint
- Instalar a solução na coleção de sites em que você deseja usar a extensão se não estiver usando a implantação no escopo do locatário
- Associe o componente personalizado ao tipo de conteúdo usando as propriedades específicas no objeto ContentType. Há poucas opções para fazer isso:
- Você pode provisionar a lista usada e o tipo de conteúdo da sua solução se estiver usando a opção de implantação no escopo do site
- Você pode associar o componente a tipos de conteúdo usando APIs REST ou CSOM. Observe que, se você associar o componente na coleção de sites ou no nível do hub de tipo de conteúdo, ele será herdado automaticamente a todas as novas instâncias de tipo de conteúdo