Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier les répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer de répertoire.
Dans ce tutoriel, vous allez créer un composant de code de jeu de données d’application canevas, le déployer, l’ajouter à un écran et tester le composant à l’aide de Visual Studio Code. Le composant de code affiche une grille de jeux de données paginable et à défilement qui fournit des colonnes triables et filtrables. Il permet également de mettre en surbrillance des lignes spécifiques en configurant une colonne d’indicateur. Il s’agit d’une demande courante des créateurs d’applications et peut être complexe à implémenter à l’aide de composants d’application canevas natifs. Les composants de code peuvent être écrits pour fonctionner sur des applications de canevas et basées sur des modèles. Toutefois, ce composant est écrit pour cibler spécifiquement l’utilisation dans les applications de canevas.
En plus de ces exigences, vous allez également vous assurer que le composant de code suit les conseils de bonnes pratiques :
- Utilisation de l’interface utilisateur Microsoft Fluent
- Localisation des étiquettes des composants de code au moment de la conception et de l’exécution
- Assurance que le composant de code présente la largeur et la hauteur fournies par l′écran de l′application canevas parent
- Considérations relatives au créateur d’applications pour personnaliser l’interface utilisateur à l’aide de propriétés d’entrée et d’éléments d’application externes autant que possible
Note
Avant de commencer, vérifiez que vous avez installé tous les composants requis.
Code
Vous pouvez télécharger l’exemple complet à partir de PowerApps-Samples/component-framework/CanvasGridControl/.
Créer un pcfproj projet
Créez un dossier à utiliser pour votre composant de code. Par exemple :
C:\repos\CanvasGrid.Ouvrez Visual Studio Code , puis ouvrez>le dossier , puis sélectionnez le
CanvasGriddossier. Si vous avez ajouté les extensions de l’Explorateur Windows pendant l’installation de Visual Studio Code, vous pouvez utiliser l’option Ouvrir avec Code dans le dossier. Vous pouvez également charger n’importe quel dossier dans Visual Studio Code à l’aidecode .de l’invite de commandes lorsque le répertoire actif est défini sur cet emplacement.Dans un nouveau terminal PowerShell Visual Studio Code (Terminal>New Terminal), utilisez la commande pac pcf init pour créer un projet de composant de code :
pac pcf init --namespace SampleNamespace --name CanvasGrid --template datasetou en utilisant le formulaire court :
pac pcf init -ns SampleNamespace -n CanvasGrid -t datasetCela ajoute un nouveau
pcfprojet des fichiers associés dans le dossier actuel, y compris unpackages.jsonqui définit les modules nécessaires. Pour installer les modules requis, utilisez npm install :npm installNote
Si vous recevez le message,
The term 'npm' is not recognized as the name of a cmdlet, function, script file, or operable program.vérifiez que vous avez installé tous les prérequis, en particulier node.js (la version LTS est recommandée).
Le modèle inclut un index.ts fichier ainsi que différents fichiers de configuration. Il s’agit du point de départ de votre composant de code et contient les méthodes de cycle de vie décrites dans l’implémentation du composant.
Installer l’interface utilisateur Microsoft Fluent
Vous utiliserez l’interface utilisateur Microsoft Fluent et React pour créer l’interface utilisateur. Vous devez donc les installer en tant que dépendances. Utilisez ce qui suit sur le terminal :
npm install react react-dom @fluentui/react
Cela ajoute les modules au packages.json dossier et les installe dans le node_modules dossier. Vous ne devez pas valider node_modules dans le contrôle de code source, car tous les modules requis peuvent être restaurés à l’aide de npm install.
L’un des avantages de l’interface utilisateur Microsoft Fluent est qu’elle offre une interface utilisateur cohérente et hautement accessible .
Configuration de eslint
Le modèle utilisé par pac pcf init installe le module eslint sur le projet et le configure en ajoutant un fichier .eslintrc.json.
Eslint nécessite désormais la configuration pour les styles de codage TypeScript et React. Plus d’informations : Linting - Meilleures pratiques et conseils pour les composants de code.
Définir les propriétés du jeu de données
Le CanvasGrid\ControlManifest.Input.xml fichier définit les métadonnées décrivant le comportement du composant de code. L’attribut de contrôle contient déjà l’espace de noms et le nom du composant.
Conseil / Astuce
Vous pouvez trouver le code XML plus facile à lire en le mettant en forme afin que les attributs apparaissent sur des lignes distinctes. Recherchez et installez un outil de mise en forme XML de votre choix dans Visual Studio Code Marketplace : recherchez des extensions de mise en forme xml.
Les exemples ci-dessous ont été mis en forme avec des attributs sur des lignes distinctes pour faciliter leur lecture.
Vous devez définir les enregistrements auxquels le composant de code peut être lié, en ajoutant ce qui suit à l’intérieur de l’élément control , en remplaçant l’élément existant data-set :
Les enregistrements data-set sont liés à un source de données si le composant de code est ajouté à une application canevas. Le ensemble de propriétés indique que l’utilisateur doit configurer une des colonnes de ce jeu de données pour l’utiliser en tant qu’indicateur de surbrillance des lignes.
Conseil / Astuce
Vous pouvez spécifier plusieurs éléments de jeu de données. Cela peut être utile si vous souhaitez rechercher un jeu de données, mais afficher une liste d’enregistrements à l’aide d’un second jeu de données.
Définition des propriétés d’entrée et de sortie
Outre le jeu de données, vous pouvez fournir les propriétés d’entrée suivantes :
-
HighlightValue- Permet au créateur d’applications de fournir une valeur à comparer à la colonne définie comme leHighlightIndicatorproperty-set. Lorsque les valeurs sont égales, la ligne doit être mise en surbrillance. -
HighlightColor- Permet au créateur d’application de sélectionner une couleur à utiliser pour mettre en surbrillance les lignes.
Conseil / Astuce
Lors de la création de composants de code à utiliser dans les applications de canevas, il est recommandé de fournir des propriétés d’entrée pour le style des aspects courants de vos composants de code.
Outre les propriétés d’entrée, une propriété de sortie nommée FilteredRecordCount est mise à jour (et déclenche l’événement OnChange ) lorsque le nombre de lignes est modifié en raison d’une action de filtre appliquée à l’intérieur du composant de code. Cela est utile lorsque vous souhaitez afficher un No Rows Found message à l’intérieur de l’application parente.
Note
À l’avenir, les composants de code prennent en charge les événements personnalisés afin de pouvoir définir un événement spécifique plutôt que d’utiliser l’événement générique OnChange .
Pour définir ces trois propriétés, ajoutez ce qui suit au CanvasGrid\ControlManifest.Input.xml fichier, sous l’élément data-set :
<property name="FilteredRecordCount"
display-name-key="FilteredRecordCount_Disp"
description-key="FilteredRecordCount_Desc"
of-type="Whole.None"
usage="output" />
<property name="HighlightValue"
display-name-key="HighlightValue_Disp"
description-key="HighlightValue_Desc"
of-type="SingleLine.Text"
usage="input"
required="true"/>
<property name="HighlightColor"
display-name-key="HighlightColor_Disp"
description-key="HighlightColor_Desc"
of-type="SingleLine.Text"
usage="input"
required="true"/>
Enregistrez ce fichier, puis, sur la ligne de commande, utilisez :
npm run build
Note
Si vous obtenez une erreur comme celle-ci lors de l’exécution npm run build:
[2:48:57 PM] [build] Running ESLint...
[2:48:57 PM] [build] Failed:
[pcf-1065] [Error] ESLint validation error:
C:\repos\CanvasGrid\CanvasGrid\index.ts
2:47 error 'PropertyHelper' is not defined no-undef
Ouvrez index.ts fichier et ajoutez ceci : // eslint-disable-next-line no-undef, directement au-dessus de la ligne :
import DataSetInterfaces = ComponentFramework.PropertyHelper.DataSetApi;
Réexécutez npm run build .
Une fois le composant généré, vous verrez que :
Un fichier
CanvasGrid\generated\ManifestTypes.d.tsgénéré automatiquement est ajouté à votre projet. Cela est généré dans le cadre du processus de génération à partir duControlManifest.Input.xmlet fournit les types nécessaires pour interagir avec les propriétés d'entrée/sortie.La sortie de build est ajoutée au
outrépertoire. Lebundle.jsest le JavaScript transpilé qui s'exécute à l'intérieur du navigateur, et leControlManifest.xmlest une version reformattée du fichierControlManifest.Input.xmlutilisée pendant le déploiement.Note
Ne modifiez pas directement le contenu des
generateddossiers.outIls seront remplacés lors du processus de génération.
Ajouter le composant Grid Fluent UI React
Lorsque le composant de code utilise React, il doit y avoir un seul composant racine rendu dans la méthode updateView. Dans le CanvasGrid dossier, ajoutez un nouveau fichier TypeScript nommé Grid.tsxet ajoutez le contenu suivant :
import {
DetailsList,
ConstrainMode,
DetailsListLayoutMode,
IColumn,
IDetailsHeaderProps,
} from '@fluentui/react/lib/DetailsList';
import { Overlay } from '@fluentui/react/lib/Overlay';
import {
ScrollablePane,
ScrollbarVisibility
} from '@fluentui/react/lib/ScrollablePane';
import { Stack } from '@fluentui/react/lib/Stack';
import { Sticky } from '@fluentui/react/lib/Sticky';
import { StickyPositionType } from '@fluentui/react/lib/Sticky';
import { IObjectWithKey } from '@fluentui/react/lib/Selection';
import { IRenderFunction } from '@fluentui/react/lib/Utilities';
import * as React from 'react';
type DataSet = ComponentFramework.PropertyHelper.DataSetApi.EntityRecord & IObjectWithKey;
export interface GridProps {
width?: number;
height?: number;
columns: ComponentFramework.PropertyHelper.DataSetApi.Column[];
records: Record<string, ComponentFramework.PropertyHelper.DataSetApi.EntityRecord>;
sortedRecordIds: string[];
hasNextPage: boolean;
hasPreviousPage: boolean;
totalResultCount: number;
currentPage: number;
sorting: ComponentFramework.PropertyHelper.DataSetApi.SortStatus[];
filtering: ComponentFramework.PropertyHelper.DataSetApi.FilterExpression;
resources: ComponentFramework.Resources;
itemsLoading: boolean;
highlightValue: string | null;
highlightColor: string | null;
}
const onRenderDetailsHeader: IRenderFunction<IDetailsHeaderProps> = (props, defaultRender) => {
if (props && defaultRender) {
return (
<Sticky stickyPosition={StickyPositionType.Header} isScrollSynced>
{defaultRender({
...props,
})}
</Sticky>
);
}
return null;
};
const onRenderItemColumn = (
item?: ComponentFramework.PropertyHelper.DataSetApi.EntityRecord,
index?: number,
column?: IColumn,
) => {
if (column && column.fieldName && item) {
return <>{item?.getFormattedValue(column.fieldName)}</>;
}
return <></>;
};
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
} = props;
const [isComponentLoading, setIsLoading] = React.useState<boolean>(false);
const items: (DataSet | undefined)[] = React.useMemo(() => {
setIsLoading(false);
const sortedRecords: (DataSet | undefined)[] = sortedRecordIds.map((id) => {
const record = records[id];
return record;
});
return sortedRecords;
}, [records, sortedRecordIds, hasNextPage, setIsLoading]);
const gridColumns = React.useMemo(() => {
return columns
.filter((col) => !col.isHidden && col.order >= 0)
.sort((a, b) => a.order - b.order)
.map((col) => {
const sortOn = sorting && sorting.find((s) => s.name === col.name);
const filtered =
filtering &&
filtering.conditions &&
filtering.conditions.find((f) => f.attributeName == col.name);
return {
key: col.name,
name: col.displayName,
fieldName: col.name,
isSorted: sortOn != null,
isSortedDescending: sortOn?.sortDirection === 1,
isResizable: true,
isFiltered: filtered != null,
data: col,
} as IColumn;
});
}, [columns, sorting]);
const rootContainerStyle: React.CSSProperties = React.useMemo(() => {
return {
height: height,
width: width,
};
}, [width, height]);
return (
<Stack verticalFill grow style={rootContainerStyle}>
<Stack.Item grow style={{ position: 'relative', backgroundColor: 'white' }}>
<ScrollablePane scrollbarVisibility={ScrollbarVisibility.auto}>
<DetailsList
columns={gridColumns}
onRenderItemColumn={onRenderItemColumn}
onRenderDetailsHeader={onRenderDetailsHeader}
items={items}
setKey={`set${currentPage}`} // Ensures that the selection is reset when paging
initialFocusedIndex={0}
checkButtonAriaLabel="select row"
layoutMode={DetailsListLayoutMode.fixedColumns}
constrainMode={ConstrainMode.unconstrained}
></DetailsList>
</ScrollablePane>
{(itemsLoading || isComponentLoading) && <Overlay />}
</Stack.Item>
</Stack>
);
});
Grid.displayName = 'Grid';
Note
Le fichier a l’extension tsx qui est un fichier TypeScript qui prend en charge la syntaxe de style XML utilisée par React. Elle est compilée en JavaScript standard par le processus de génération.
Notes de conception de grille
Cette section inclut des commentaires sur la conception du composant Grid.tsx.
Il s’agit d’un composant fonctionnel
Il s’agit d’un composant fonctionnel React, mais également, il peut s’agir d’un composant de classe. Cela est basé sur votre style de codage préféré. Les composants de classe et les composants fonctionnels peuvent également être mélangés dans le même projet. Les composants de fonction et de classe utilisent la syntaxe de tsx style XML utilisée par React. Plus d’informations : Composants de fonction et de classe
Réduire la taille de bundle.js
Lors de l'importation des composants Fluent UI à l'aide d'importations basées sur le chemin ChoiceGroup, au lieu de :
import {
DetailsList,
ConstrainMode,
DetailsListLayoutMode,
IColumn,
IDetailsHeaderProps,
Stack
} from "@fluentui/react";
Ce code utilise :
import {
DetailsList,
ConstrainMode,
DetailsListLayoutMode,
IColumn,
IDetailsHeaderProps,
} from '@fluentui/react/lib/DetailsList';
import { Stack } from '@fluentui/react/lib/Stack';
De cette façon, la taille de votre offre groupée sera plus petite, ce qui entraînera des exigences de capacité inférieures et de meilleures performances d’exécution.
Une autre possibilité consisterait à utiliser Tree Shaking.
Déstructuration de l’affectation
Ce code :
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
} = props;
utilise une affectation de déstructuration. De cette façon, vous extrayez les attributs requis pour rendre à partir des props, plutôt que de les préfixer avec props. chaque fois qu’ils sont utilisés.
Le code utilise également React.memo pour encapsuler le composant fonctionnel afin qu’il ne s’affiche pas, sauf si l’un des accessoires d’entrée a changé.
Utilisation de React.useMemo
React.useMemo est utilisé à plusieurs endroits pour s’assurer que le tableau d’éléments créé n’est muté que lorsque les propriétés d’entrée options ou configuration changent. Il s’agit d’une bonne pratique en matière de composants de fonction qui réduit les rendus inutiles des composants enfants.
Autres éléments à noter :
- L’élément
DetailsListsitué dans unStackest encapsulé, car vous ajouterez plus tard un élément de pied de page avec les contrôles de pagination. - Le composant Fluent UI
Stickyest utilisé pour encapsuler les colonnes d’en-tête (à l’aide deonRenderDetailsHeader) afin qu’elles restent visibles lors du défilement de la grille. -
setKeyest passé auDetailsListavecinitialFocusedIndexafin que lorsque la page actuelle change, la position de défilement et la sélection soient réinitialisées. - La fonction
onRenderItemColumnest utilisée pour afficher le contenu de la cellule. Il accepte l’élément de ligne et utilise getFormattedValue pour retourner la valeur d’affichage de la colonne. La méthode getValue retourne une valeur que vous pouvez utiliser pour fournir un autre rendu. L’avantage degetFormattedValueest qu'il contient une chaîne mise en forme pour les colonnes de types autres que des chaînes, telles que des dates et des recherches de données. - Le
gridColumnsbloc mappe la forme d’objet des colonnes fournies par le contexte de jeu de données, sur la forme attendue par la propriétéDetailsListcolonnes. Étant donné qu’elle est encapsulée dans le hook React.useMemo, la sortie change uniquement lorsque lescolumnsou lessortingpropriétés changent. Vous pouvez afficher les icônes de tri et de filtre sur les colonnes où les détails de tri et de filtrage fournis par le contexte du composant de code correspondent à la colonne mappée. Les colonnes sont triées à l’aide de lacolumn.orderpropriété pour s’assurer qu’elles sont dans l’ordre correct sur la grille, comme défini par le créateur d’application. - Vous conservez un état interne pour
isComponentLoadingdans le composant React. Cela est dû au fait que lorsque l’utilisateur sélectionne les actions de tri et de filtrage, vous pouvez griser la grille en tant que repère visuel jusqu’à ce que l’étatsortedRecordIdssoit mis à jour et que l’état soit réinitialisé. Il existe une propriété d’entrée supplémentaire appeléeitemsLoadingqui est mappée à la propriété dataset.loading fournie par le contexte du jeu de données. Les deux indicateurs sont utilisés pour contrôler l’indicateur de chargement visuel implémenté à l’aide du composant Fluent UIOverlay.
Mettre à jour index.ts
L’étape suivante consiste à apporter des modifications au index.ts fichier pour qu’il corresponde aux propriétés définies dans Grid.tsx.
Ajouter des instructions d’importation et initialiser des icônes
Dans l’en-tête de index.ts, remplacez les importations existantes par les éléments suivants :
import {IInputs, IOutputs} from './generated/ManifestTypes';
import DataSetInterfaces = ComponentFramework.PropertyHelper.DataSetApi;
type DataSet = ComponentFramework.PropertyTypes.DataSet;
Note
L’importation de initializeIcons est requise, car ce code utilise le jeu d’icônes Fluent UI. Vous appelez initializeIcons pour charger les icônes à l’intérieur du harnais de test. Dans les applications Canvas, elles sont déjà initialisées.
Ajouter des champs à la classe CanvasGrid
Ajoutez les champs suivants à la CanvasGrid classe :
export class CanvasGrid implements ComponentFramework.StandardControl<IInputs, IOutputs> {
/**
* Empty constructor.
*/
constructor() {
}
Mettre à jour la méthode init
Ajoutez les éléments suivants à init:
public init(
context: ComponentFramework.Context<IInputs>,
notifyOutputChanged: () => void,
state: ComponentFramework.Dictionary,
container: HTMLDivElement): void {
// Add control initialization code
}
La init fonction est appelée lorsque le composant de code est d’abord initialisé sur un écran d’application. Vous stockez une référence aux éléments suivants :
-
notifyOutputChanged: rappel si vous avez effectué un appel pour informer l’application canevas que l’une des propriétés a changé. -
container:Il s’agit de l’élément DOM auquel vous ajoutez votre interface utilisateur du composant de code. -
resources:permet de récupérer des chaînes localisées dans la langue de l’utilisateur actuel.
Le context.mode.trackContainerResize(true)) est utilisé pour que updateView soit appelé lorsque le composant de code change de taille.
Note
Actuellement, il n′existe aucun moyen de déterminer si le composant de code s′exécute dans le faisceau de test. Vous devez détecter si l’élément control-dimensionsdiv est présent en tant qu’indicateur.
Mettre à jour la méthode updateView
Ajoutez les éléments suivants à updateView:
public updateView(context: ComponentFramework.Context<IInputs>): void {
// Add code to update control view
}
Vous pouvez voir que :
- Vous appelez React.createElement, en passant la référence au conteneur DOM que vous avez reçu à l’intérieur de la
initfonction. - Le
Gridcomposant est défini à l’intérieurGrid.tsxet est importé en haut du fichier. - Les
allocatedWidthetallocatedHeightseront fournis par le contexte parent chaque fois qu'ils changent (par exemple, lorsque l'application redimensionne le composant de code ou que vous entrez en mode plein écran), car vous avez effectué un appel à trackContainerResize(true) à l'intérieur de la fonctioninit. - Vous pouvez détecter lorsqu’il existe de nouvelles lignes à afficher lorsque le tableau mis à jourProperties contient la
datasetchaîne. - Dans le harnais de test, le tableau
updatedPropertiesn’est pas rempli. Vous pouvez donc utiliser le drapeauisTestHarnessque vous définissez dans la fonctioninitpour court-circuiter la logique qui définitsortedRecordIdetrecords. Vous conservez une référence aux valeurs actuelles jusqu’à ce qu’elles changent, de sorte que vous ne les mutiez pas lorsqu’elles sont passées au composant enfant, sauf si un nouveau rendu des données est requis. - Étant donné que le composant de code conserve l’état de la page affichée, le numéro de page est réinitialisé lorsque le contexte parent réinitialise les enregistrements à la première page. Vous savez quand vous revenez sur la première page quand
hasPreviousPageest false.
Mettre à jour la méthode de destruction
Enfin, vous devez faire le ménage quand le composant du code est détruit.
Démarrer le harnais de test
Vérifiez que tous les fichiers sont enregistrés et utilisez le terminal pour :
npm start watch
Vous devez définir la largeur et la hauteur pour voir la grille du composant de code remplie à l’aide de l’exemple de trois enregistrements. Vous pouvez ensuite exporter un ensemble d’enregistrements dans un fichier CSV à partir de Dataverse, puis le charger dans le harnais de test à l’aide dupanneau Enregistrements> de données :
Voici quelques exemples de données séparés par des virgules que vous pouvez enregistrer dans un fichier .csv et utiliser :
address1_city,address1_country,address1_stateorprovince,address1_line1,address1_postalcode,telephone1,emailaddress1,firstname,fullname,jobtitle,lastname
Seattle,U.S.,WA,7842 Ygnacio Valley Road,12150,555-0112,someone_m@example.com,Thomas,Thomas Andersen (sample),Purchasing Manager,Andersen (sample)
Renton,U.S.,WA,7165 Brock Lane,61795,555-0109,someone_j@example.com,Jim,Jim Glynn (sample),Owner,Glynn (sample)
Snohomish,U.S.,WA,7230 Berrellesa Street,78800,555-0106,someone_g@example.com,Robert,Robert Lyon (sample),Owner,Lyon (sample)
Seattle,U.S.,WA,931 Corte De Luna,79465,555-0111,someone_l@example.com,Susan,Susan Burk (sample),Owner,Burk (sample)
Seattle,U.S.,WA,7765 Sunsine Drive,11910,555-0110,someone_k@example.com,Patrick,Patrick Sands (sample),Owner,Sands (sample)
Seattle,U.S.,WA,4948 West Th St,73683,555-0108,someone_i@example.com,Rene,Rene Valdes (sample),Purchasing Assistant,Valdes (sample)
Redmond,U.S.,WA,7723 Firestone Drive,32147,555-0107,someone_h@example.com,Paul,Paul Cannon (sample),Purchasing Assistant,Cannon (sample)
Issaquah,U.S.,WA,989 Caravelle Ct,33597,555-0105,someone_f@example.com,Scott,Scott Konersmann (sample),Purchasing Manager,Konersmann (sample)
Issaquah,U.S.,WA,7691 Benedict Ct.,57065,555-0104,someone_e@example.com,Sidney,Sidney Higa (sample),Owner,Higa (sample)
Monroe,U.S.,WA,3747 Likins Avenue,37925,555-0103,someone_d@example.com,Maria,Maria Campbell (sample),Purchasing Manager,Campbell (sample)
Duvall,U.S.,WA,5086 Nottingham Place,16982,555-0102,someone_c@example.com,Nancy,Nancy Anderson (sample),Purchasing Assistant,Anderson (sample)
Issaquah,U.S.,WA,5979 El Pueblo,23382,555-0101,someone_b@example.com,Susanna,Susanna Stubberod (sample),Purchasing Manager,Stubberod (sample)
Redmond,U.S.,WA,249 Alexander Pl.,86372,555-0100,someone_a@example.com,Yvonne,Yvonne McKay (sample),Purchasing Manager,McKay (sample)
Note
Il n’existe qu’une seule colonne affichée dans le harnais de test, quelles que soient les colonnes que vous fournissez dans le fichier CSV chargé. Cela est dû au fait que le faisceau de test n′affiche property-set que lorsqu′il en existe un défini. Si aucune valeur n’est property-set définie, toutes les colonnes du fichier CSV chargé sont renseignées.
Ajouter une sélection de lignes
Bien que l’interface utilisateur DetailsList Fluent autorise la sélection d’enregistrements par défaut, les enregistrements sélectionnés ne sont pas liés à la sortie du composant de code. Vous avez besoin des propriétés Selected et SelectedItems pour refléter les enregistrements choisis à l’intérieur d’une application canevas, afin que les composants associés puissent être actualisés. Dans cet exemple, vous autorisez la sélection d’un seul élément à la fois afin de SelectedItems ne contenir qu’un seul enregistrement.
Mettre à jour les imports de Grid.tsx
Ajoutez les éléments suivants aux imports dans Grid.tsx:
import {
DetailsList,
ConstrainMode,
DetailsListLayoutMode,
IColumn,
IDetailsHeaderProps,
} from '@fluentui/react/lib/DetailsList';
import { Overlay } from '@fluentui/react/lib/Overlay';
import {
ScrollablePane,
ScrollbarVisibility
} from '@fluentui/react/lib/ScrollablePane';
import { Stack } from '@fluentui/react/lib/Stack';
import { Sticky } from '@fluentui/react/lib/Sticky';
import { StickyPositionType } from '@fluentui/react/lib/Sticky';
import { IObjectWithKey } from '@fluentui/react/lib/Selection';
import { IRenderFunction } from '@fluentui/react/lib/Utilities';
import * as React from 'react';
Ajouter setSelectedRecords à GridProps
Pour l’interface GridProps, à l’intérieur de Grid.tsx, ajoutez ce qui suit :
export interface GridProps {
width?: number;
height?: number;
columns: ComponentFramework.PropertyHelper.DataSetApi.Column[];
records: Record<string, ComponentFramework.PropertyHelper.DataSetApi.EntityRecord>;
sortedRecordIds: string[];
hasNextPage: boolean;
hasPreviousPage: boolean;
totalResultCount: number;
currentPage: number;
sorting: ComponentFramework.PropertyHelper.DataSetApi.SortStatus[];
filtering: ComponentFramework.PropertyHelper.DataSetApi.FilterExpression;
resources: ComponentFramework.Resources;
itemsLoading: boolean;
highlightValue: string | null;
highlightColor: string | null;
}
Ajouter la propriété setSelectedRecords à Grid
Dans le composant de fonction Grid.tsx mettez à jour la déstructuration des propriétés props pour ajouter la nouvelle propriété setSelectedRecords.
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
} = props;
Directement ci-dessous, ajoutez :
const forceUpdate = useForceUpdate();
const onSelectionChanged = React.useCallback(() => {
const items = selection.getItems() as DataSet[];
const selected = selection.getSelectedIndices().map((index: number) => {
const item: DataSet | undefined = items[index];
return item && items[index].getRecordId();
});
setSelectedRecords(selected);
forceUpdate();
}, [forceUpdate]);
const selection: Selection = useConst(() => {
return new Selection({
selectionMode: SelectionMode.single,
onSelectionChanged: onSelectionChanged,
});
});
Les hooks React.useCallback et useConst garantissent que ces valeurs ne mutent pas entre les rendus et provoquent un rendu de composant enfant inutile.
Le hook useForceUpdate garantit que lorsque la sélection est mise à jour, le composant est réexécuté pour refléter le nombre de sélections mis à jour.
Ajouter une sélection à DetailsList
L’objet selection créé pour conserver l’état de la sélection est ensuite passé dans le DetailsList composant :
<DetailsList
columns={gridColumns}
onRenderItemColumn={onRenderItemColumn}
onRenderDetailsHeader={onRenderDetailsHeader}
items={items}
setKey={`set${currentPage}`} // Ensures that the selection is reset when paging
initialFocusedIndex={0}
checkButtonAriaLabel="select row"
layoutMode={DetailsListLayoutMode.fixedColumns}
constrainMode={ConstrainMode.unconstrained}
></DetailsList>
Définition du rappel setSelectedRecords
Vous devez définir la nouvelle fonction de rappel setSelectedRecords à l'intérieur de index.ts et la transmettre au composant Grid. En haut de CanvasGrid la classe, ajoutez les éléments suivants :
export class CanvasGrid
implements ComponentFramework.StandardControl<IInputs, IOutputs>
{
notifyOutputChanged: () => void;
container: HTMLDivElement;
context: ComponentFramework.Context<IInputs>;
sortedRecordsIds: string[] = [];
resources: ComponentFramework.Resources;
isTestHarness: boolean;
records: {
[id: string]: ComponentFramework.PropertyHelper.DataSetApi.EntityRecord;
};
currentPage = 1;
filteredRecordCount?: number;
Note
La méthode est définie en tant que fonction de flèche pour la lier à l’instance actuelle this du composant de code.
L’appel à setSelectedRecordIds informe l’application canevas que la sélection a changé, ce qui permet de mettre à jour les autres composants qui référencent SelectedItems et Selected.
Ajout d’un nouveau rappel aux propriétés d’entrée
Enfin, ajoutez le nouveau callback aux propriétés d’entrée du composant Grid dans la méthode updateView :
ReactDOM.render(
React.createElement(Grid, {
width: allocatedWidth,
height: allocatedHeight,
columns: dataset.columns,
records: this.records,
sortedRecordIds: this.sortedRecordsIds,
hasNextPage: paging.hasNextPage,
hasPreviousPage: paging.hasPreviousPage,
currentPage: this.currentPage,
totalResultCount: paging.totalResultCount,
sorting: dataset.sorting,
filtering: dataset.filtering && dataset.filtering.getFilter(),
resources: this.resources,
itemsLoading: dataset.loading,
highlightValue: this.context.parameters.HighlightValue.raw,
highlightColor: this.context.parameters.HighlightColor.raw,
}),
this.container
);
Appel de l’événement OnSelect
Il existe un modèle dans les applications de canevas où si une galerie ou une grille a une sélection d’éléments appelée (par exemple, en sélectionnant une icône de chevron), il déclenche l’événement OnSelect . Vous pouvez implémenter ce modèle à l’aide de la méthode openDatasetItem du jeu de données.
Ajouter onNavigate à l’interface GridProps
Comme précédemment, vous ajoutez une propriété de rappel supplémentaire sur le composant Grid en ajoutant ce qui suit à l’interface GridProps dans Grid.tsx :
export interface GridProps {
width?: number;
height?: number;
columns: ComponentFramework.PropertyHelper.DataSetApi.Column[];
records: Record<
string,
ComponentFramework.PropertyHelper.DataSetApi.EntityRecord
>;
sortedRecordIds: string[];
hasNextPage: boolean;
hasPreviousPage: boolean;
totalResultCount: number;
currentPage: number;
sorting: ComponentFramework.PropertyHelper.DataSetApi.SortStatus[];
filtering: ComponentFramework.PropertyHelper.DataSetApi.FilterExpression;
resources: ComponentFramework.Resources;
itemsLoading: boolean;
highlightValue: string | null;
highlightColor: string | null;
setSelectedRecords: (ids: string[]) => void;
}
Ajouter onNavigate aux propriétés du composant Grid
Là encore, vous devez ajouter la nouvelle propriété à la déstructuration des propriétés :
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
setSelectedRecords,
} = props;
Ajouter onItemInvoked à DetailsList
DetailList a une propriété de rappel appelée onItemInvoked à laquelle, en retour, vous transmettez votre rappel :
<DetailsList
columns={gridColumns}
onRenderItemColumn={onRenderItemColumn}
onRenderDetailsHeader={onRenderDetailsHeader}
items={items}
setKey={`set${currentPage}`} // Ensures that the selection is reset when paging
initialFocusedIndex={0}
checkButtonAriaLabel="select row"
layoutMode={DetailsListLayoutMode.fixedColumns}
constrainMode={ConstrainMode.unconstrained}
selection={selection}
></DetailsList>
Ajouter une méthode onNavigate à index.ts
Ajoutez la onNavigate méthode au index.ts juste en dessous de la setSelectedRecords méthode :
onNavigate = (
item?: ComponentFramework.PropertyHelper.DataSetApi.EntityRecord
): void => {
if (item) {
this.context.parameters.records.openDatasetItem(item.getNamedReference());
}
};
Cela appelle simplement la méthode openDatasetItem de l'enregistrement du jeu de données afin que le composant de code déclenche l'événement OnSelect. La méthode est définie en tant que fonction de flèche pour la lier à l’instance actuelle this du composant de code.
Vous devez transmettre ce rappel dans les propriétés du composant Grid dans la méthode updateView :
ReactDOM.render(
React.createElement(Grid, {
width: allocatedWidth,
height: allocatedHeight,
columns: dataset.columns,
records: this.records,
sortedRecordIds: this.sortedRecordsIds,
hasNextPage: paging.hasNextPage,
hasPreviousPage: paging.hasPreviousPage,
currentPage: this.currentPage,
totalResultCount: paging.totalResultCount,
sorting: dataset.sorting,
filtering: dataset.filtering && dataset.filtering.getFilter(),
resources: this.resources,
itemsLoading: dataset.loading,
highlightValue: this.context.parameters.HighlightValue.raw,
highlightColor: this.context.parameters.HighlightColor.raw,
setSelectedRecords: this.setSelectedRecords,
}),
this.container
);
Lorsque vous enregistrez tous les fichiers, le harnais de test est rechargé. Utilisez Ctrl + Shift + I(ouF12) et utilisez Open File (Ctrl + P) à la recherche index.ts et vous pouvez placer un point d’arrêt à l’intérieur de la onNavigate méthode. Double-cliquer sur une ligne (ou la surligner avec les touches du curseur et appuyer sur Enter) permet d′atteindre le point d′arrêt car DetailsList appelle le rappel onNavigate.
Il y a une référence à _this car la fonction est définie comme une fonction fléchée et a été transpilée en une closure JavaScript pour capturer l’instance de this.
Ajouter une localisation
Avant d’aller plus loin, vous devez ajouter des chaînes de ressources au composant de code afin de pouvoir utiliser des chaînes localisées pour les messages tels que la pagination, le tri et le filtrage. Ajoutez un nouveau fichier CanvasGrid\strings\CanvasGrid.1033.resx et utilisez l’éditeur de ressources Visual Studio ou Visual Studio Code avec une extension pour entrer les éléments suivants :
| Nom | Valeur |
|---|---|
Records_Dataset_Display |
Enregistrements |
FilteredRecordCount_Disp |
Nombre d’enregistrements filtrés |
FilteredRecordCount_Desc |
Nombre d’enregistrements après filtrage |
HighlightValue_Disp |
Valeur de surbrillance |
HighlightValue_Desc |
Valeur permettant d’indiquer qu’une ligne doit être mise en surbrillance |
HighlightColor_Disp |
Couleur de surbrillance |
HighlightColor_Desc |
Couleur permettant de mettre en surbrillance une ligne. |
HighlightIndicator_Disp |
Champ d’indicateur de surbrillance |
HighlightIndicator_Desc |
Spécifiez le nom du champ à comparer à la valeur mise en évidence |
Label_Grid_Footer |
Page {0} ({1} Sélectionnée) |
Label_SortAZ |
A à Z |
Label_SortZA |
Z à A |
Label_DoesNotContainData |
Ne contient pas de données |
Label_ShowFullScreen |
Afficher plein écran |
Conseil / Astuce
Il n’est pas recommandé de modifier resx les fichiers directement. Utilisez plutôt l’éditeur de ressources de Visual Studio ou une extension pour Visual Studio Code. Rechercher une extension Visual Studio Code : Rechercher dans Visual Studio Marketplace pour un éditeur resx
Les données de ce fichier peuvent également être définies en ouvrant le fichier dans le CanvasGrid.1033.resx Bloc-notes et en copiant le contenu XML ci-dessous :
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Records_Dataset_Display" xml:space="preserve">
<value>Records</value>
</data>
<data name="FilteredRecordCount_Disp" xml:space="preserve">
<value>Filtered Record Count</value>
</data>
<data name="FilteredRecordCount_Desc" xml:space="preserve">
<value>The number of records after filtering</value>
</data>
<data name="HighlightValue_Disp" xml:space="preserve">
<value>Highlight Value</value>
</data>
<data name="HighlightValue_Desc" xml:space="preserve">
<value>The value to indicate a row should be highlighted</value>
</data>
<data name="HighlightColor_Disp" xml:space="preserve">
<value>Highlight Color</value>
</data>
<data name="HighlightColor_Desc" xml:space="preserve">
<value>The color to highlight a row using</value>
</data>
<data name="HighlightIndicator_Disp" xml:space="preserve">
<value>Highlight Indicator Field</value>
</data>
<data name="HighlightIndicator_Desc" xml:space="preserve">
<value>Set to the name of the field to compare against the Highlight Value</value>
</data>
<data name="Label_Grid_Footer" xml:space="preserve">
<value>Page {0} ({1} Selected)</value>
</data>
<data name="Label_SortAZ" xml:space="preserve">
<value>A to Z</value>
</data>
<data name="Label_SortZA" xml:space="preserve">
<value>Z to A</value>
</data>
<data name="Label_DoesNotContainData" xml:space="preserve">
<value>Does not contain data</value>
</data>
<data name="Label_ShowFullScreen" xml:space="preserve">
<value>Show Full Screen</value>
</data>
</root>
Vous disposez de chaînes de ressources pour les propriétés input, /, output et les dataset ainsi que les property-set associés. Celles-ci seront utilisées dans Power Apps Studio au moment du design en fonction du langage du navigateur du créateur. Vous pouvez également ajouter des chaînes d’étiquette qui peuvent être récupérées au moment de l’exécution à l’aide de getString. Plus d’informations : Implémentation du composant d’API de localisation.
Ajoutez ce nouveau fichier de ressources au fichier ControlManifest.Input.xml à l’intérieur de l’élément resources :
Ajouter le tri et le filtrage des colonnes
Si vous souhaitez permettre à l’utilisateur de trier et de filtrer à l’aide d’en-têtes de colonne de grille, l’interface utilisateur DetailList Fluent permet d’ajouter facilement des menus contextuels aux en-têtes de colonne.
Ajouter onSort et onFilter à GridProps
Tout d’abord, ajoutez onSort et onFilter à l’interface GridProps à l’intérieur de Grid.tsx pour fournir des fonctions de rappel pour le tri et le filtrage :
export interface GridProps {
width?: number;
height?: number;
columns: ComponentFramework.PropertyHelper.DataSetApi.Column[];
records: Record<
string,
ComponentFramework.PropertyHelper.DataSetApi.EntityRecord
>;
sortedRecordIds: string[];
hasNextPage: boolean;
hasPreviousPage: boolean;
totalResultCount: number;
currentPage: number;
sorting: ComponentFramework.PropertyHelper.DataSetApi.SortStatus[];
filtering: ComponentFramework.PropertyHelper.DataSetApi.FilterExpression;
resources: ComponentFramework.Resources;
itemsLoading: boolean;
highlightValue: string | null;
highlightColor: string | null;
setSelectedRecords: (ids: string[]) => void;
onNavigate: (
item?: ComponentFramework.PropertyHelper.DataSetApi.EntityRecord
) => void;
}
Ajouter onSort, onFilter et des ressources aux propriétés
Ajoutez ensuite ces nouvelles propriétés avec la référence resources (pour récupérer les étiquettes localisées pour le tri et le filtrage) à la déstructuration des accessoires :
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
setSelectedRecords,
onNavigate,
} = props;
Importer des composants ContextualMenu
Vous devez ajouter des importations en haut Grid.tsx pour pouvoir utiliser le ContextualMenu composant fourni par Fluent UI. Vous pouvez utiliser des imports basés sur le chemin pour réduire la taille du bundle.
import { ContextualMenu, DirectionalHint, IContextualMenuProps } from '@fluentui/react/lib/ContextualMenu';
Ajouter une fonctionnalité de rendu de menu contextuel
Maintenant, ajoutez la fonctionnalité de rendu du menu contextuel juste sous la ligne après Grid.tsx.
const [isComponentLoading, setIsLoading] = React.useState<boolean>(false); :
Vous verrez que :
- L’état
contextualMenuPropscontrôle la visibilité du menu contextuel rendu à l’aide du composant Fluent UIContextualMenu. - Ce code fournit un filtre simple pour afficher uniquement les valeurs où le champ ne contient aucune donnée. Vous pouvez étendre cette option pour fournir un filtrage supplémentaire.
- Ce code utilise
resources.getStringpour afficher des étiquettes dans le menu contextuel qui peut être localisé. - Le
React.useCallbackhook, similaire àReact.useMemo, garantit que les rappels ne sont mutés que lorsque les valeurs dépendantes changent. Cela optimise le rendu des composants enfants.
Ajouter de nouvelles fonctions de menu contextuel aux événements de menu contextuel et de sélection de colonne
Ajoutez ces nouvelles fonctions de menu contextuel aux événements de menu contextuel et de sélection de colonne. Mettez à jour le const gridColumns pour ajouter les rappels onColumnContextMenu et onColumnClick.
const gridColumns = React.useMemo(() => {
return columns
.filter((col) => !col.isHidden && col.order >= 0)
.sort((a, b) => a.order - b.order)
.map((col) => {
const sortOn = sorting && sorting.find((s) => s.name === col.name);
const filtered =
filtering &&
filtering.conditions &&
filtering.conditions.find((f) => f.attributeName == col.name);
return {
key: col.name,
name: col.displayName,
fieldName: col.name,
isSorted: sortOn != null,
isSortedDescending: sortOn?.sortDirection === 1,
isResizable: true,
isFiltered: filtered != null,
data: col,
} as IColumn;
});
}, [columns, sorting]);
Ajouter un menu contextuel à la sortie affichée
Pour que le menu contextuel s’affiche, vous devez l’ajouter à la sortie rendue. Ajoutez les éléments suivants directement sous le DetailsList composant dans la sortie retournée :
<ScrollablePane scrollbarVisibility={ScrollbarVisibility.auto}>
<DetailsList
columns={gridColumns}
onRenderItemColumn={onRenderItemColumn}
onRenderDetailsHeader={onRenderDetailsHeader}
items={items}
setKey={`set${currentPage}`} // Ensures that the selection is reset when paging
initialFocusedIndex={0}
checkButtonAriaLabel="select row"
layoutMode={DetailsListLayoutMode.fixedColumns}
constrainMode={ConstrainMode.unconstrained}
selection={selection}
onItemInvoked={onNavigate}
></DetailsList>
</ScrollablePane>
Ajouter des fonctions OnSort et OnFilter
Maintenant que vous avez ajouté l′interface utilisateur de tri et de filtrage, ajoutez les rappels à index.ts pour réellement effectuer le tri et le filtrage sur les enregistrements liés au composant de code. Ajoutez ce qui suit à index.ts, juste en dessous de la fonction onNavigate :
onSort = (name: string, desc: boolean): void => {
const sorting = this.context.parameters.records.sorting;
while (sorting.length > 0) {
sorting.pop();
}
this.context.parameters.records.sorting.push({
name: name,
sortDirection: desc ? 1 : 0,
});
this.context.parameters.records.refresh();
};
onFilter = (name: string, filter: boolean): void => {
const filtering = this.context.parameters.records.filtering;
if (filter) {
filtering.setFilter({
conditions: [
{
attributeName: name,
conditionOperator: 12, // Does not contain Data
},
],
} as ComponentFramework.PropertyHelper.DataSetApi.FilterExpression);
} else {
filtering.clearFilter();
}
this.context.parameters.records.refresh();
};
Vous verrez que :
- Le tri et le filtre sont appliqués au jeu de données à l’aide des propriétés de tri et de filtrage .
- Lors de la modification des colonnes de tri, les définitions de tri existantes doivent être supprimées en utilisant la méthode pop plutôt que de remplacer le tableau de tri lui-même.
- L’actualisation doit être appelée après l’application du tri et du filtrage. Si un filtre et un tri sont appliqués en même temps, l’actualisation doit être appelée une seule fois.
Ajouter des rappels OnSort et OnFilter au rendu de la grille
Enfin, vous pouvez transmettre ces deux rappels dans l′appel de rendu Grid :
ReactDOM.render(
React.createElement(Grid, {
width: allocatedWidth,
height: allocatedHeight,
columns: dataset.columns,
records: this.records,
sortedRecordIds: this.sortedRecordsIds,
hasNextPage: paging.hasNextPage,
hasPreviousPage: paging.hasPreviousPage,
currentPage: this.currentPage,
totalResultCount: paging.totalResultCount,
sorting: dataset.sorting,
filtering: dataset.filtering && dataset.filtering.getFilter(),
resources: this.resources,
itemsLoading: dataset.loading,
highlightValue: this.context.parameters.HighlightValue.raw,
highlightColor: this.context.parameters.HighlightColor.raw,
setSelectedRecords: this.setSelectedRecords,
onNavigate: this.onNavigate,
}),
this.container
);
Note
À ce stade, vous ne pouvez plus tester à l’aide du harnais de test, car il ne prend pas en charge le tri et le filtrage. Plus tard, vous pouvez déployer à l’aide de pac pcf push, puis l’ajouter à une application de type canevas pour les tests. Si vous le souhaitez, vous pouvez passer à cette étape pour voir comment le composant de code apparaît dans les applications Canvas.
Mettre à jour la propriété de sortie FilteredRecordCount
Étant donné que la grille peut désormais filtrer les enregistrements en interne, il est important de renvoyer à l’application canevas le nombre d’enregistrements affichés. Cela permet d’afficher un message de type « Aucun enregistrement ».
Conseil / Astuce
Vous pouvez implémenter cela en interne dans le composant de code, mais il est recommandé de laisser autant d’interface utilisateur à l’application canevas, car elle donnera plus de flexibilité au créateur d’application.
Vous avez déjà défini une propriété de sortie appelée FilteredRecordCount dans le ControlManifest.Input.xml. Lorsque le filtrage a lieu et que les enregistrements filtrés sont chargés, la updateView fonction est appelée avec une chaîne dataset dans le tableau updatedProperties . Si le nombre d’enregistrements a changé, vous devez appeler notifyOutputChanged pour que l’application canevas sache qu’elle doit mettre à jour les contrôles utilisant la propriété FilteredRecordCount. À l’intérieur de la updateView méthode de index.ts, ajoutez les éléments suivants juste au-dessus de l’élément ReactDOM.render et ci-dessous allocatedHeight:
Ajouter FilteredRecordCount à getOutputs
Cela met à jour la filteredRecordCount classe de composant de code que vous avez définie précédemment lorsqu’elle est différente des nouvelles données reçues. Une fois notifyOutputChanged appelée, vous devez vous assurer que la valeur est retournée quand getOutputs elle est appelée. Mettez donc à jour la getOutputs méthode comme suit :
Ajouter la pagination à la grille
Pour les jeux de données volumineux, les applications de canevas fractionneront les enregistrements sur plusieurs pages. Vous pouvez ajouter un pied de page qui affiche les contrôles de navigation de page. Chaque bouton sera rendu à l’aide d’une interface utilisateur IconButtonFluent, que vous devez importer.
Ajouter IconButton aux importations
Ajoutez ceci aux importations dans Grid.tsx :
import { IconButton } from '@fluentui/react/lib/Button';
Ajouter une fonction stringFormat
L'étape suivante ajoutera des fonctions pour charger le format de l'étiquette de l'indicateur de page à partir des chaînes de ressources ("Page {0} ({1} Selected)") et le structurer à l'aide d'une fonction simple stringFormat. Cette fonction peut également se trouver dans un fichier distinct et partagé entre vos composants pour des raisons pratiques :
Dans ce tutoriel, ajoutez-le en haut de Grid.tsx, juste en dessous type DataSet ....
function stringFormat(template: string, ...args: string[]): string {
for (const k in args) {
template = template.replace("{" + k + "}", args[k]);
}
return template;
}
Ajouter des boutons de pagination
Dans Grid.tsx, ajoutez les éléments suivants sous l’élément existant Stack.Item qui contient le ScrollablePane :
return (
<Stack verticalFill grow style={rootContainerStyle}>
<Stack.Item grow style={{ position: 'relative', backgroundColor: 'white' }}>
<ScrollablePane scrollbarVisibility={ScrollbarVisibility.auto}>
<DetailsList
columns={gridColumns}
onRenderItemColumn={onRenderItemColumn}
onRenderDetailsHeader={onRenderDetailsHeader}
items={items}
setKey={`set${currentPage}`} // Ensures that the selection is reset when paging
initialFocusedIndex={0}
checkButtonAriaLabel="select row"
layoutMode={DetailsListLayoutMode.fixedColumns}
constrainMode={ConstrainMode.unconstrained}
selection={selection}
onItemInvoked={onNavigate}
></DetailsList>
{contextualMenuProps && <ContextualMenu {...contextualMenuProps} />}
</ScrollablePane>
{(itemsLoading || isComponentLoading) && <Overlay />}
</Stack.Item>
</Stack>
);
Vous verrez que :
- Le
Stackgarantit que le pied de page s'affichera sous leDetailsList. L’attributgrowest utilisé pour vous assurer que la grille se développe pour remplir l’espace disponible. - Vous chargez le format de l’étiquette de l’indicateur de page à partir des chaînes de ressources (
"Page {0} ({1} Selected)") et vous le formatez en utilisant la fonctionstringFormatque vous avez ajoutée à l’étape précédente. - Fournissez le texte
altpour l’accessibilité sur la paginationIconButtons. - Le style sur le pied de page peut également être appliqué à l’aide d’un nom de classe CSS référençant un fichier CSS ajouté au composant de code.
Ajouter des propriétés de rappel pour prendre en charge la pagination
Ensuite, vous devez ajouter les propriétés de rappel manquantes loadFirstPage, loadNextPage et loadPreviousPage.
Pour l’interface GridProps , ajoutez les éléments suivants :
export interface GridProps {
width?: number;
height?: number;
columns: ComponentFramework.PropertyHelper.DataSetApi.Column[];
records: Record<string, ComponentFramework.PropertyHelper.DataSetApi.EntityRecord>;
sortedRecordIds: string[];
hasNextPage: boolean;
hasPreviousPage: boolean;
totalResultCount: number;
currentPage: number;
sorting: ComponentFramework.PropertyHelper.DataSetApi.SortStatus[];
filtering: ComponentFramework.PropertyHelper.DataSetApi.FilterExpression;
resources: ComponentFramework.Resources;
itemsLoading: boolean;
highlightValue: string | null;
highlightColor: string | null;
setSelectedRecords: (ids: string[]) => void;
onNavigate: (item?: ComponentFramework.PropertyHelper.DataSetApi.EntityRecord) => void;
onSort: (name: string, desc: boolean) => void;
onFilter: (name: string, filtered: boolean) => void;
}
Ajouter de nouvelles propriétés de pagination à la grille
Ajoutez ces nouvelles propriétés à la déstructuration des propriétés :
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
setSelectedRecords,
onNavigate,
onSort,
onFilter,
resources,
} = props;
Ajouter des fonctions de rappel à index.ts
Ajoutez ces rappels à index.ts sous la méthode onFilter :
loadFirstPage = (): void => {
this.currentPage = 1;
this.context.parameters.records.paging.loadExactPage(1);
};
loadNextPage = (): void => {
this.currentPage++;
this.context.parameters.records.paging.loadExactPage(this.currentPage);
};
loadPreviousPage = (): void => {
this.currentPage--;
this.context.parameters.records.paging.loadExactPage(this.currentPage);
};
Mettez ensuite à jour l’appel de rendu de Grid pour qu’il inclue ces rappels :
ReactDOM.render(
React.createElement(Grid, {
width: allocatedWidth,
height: allocatedHeight,
columns: dataset.columns,
records: this.records,
sortedRecordIds: this.sortedRecordsIds,
hasNextPage: paging.hasNextPage,
hasPreviousPage: paging.hasPreviousPage,
currentPage: this.currentPage,
totalResultCount: paging.totalResultCount,
sorting: dataset.sorting,
filtering: dataset.filtering && dataset.filtering.getFilter(),
resources: this.resources,
itemsLoading: dataset.loading,
highlightValue: this.context.parameters.HighlightValue.raw,
highlightColor: this.context.parameters.HighlightColor.raw,
setSelectedRecords: this.setSelectedRecords,
onNavigate: this.onNavigate,
onSort: this.onSort,
onFilter: this.onFilter,
}),
this.container
);
Ajouter la compatibilité mode plein écran
Les composants de code offrent la possibilité d’afficher en mode plein écran. Cela est particulièrement utile sur les petites tailles d’écran ou où il existe un espace limité pour le composant de code dans un écran d’application canevas.
Importer un composant Fluent UI Link
Pour lancer le mode plein écran, vous pouvez utiliser le composant Fluent UI Link . Ajoutez-le aux imports en haut de Grid.tsx:
import { Link } from '@fluentui/react/lib/Link';
Ajouter le contrôle Link
Pour ajouter un lien plein écran, vous ajoutez ce qui suit à l’existant Stack qui contient les contrôles de pagination.
Note
Veillez à l’ajouter à l'élément imbriqué Stack, et non à la racine Stack.
<Stack horizontal style={{ width: '100%', paddingLeft: 8, paddingRight: 8 }}>
<IconButton
alt="First Page"
iconProps={{ iconName: 'Rewind' }}
disabled={!hasPreviousPage}
onClick={loadFirstPage}
/>
<IconButton
alt="Previous Page"
iconProps={{ iconName: 'Previous' }}
disabled={!hasPreviousPage}
onClick={loadPreviousPage}
/>
<Stack.Item align="center">
{stringFormat(
resources.getString('Label_Grid_Footer'),
currentPage.toString(),
selection.getSelectedCount().toString(),
)}
</Stack.Item>
<IconButton
alt="Next Page"
iconProps={{ iconName: 'Next' }}
disabled={!hasNextPage}
onClick={loadNextPage}
/>
</Stack>
Vous verrez que :
- Ce code utilise des ressources pour afficher l’étiquette pour prendre en charge la localisation.
- Si le mode plein écran est ouvert, le lien n’est pas affiché. Au lieu de cela, le contexte de l’application parente affiche automatiquement une icône de fermeture.
Ajouter des propriétés pour prendre en charge le plein écran à GridProps
Ajoutez les propriétés onFullScreen et isFullScreen à l'interface GridProps à l'intérieur de Grid.tsx pour fournir des fonctions de rappel pour le tri et le filtrage.
export interface GridProps {
width?: number;
height?: number;
columns: ComponentFramework.PropertyHelper.DataSetApi.Column[];
records: Record<string, ComponentFramework.PropertyHelper.DataSetApi.EntityRecord>;
sortedRecordIds: string[];
hasNextPage: boolean;
hasPreviousPage: boolean;
totalResultCount: number;
currentPage: number;
sorting: ComponentFramework.PropertyHelper.DataSetApi.SortStatus[];
filtering: ComponentFramework.PropertyHelper.DataSetApi.FilterExpression;
resources: ComponentFramework.Resources;
itemsLoading: boolean;
highlightValue: string | null;
highlightColor: string | null;
setSelectedRecords: (ids: string[]) => void;
onNavigate: (item?: ComponentFramework.PropertyHelper.DataSetApi.EntityRecord) => void;
onSort: (name: string, desc: boolean) => void;
onFilter: (name: string, filtered: boolean) => void;
loadFirstPage: () => void;
loadNextPage: () => void;
loadPreviousPage: () => void;
}
Ajouter des propriétés à la grille pour prendre en charge le plein écran
Ajoutez ces nouvelles propriétés à la déstructuration des propriétés :
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
setSelectedRecords,
onNavigate,
onSort,
onFilter,
resources,
loadFirstPage,
loadNextPage,
loadPreviousPage,
} = props;
Mettre à jour index.ts pour prendre en charge le plein écran dans la grille
Pour fournir ces nouvelles propriétés, dans index.ts, ajoutez la méthode de rappel suivante sous loadPreviousPage :
onFullScreen = (): void => {
this.context.mode.setFullScreen(true);
};
L’appel à setFullScreen provoque le composant de code à s'ouvrir en mode plein écran et ajuste allocatedHeight et allocatedWidth en conséquence à cause de l'appel à trackContainerResize(true) dans la méthode init. Une fois le mode plein écran ouvert, updateView sera appelé pour mettre à jour le rendu du composant avec la nouvelle taille.
updatedProperties Contient fullscreen_open oufullscreen_close, en fonction de la transition qui se produit.
Pour stocker l’état du mode plein écran, ajoutez un nouveau champ isFullScreen à la classe CanvasGrid à l’intérieur de index.ts:
export class CanvasGrid implements ComponentFramework.StandardControl<IInputs, IOutputs> {
notifyOutputChanged: () => void;
container: HTMLDivElement;
context: ComponentFramework.Context<IInputs>;
sortedRecordsIds: string[] = [];
resources: ComponentFramework.Resources;
isTestHarness: boolean;
records: {
[id: string]: ComponentFramework.PropertyHelper.DataSetApi.EntityRecord;
};
currentPage = 1;
filteredRecordCount?: number;
Modifier updateView pour suivre l’état
Ajoutez ce qui suit à la updateView méthode pour suivre l’état :
public updateView(context: ComponentFramework.Context<IInputs>): void {
const dataset = context.parameters.records;
const paging = context.parameters.records.paging;
const datasetChanged = context.updatedProperties.indexOf("dataset") > -1;
const resetPaging =
datasetChanged &&
!dataset.loading &&
!dataset.paging.hasPreviousPage &&
this.currentPage !== 1;
if (resetPaging) {
this.currentPage = 1;
}
Transmettre le rappel et le champ isFullScreen à afficher dans la grille
Vous pouvez maintenant transmettre le rappel et le champ isFullScreen dans les propriétés de rendu Grid :
ReactDOM.render(
React.createElement(Grid, {
width: allocatedWidth,
height: allocatedHeight,
columns: dataset.columns,
records: this.records,
sortedRecordIds: this.sortedRecordsIds,
hasNextPage: paging.hasNextPage,
hasPreviousPage: paging.hasPreviousPage,
currentPage: this.currentPage,
totalResultCount: paging.totalResultCount,
sorting: dataset.sorting,
filtering: dataset.filtering && dataset.filtering.getFilter(),
resources: this.resources,
itemsLoading: dataset.loading,
highlightValue: this.context.parameters.HighlightValue.raw,
highlightColor: this.context.parameters.HighlightColor.raw,
setSelectedRecords: this.setSelectedRecords,
onNavigate: this.onNavigate,
onSort: this.onSort,
onFilter: this.onFilter,
loadFirstPage: this.loadFirstPage,
loadNextPage: this.loadNextPage,
loadPreviousPage: this.loadPreviousPage,
}),
this.container
);
Mise en surbrillance des lignes
Vous êtes maintenant prêt à ajouter la fonctionnalité de surlignage conditionnel de ligne. Vous avez déjà défini les propriétés d’entrée HighlightValue et HighlightColor, ainsi que les propriétés HighlightIndicatorproperty-set. Le property-set permet au créateur de choisir un champ à utiliser pour comparer avec la valeur qu'il fournit dans HighlightValue.
Importer des types pour prendre en charge la mise en surbrillance
Le rendu de ligne personnalisé dans le DetailsList fichier nécessite des importations supplémentaires. Il existe déjà certains types à partir de @fluentui/react/lib/DetailsList, donc ajouter IDetailsListPropset IDetailsRowStylesDetailsRow à cette instruction import dans Grid.tsx:
import {
DetailsList,
ConstrainMode,
DetailsListLayoutMode,
IColumn,
IDetailsHeaderProps
} from '@fluentui/react/lib/DetailsList';
À présent, créez le rendu de ligne personnalisé en ajoutant ce qui suit juste en dessous du bloc const rootContainerStyle :
const onRenderRow: IDetailsListProps['onRenderRow'] = (props) => {
const customStyles: Partial<IDetailsRowStyles> = {};
if (props && props.item) {
const item = props.item as DataSet | undefined;
if (highlightColor && highlightValue && item?.getValue('HighlightIndicator') == highlightValue) {
customStyles.root = { backgroundColor: highlightColor };
}
return <DetailsRow {...props} styles={customStyles} />;
}
return null;
};
Vous verrez que :
- Vous pouvez récupérer la valeur du champ choisi par le créateur via l’alias à l’aide
HighlightIndicatorde :
item?.getValue('HighlightIndicator'). - Lorsque la valeur du
HighlightIndicatorchamp correspond à la valeur de lahighlightValuepropriété d’entrée fournie sur le composant de code, vous pouvez ajouter une couleur d’arrière-plan à la ligne. - Le
DetailsRowcomposant est utilisé par leDetailsListservice pour afficher les colonnes que vous avez définies. Vous n’avez pas besoin de modifier le comportement autre que la couleur d’arrière-plan.
Ajouter des propriétés supplémentaires pour prendre en charge le surlignage
Ajoutez des propriétés supplémentaires pour highlightColor et highlightValue qui seront fournies par le rendu dans updateView. Elles sont déjà ajoutées à l′interface GridProps, vous n′avez donc plus qu′à les ajouter à la déstructuration des propriétés :
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
setSelectedRecords,
onNavigate,
onSort,
onFilter,
resources,
loadFirstPage,
loadNextPage,
loadPreviousPage,
onFullScreen,
isFullScreen,
} = props;
Ajouter la méthode onRenderRow à DetailsList
Passez la méthode onRenderRow aux propriétés DetailsList.
<DetailsList
columns={gridColumns}
onRenderItemColumn={onRenderItemColumn}
onRenderDetailsHeader={onRenderDetailsHeader}
items={items}
setKey={`set${currentPage}`} // Ensures that the selection is reset when paging
initialFocusedIndex={0}
checkButtonAriaLabel="select row"
layoutMode={DetailsListLayoutMode.fixedColumns}
constrainMode={ConstrainMode.unconstrained}
selection={selection}
onItemInvoked={onNavigate}
></DetailsList>
Déployer et configurer le composant
Maintenant que vous avez implémenté toutes les fonctionnalités, vous devez déployer le composant de code sur Microsoft Dataverse pour les tests.
Dans votre environnement Dataverse, vérifiez qu’il existe un éditeur créé avec un préfixe de
samples:
Il peut également s’agir de votre propre éditeur, à condition de mettre à jour le paramètre de préfixe de l’éditeur dans l’appel à pac pcf push ci-dessous. Pour plus d’informations, créez un éditeur de solution.
Une fois l′éditeur enregistré, vous êtes prêt à autoriser la CLI sur votre environnement pour pousser le composant de code compilé. Sur la ligne de commande, utilisez :
pac auth create --url https://myorg.crm.dynamics.comRemplacez
myorg.crm.dynamics.compar l’URL de votre propre environnement Dataverse. Connectez-vous avec un utilisateur administrateur/personnalisateur lorsque vous y êtes invité. Les privilèges fournis par ces rôles d’utilisateur sont nécessaires pour déployer tous les composants de code dans Dataverse.Pour déployer votre composant de code, utilisez :
pac pcf push --publisher-prefix samplesNote
Si vous recevez l’erreur,
Missing required tool: MSBuild.exe/dotnet.exe. Please add MSBuild.exe/dotnet.exe in Path environment variable or use 'Developer Command Prompt for VSvous devez installer Visual Studio 2019 pour Windows &Mac ou Build Tools pour Visual Studio 2019, en étant sûr de sélectionner la charge de travail « Outils de génération .NET », comme décrit dans les conditions préalables.Une fois terminé, ce processus aura créé une petite solution temporaire nommée PowerAppTools_samples dans votre environnement, et le
CanvasGridcomposant de code sera ajouté à cette solution. Vous pouvez déplacer le composant de code dans votre propre solution ultérieurement si nécessaire. Plus d’informations : Gestion du cycle de vie des applications du composant de code (ALM).
Pour utiliser des composants de code à l’intérieur d’applications canevas, vous devez activer l’infrastructure de composants Power Apps pour les applications de canevas sur l’environnement que vous utilisez.
a. Ouvrez le Centre d’administration (admin.powerplatform.microsoft.com) et accédez à votre environnement. b. Accédez aux paramètres>des fonctionnalités>. Vérifiez que l’infrastructure du composant Power Apps pour les applications de canevas est activée :
Créez une application canevas dans la disposition Tablette.
Dans le volet Insertion , sélectionnez Obtenir plus de composants.
Sélectionnez l’onglet Code dans le volet Importer des composants .
Sélectionnez le composant
CanvasGrid.Cliquez sur Importer. Le composant de code s’affiche désormais sous Composants de code dans le panneau Insertion .
Faites glisser le
CanvasGridcomposant sur l’écran et attachez-le à laContactstable dans Microsoft Dataverse.Définissez les propriétés suivantes sur le composant de
CanvasGridcode à l’aide du panneau de propriétés :-
Valeur de surbrillance =
1- Il s’agit de la valeur questatecodea lorsque l’enregistrement est inactif. - Couleur =
#FDE7E9- Il s’agit de la couleur à utiliser lorsque l’enregistrement est inactif. -
HighlightIndicator="statecode"- Il s’agit du champ à comparer. Cela sera sur le panneau Avancé dans la section Données.
-
Valeur de surbrillance =
Ajoutez un nouveau
TextInputcomposant et nommez-letxtSearch.Mettez à jour la propriété
CanvasGrid.Itemspour qu’elle soitSearch(Contacts,txtSearch.Text,"fullname").Lorsque vous tapez l’entrée de texte, vous verrez que les contacts sont filtrés dans la grille.
Ajoutez une nouvelle étiquette De texte et définissez le texte sur « Aucun enregistrement trouvé ». Positionnez l’étiquette sur la grille de canevas.
Définissez la propriété Visible de l’étiquette De texte sur
CanvasGrid1.FilteredRecordCount=0.
Cela signifie qu’en l’absence d’enregistrements correspondant à la txtSearch valeur ou si un filtre de colonne est appliqué à l’aide du menu contextuel qui ne retourne aucun enregistrement (par exemple, le nom complet ne contient pas de données), l’étiquette s’affiche.
Ajoutez un formulaire d’affichage (à partir du groupe d’entrée dans le volet Insertion ).
Définissez le formulaire
DataSourcesur laContactstable et ajoutez des champs de formulaire.Définissez la propriété de formulaire
ItemsurCanvasGrid1.Selected.Vous devez maintenant voir que lorsque vous sélectionnez des éléments dans la grille, le formulaire affiche l’élément sélectionné.
Ajoutez un nouvel écran à l’application canevas appelée
scrDetails.Copiez le formulaire à partir de l’écran précédent et collez-le sur le nouvel écran.
Définissez la
CanvasGrid1.OnSelectpropriété surNavigate(scrDetails).Lorsque vous exécutez l’action de sélection de ligne de grille, vous verrez alors que l’application navigue vers le deuxième écran avec l’élément sélectionné.
Débogage après le déploiement
Vous pouvez facilement déboguer votre composant de code pendant son exécution dans l'application canevas en ouvrant les outils de développement à l’aide de Ctrl+Shift+I.
Sélectionnez Ctrl+P et tapez Grid.tsx ou Index.ts. Vous pouvez ensuite définir un point d’arrêt et parcourir votre code.
Si vous devez apporter d’autres modifications à votre composant, vous n’avez pas besoin de déployer chaque fois. Utilisez plutôt la technique décrite dans Déboguer les composants de code pour créer une règle AutoResponder Fiddler pour charger le fichier à partir du système de fichiers local pendant l′exécution de npm start watch.
L’AutoResponder ressemble à ce qui suit :
REGEX:(.*?)((?'folder'css|html)(%252f|\/))?SampleNamespace\.CanvasGrid[\.\/](?'fname'[^?]*\.*)(.*?)$
C:\repos\CanvasGrid\out\controls\CanvasGrid\${folder}\${fname}
Vous devez également activer les filtres pour ajouter l’en-tête Access-Control-Allow-Origin . Plus d’informations : Débogage après le déploiement dans Microsoft Dataverse.
Vous devez vider le cache et actualiser durement votre session de navigateur pour que le fichier AutoResponder soit récupéré. Une fois chargé, vous pouvez simplement actualiser le navigateur, car Fiddler ajoute un en-tête de contrôle de cache au fichier pour l’empêcher d’être mis en cache.
Une fois que vous êtes satisfait de vos modifications, vous pouvez incrémenter la version du correctif dans le manifeste, puis redéployer à l’aide de pac pcf push.
Jusqu’à présent, vous avez déployé une build de développement, qui n’est pas optimisée et s’exécute plus lentement au moment de l’exécution. Vous pouvez choisir de déployer une build optimisée à l’aide de pac pcf push en modifiant le CanvasGrid.pcfproj. Sous le OutputPath, ajoutez les éléments suivants : <PcfBuildMode>production</PcfBuildMode>
<PropertyGroup>
<Name>CanvasGrid</Name>
<ProjectGuid>a670bba8-e0ae-49ed-8cd2-73917bace346</ProjectGuid>
<OutputPath>$(MSBuildThisFileDirectory)out\controls</OutputPath>
</PropertyGroup>
Articles connexes
Application Lifecycle Management (ALM) avec Microsoft Power Platform
Référence d'API d'infrastructure Power Apps component framework
Création de votre premier composant
Déboguer des composants de code