Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Vous pouvez choisir d’utiliser la bibliothèque @pnp/spquand vous générez des composants WebPart de SharePoint Framework (SPFx). Cette bibliothèque fournit une API Fluent pour faciliter l’écriture de requêtes REST, ainsi que pour prendre en charge le traitement par lots et la mise en cache. Pour en savoir plus, consultez la page d’accueil du projet qui contient des liens vers de la documentation, des exemples et d’autres ressources pour vous aider à commencer.
Remarque
PnPJS est une solution open source pour laquelle le support est assuré par la communauté active. Il n’existe pas de contrat SLA Microsoft pour le support technique relatif à cet outil open source.
Vous pouvez télécharger la source complète pour cet article à partir du site d’exemples.
Remarque
Avant de suivre les étapes décrites dans cet article, n’oubliez pas de configurer votre environnement de développement de composant WebPart côté client SharePoint.
Création d’un projet
Créez un dossier pour le projet à l’aide de la console de votre choix :
md spfx-pnp-js-example
Saisissez le dossier suivant :
cd spfx-pnp-js-example
Exécutez le générateur Yeoman pour SPFx :
yo @microsoft/sharepoint
Entrez les valeurs suivantes lorsque vous y êtes invité pendant la configuration du nouveau projet :
- spfx-sp-pnp-js-examplecomme nom de solution (conservez la valeur par défaut)
- spfx-sp-pnp-js-examplecomme nom de solution (conservez la valeur par défaut)
- SharePoint Online uniquement (dernière version) comme version de packages de référence
- N pour autoriser l’accès à des API web uniques
- WebPart pour le composant à créer
- SPPnPJSExample comme nom de composant WebPart
- Description PnPJSExample comme description
- React comme l’infrastructure à utiliser.
Ouvrez le projet dans l’éditeur de code de votre choix. Les captures d’écran ci-dessous illustrent Visual Studio Code. Pour ouvrir le répertoire dans Visual Studio Code, entrez les informations suivantes dans la console :
code .
Configurez l’emplacement de votre workbench hébergé SharePoint en modifiant la valeur
initialPage
dans le fichier config/serve.json pour qu’elle pointe vers votre client/site.
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://{your tenant name}/sites/dev/_layouts/15/workbench.aspx"
}
Installer et configurer @pnp/sp
Une fois votre projet créé, vous devez installer et configurer le @pnp/sp package. En outre, nous allons utiliser l’extension @pnp/logging , mais cela est facultatif. Ces étapes sont courantes pour n’importe quel type de projet (React, etc.).
npm install @pnp/logging @pnp/sp --save
Pour SPFx version 1.14.x ou ceux qui ne prennent pas en charge typescript v4.x
Remarque : PnPjs version 3.x est uniquement pris en charge dans SPFx version 1.14 et ultérieure et NodeJs version 12.x et ultérieures.
Mettez à jour le compilateur de pile de pointe sur 4.2. Ceci est abordé dans cet excellent article d’Elio, mais les étapes sont répertoriées ci-dessous.
- Désinstallez le compilateur de pile rush existant (remplacez le x par la version installée dans votre fichier package.json) :
npm uninstall @microsoft/rush-stack-compiler-3.x
- Installez la version 4.2 :
npm i @microsoft/rush-stack-compiler-4.2
- Mettez à jour tsconfig.json pour étendre la configuration 4.2 :
"extends": "./node_modules/@microsoft/rush-stack-compiler-4.2/includes/tsconfig-web.json"
- Désinstallez le compilateur de pile rush existant (remplacez le x par la version installée dans votre fichier package.json) :
Remplacez le contenu de gulpfile.js par :
'use strict'; const build = require('@microsoft/sp-build-web'); build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`); var getTasks = build.rig.getTasks; build.rig.getTasks = function () { var result = getTasks.call(build.rig); result.set('serve', result.get('serve-deprecated')); return result; }; // ********* ADDED ******* // disable tslint build.tslintCmd.enabled = false; // ********* ADDED ******* build.initialize(require('gulp'));
Mise à jour de la méthode onInit dans SpPnPjsExampleWebPart.ts
Étant donné que la @pnp/sp bibliothèque construit des requêtes REST, elle doit connaître l’URL pour envoyer ces requêtes. Lorsque nous travaillons dans SPFx, nous devons nous appuyer sur l’objet de contexte fourni par l’infrastructure.
Il existe deux manières de vous assurer que vous avez correctement configuré vos demandes. Nous employons la méthode onInit
dans cet exemple.
Ouvrez le fichier src\webparts\spPnPjsExample\SpPnPjsExampleWebPart.ts et ajoutez une instruction d'importation pour le fichier de configuration du projet pnp (plus d'informations sur ce fichier ci-dessous) :
import { getSP } from './pnpjsConfig';
Dans la
onInit()
méthode , mettez à jour le code pour qu’il apparaisse comme suit. Ajoutez l’appel pour initialiser notre configuration de projet après l’appelsuper.onInit()
. Nous procédons ainsi après poursuper.onInit()
nous assurer que l’infrastructure a la possibilité d’initialiser tout ce qui est nécessaire et que nous configurons la bibliothèque une fois ces étapes terminées./** * Initialize the web part. */ public async onInit(): Promise<void> { this._environmentMessage = this._getEnvironmentMessage(); await super.onInit(); //Initialize our _sp object that we can then use in other packages without having to pass around the context. // Check out pnpjsConfig.ts for an example of a project setup file. getSP(this.context); }
Ajouter un fichier de configuration de projet
Ensuite, nous allons créer un fichier de configuration de projet pour PnPjs. Ce fichier nous permet de configurer les importations dont nous aurons besoin pour le projet et d’initialiser une instance de l’objet sp à utiliser sur l’un de nos autres composants.
Notez toutes les importations pour les sites web, les listes, les éléments et le traitement par lot. Dans notre composant, nous allons effectuer des appels pour obtenir des éléments à partir d’une bibliothèque. Nous devons donc inclure ces importations pour référence ultérieure. En outre, nous créons une variable qui contiendra notre instance configurée de l'SharePoint Querable
qui sera créée avec l’instance de fabrique. Si vous vous souvenez de notre fonction onInit
ci-dessus, nous appelons le getSP exporté avec le contexte SPFx passé en tant que propriété. En procédant ainsi, nous sommes en mesure d’établir un contexte avec la bibliothèque PnPjs afin de pouvoir ensuite effectuer des appels à l’API SharePoint. Les appels suivants à getSP
sans contexte retournent l’objet qui a été configuré.
Cet exemple montre également comment ajouter un behavior
supplémentaire à l’instance, ce qui permet la journalisation de tous les appels. Cela utilisera la journalisation par défaut, mais nous pourrions l’étendre pour inclure nos propres fonctions de journalisation.
import { WebPartContext } from "@microsoft/sp-webpart-base";
// import pnp and pnp logging system
import { spfi, SPFI, SPFx } from "@pnp/sp";
import { LogLevel, PnPLogging } from "@pnp/logging";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
import "@pnp/sp/batching";
var _sp: SPFI = null;
export const getSP = (context?: WebPartContext): SPFI => {
if (_sp === null && context != null) {
//You must add the @pnp/logging package to include the PnPLogging behavior it is no longer a peer dependency
// The LogLevel set's at what level a message will be written to the console
_sp = spfi().using(SPFx(context)).using(PnPLogging(LogLevel.Warning));
}
return _sp;
};
Ajouter un fichier d’interface pour le modèle de données
Ajoutez un nouveau fichier à la racine du dossier des composants appelé interfaces.ts
. Remplacez le contenu par les définitions suivantes qui seront référencées par notre composant.
// create File item to work with it internally
export interface IFile {
Id: number;
Title: string;
Name: string;
Size: number;
}
// create PnP JS response interface for File
export interface IResponseFile {
Length: number;
}
// create PnP JS response interface for Item
export interface IResponseItem {
Id: number;
File: IResponseFile;
FileLeafRef: string;
Title: string;
}
Mettre à jour le composant par défaut
Enfin, nous devons effectuer un peu de nettoyage pour créer notre composant basé. Remplacez tout d’abord le contenu entier du fichier PnPjsExample.tsx par le code suivant.
import * as React from 'react';
import styles from './PnPjsExample.module.scss';
import { IPnPjsExampleProps } from './IPnPjsExampleProps';
// import interfaces
import { IFile, IResponseItem } from "./interfaces";
import { Caching } from "@pnp/queryable";
import { getSP } from "../pnpjsConfig";
import { SPFI, spfi } from "@pnp/sp";
import { Logger, LogLevel } from "@pnp/logging";
import { IItemUpdateResult } from "@pnp/sp/items";
import { Label, PrimaryButton } from '@microsoft/office-ui-fabric-react-bundle';
export interface IAsyncAwaitPnPJsProps {
description: string;
}
export interface IIPnPjsExampleState {
items: IFile[];
errors: string[];
}
export default class PnPjsExample extends React.Component<IPnPjsExampleProps, IIPnPjsExampleState> {
private LOG_SOURCE = "🅿PnPjsExample";
private LIBRARY_NAME = "Documents";
private _sp: SPFI;
constructor(props: IPnPjsExampleProps) {
super(props);
// set initial state
this.state = {
items: [],
errors: []
};
this._sp = getSP();
}
public componentDidMount(): void {
// read all file sizes from Documents library
this._readAllFilesSize();
}
public render(): React.ReactElement<IAsyncAwaitPnPJsProps> {
// calculate total of file sizes
const totalDocs: number = this.state.items.length > 0
? this.state.items.reduce<number>((acc: number, item: IFile) => {
return (acc + Number(item.Size));
}, 0)
: 0;
return (
<div className={styles.pnPjsExample}>
<Label>Welcome to PnP JS Version 3 Demo!</Label>
<PrimaryButton onClick={this._updateTitles}>Update Item Titles</PrimaryButton>
<Label>List of documents:</Label>
<table width="100%">
<tr>
<td><strong>Title</strong></td>
<td><strong>Name</strong></td>
<td><strong>Size (KB)</strong></td>
</tr>
{this.state.items.map((item, idx) => {
return (
<tr key={idx}>
<td>{item.Title}</td>
<td>{item.Name}</td>
<td>{(item.Size / 1024).toFixed(2)}</td>
</tr>
);
})}
<tr>
<td></td>
<td><strong>Total:</strong></td>
<td><strong>{(totalDocs / 1024).toFixed(2)}</strong></td>
</tr>
</table>
</div >
);
}
private _readAllFilesSize = async (): Promise<void> => {
try {
// do PnP JS query, some notes:
// - .expand() method will retrive Item.File item but only Length property
// - .get() always returns a promise
// - await resolves proimises making your code act syncronous, ergo Promise<IResponseItem[]> becomes IResponse[]
//Extending our sp object to include caching behavior, this modification will add caching to the sp object itself
//this._sp.using(Caching({store:"session"}));
//Creating a new sp object to include caching behavior. This way our original object is unchanged.
const spCache = spfi(this._sp).using(Caching({store:"session"}));
const response: IResponseItem[] = await spCache.web.lists
.getByTitle(this.LIBRARY_NAME)
.items
.select("Id", "Title", "FileLeafRef", "File/Length")
.expand("File/Length")();
// use map to convert IResponseItem[] into our internal object IFile[]
const items: IFile[] = response.map((item: IResponseItem) => {
return {
Id: item.Id,
Title: item.Title || "Unknown",
Size: item.File?.Length || 0,
Name: item.FileLeafRef
};
});
// Add the items to the state
this.setState({ items });
} catch (err) {
Logger.write(`${this.LOG_SOURCE} (_readAllFilesSize) - ${JSON.stringify(err)} - `, LogLevel.Error);
}
}
private _updateTitles = async (): Promise<void> => {
try {
//Will create a batch call that will update the title of each item
// in the library by adding `-Updated` to the end.
const [batchedSP, execute] = this._sp.batched();
//Clone items from the state
const items = JSON.parse(JSON.stringify(this.state.items));
let res: IItemUpdateResult[] = [];
for (let i = 0; i < items.length; i++) {
// you need to use .then syntax here as otherwise the application will stop and await the result
batchedSP.web.lists
.getByTitle(this.LIBRARY_NAME)
.items
.getById(items[i].Id)
.update({ Title: `${items[i].Name}-Updated` })
.then(r => res.push(r));
}
// Executes the batched calls
await execute();
// Results for all batched calls are available
for (let i = 0; i < res.length; i++) {
//If the result is successful update the item
//NOTE: This code is over simplified, you need to make sure the Id's match
const item = await res[i].item.select("Id, Title")<{ Id: number, Title: string }>();
items[i].Name = item.Title;
}
//Update the state which rerenders the component
this.setState({ items });
} catch (err) {
Logger.write(`${this.LOG_SOURCE} (_updateTitles) - ${JSON.stringify(err)} - `, LogLevel.Error);
}
}
}
Exécution de l’exemple
Démarrez l’exemple et ajoutez le composant WebPart à votre Workbench hébergé par SharePoint (/_layouts/15/workbench.aspx) pour le voir en action.
gulp serve --nobrowser
Vous pouvez supprimer les éléments existants en sélectionnant l’icône de la Corbeille, ou ajouter de nouveaux éléments en indiquant des valeurs dans les deux champs et en sélectionnant Ajouter.
Étapes suivantes
La @pnp/sp bibliothèque contient un large éventail de fonctionnalités et d’extensibilité. Pour obtenir des exemples, des instructions et des conseils sur l’utilisation et la configuration de la bibliothèque, consultez le Guide du Développeur.