Freigeben über


Lernprogramm: Erstellen einer Canvas-App-Datasetkomponente

In diesem Lernprogramm erstellen Sie eine Canvas-App-Dataset-Codekomponente, stellen sie bereit, fügen sie einem Bildschirm hinzu, und testen Sie die Komponente mit Visual Studio Code. Die Codekomponente zeigt ein seitenweise, bildlauffähiges Datensatzraster an, das sortierbare und filterbare Spalten bereitstellt. Sie ermöglicht auch die Hervorhebung bestimmter Zeilen durch Konfigurieren einer Indikatorspalte. Dies ist eine häufige Anforderung von App-Machern und kann komplex sein, um sie mithilfe nativer Canvas-App-Komponenten zu implementieren. Codekomponenten können für die Arbeit an Canvas- und modellgesteuerten Apps geschrieben werden. Diese Komponente wird jedoch speziell für die Verwendung in Canvas-Apps geschrieben.

Zusätzlich zu diesen Anforderungen stellen Sie außerdem sicher, dass die Codekomponente den bewährten Methodenleitfaden folgt:

  1. Verwendung der Microsoft Fluent-Benutzeroberfläche
  2. Lokalisierung der Codekomponentenbeschriftungen sowohl bei der Entwurfs- als auch zur Laufzeit
  3. Versicherung, dass die Codekomponente in der Breite und Höhe rendert, die vom Bildschirm der übergeordneten Canvas-App bereitgestellt wird
  4. Überlegung, der App erstellenden Person zu erlauben, die Benutzeroberfläche so weit wie möglich mithilfe von Eingabeeigenschaften und externen App-Elementen anzupassen

Canvas Grid Demo

Hinweis

Bevor Sie beginnen, stellen Sie sicher, dass Sie alle erforderlichen Komponenten installiert haben.

Code

Sie können das vollständige Beispiel aus PowerApps-Samples/component-framework/CanvasGridControl/herunterladen.

Erstellen eines neuen pcfproj Projekts

  1. Erstellen Sie einen neuen Ordner, der für Ihre Codekomponente verwendet werden soll. Beispiel: C:\repos\CanvasGrid.

  2. Öffnen Sie Visual Studio Code und wählen Sie dann Datei>Ordner öffnen und wählen Sie den CanvasGrid Ordner aus. Wenn Sie die Windows Explorer-Erweiterungen während der Installation von Visual Studio Code hinzugefügt haben, können Sie die Kontextmenüoption "Mit Code öffnen " im Ordner verwenden. Sie können auch jeden Ordner in Visual Studio Code laden, indem Sie code . an der Eingabeaufforderung verwenden, wenn das aktuelle Verzeichnis auf diesen Speicherort eingestellt ist.

  3. Verwenden Sie in einem neuen Visual Studio Code PowerShell-Terminal (Terminal>New Terminal) den Befehl pac pcf init , um ein neues Codekomponentenprojekt zu erstellen:

    pac pcf init --namespace SampleNamespace --name CanvasGrid --template dataset
    

    oder verwenden Sie die Kurzform:

    pac pcf init -ns SampleNamespace -n CanvasGrid -t dataset
    
  4. Dadurch werden dem aktuellen Ordner neue pcfproj und verwandte Dateien hinzugefügt, einschließlich einer packages.json , die die benötigten Module definiert. Um die erforderlichen Module zu installieren, verwenden Sie npm install:

    npm install
    

    Hinweis

    Wenn Sie die Nachricht erhalten, stellen Sie sicher, The term 'npm' is not recognized as the name of a cmdlet, function, script file, or operable program.dass Sie alle erforderlichen Komponenten installiert haben, insbesondere node.js (LTS-Version wird empfohlen).

    Canvas-Datensatzraster

Die Vorlage enthält eine index.ts Datei zusammen mit verschiedenen Konfigurationsdateien. Dies ist der Ausgangspunkt Ihrer Codekomponente und enthält die in der Komponentenimplementierung beschriebenen Lebenszyklusmethoden.

Installieren der Microsoft Fluent-Benutzeroberfläche

Sie verwenden Microsoft Fluent UI und React zum Erstellen der Benutzeroberfläche, daher müssen Sie diese als Abhängigkeiten installieren. Verwenden Sie folgendes am Terminal:

npm install react react-dom @fluentui/react

Dadurch werden die Module dem packages.json-Ordner hinzugefügt und in den node_modules-Ordner installiert. Sie werden node_modules nicht in die Quellcodeverwaltung einchecken, da alle erforderlichen Module mithilfe von npm install wiederhergestellt werden können.

Einer der Vorteile der Microsoft Fluent-Benutzeroberfläche besteht darin, dass sie eine konsistente und hochgradig barrierefreie Benutzeroberfläche bietet.

Konfigurieren eslint

Die von pac pcf init verwendete Vorlage installiert die eslint-Module in Ihrem Projekt und konfiguriert es durch Hinzufügen einer .eslintrc.json-Datei. Eslint Erfordert jetzt die Konfiguration für TypeScript- und React-Codierungsstile. Weitere Informationen: Linting – Bewährte Methoden und Anleitungen für Codekomponenten.

Definieren der Dataseteigenschaften

Die CanvasGrid\ControlManifest.Input.xml Datei definiert die Metadaten, die das Verhalten der Codekomponente beschreiben. Das Steuerelement-Attribut enthält bereits den Namespace und den Namen der Komponente.

Tipp

Sie können das XML möglicherweise leichter lesen, wenn Sie es so formatieren, dass Attribute in eigenen Zeilen erscheinen. Suchen und installieren Sie ein XML-Formatierungstool Ihrer Wahl im Visual Studio Code Marketplace: Suchen Sie nach XML-Formatierungserweiterungen.

Die folgenden Beispiele wurden mit Attributen in separaten Zeilen formatiert, damit sie leichter lesbar sind.

Sie müssen die Datensätze definieren, an die die Codekomponente gebunden werden kann, indem Sie Folgendes innerhalb des control Elements hinzufügen und das vorhandene data-set Element ersetzen:

<data-set name="sampleDataSet"
  display-name-key="Dataset_Display_Key">
</data-set>

Der Datensatz wird an eine Datenquelle gebunden, wenn die Codekomponente einer Canvas-App hinzugefügt wird. Der Eigenschaftensatz gibt an, dass der Benutzer eine der Spalten dieses Datensatzes konfigurieren muss, damit sie als Indikator für die Zeilenhervorhebung verwendet wird.

Tipp

Sie können mehrere Datasetelemente angeben. Dies könnte nützlich sein, wenn Sie ein DataSet suchen, aber eine Liste von Datensätzen mit einem zweiten anzeigen möchten.

Definieren der Eingabe- und Ausgabeeigenschaften

Zusätzlich zum Dataset können Sie die folgenden Eingabeeigenschaften bereitstellen:

  • HighlightValue– Ermöglicht dem App-Entwickler, einen Wert bereitzustellen, der mit der Spalte verglichen werden kann, die als HighlightIndicatorproperty-set definiert ist. Wenn die Werte gleich sind, sollte die Zeile hervorgehoben werden.
  • HighlightColor – Ermöglicht dem App-Maker, eine Farbe auszuwählen, die zum Hervorheben von Zeilen verwendet werden soll.

Tipp

Beim Erstellen von Codekomponenten für die Verwendung in Canvas-Apps wird empfohlen, Eingabeeigenschaften für die Formatierung allgemeiner Aspekte Ihrer Codekomponenten bereitzustellen.

Zusätzlich zu den Eingabeeigenschaften wird eine benannte AusgabeeigenschaftFilteredRecordCount aktualisiert (und löst das OnChange Ereignis aus), wenn die Zeilenanzahl aufgrund einer Filteraktion, die innerhalb der Codekomponente angewendet wird, geändert wird. Dies ist hilfreich, wenn Sie eine No Rows Found Nachricht in der übergeordneten App anzeigen möchten.

Hinweis

In Zukunft unterstützen Codekomponenten benutzerdefinierte Ereignisse, sodass Sie ein bestimmtes Ereignis definieren können, anstatt das generische OnChange Ereignis zu verwenden.

Um diese drei Eigenschaften zu definieren, fügen Sie Folgendes zu der CanvasGrid\ControlManifest.Input.xml-Datei unter dem data-set-Element hinzu:

<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"/>

Speichern Sie diese Datei, und verwenden Sie dann in der Befehlszeile Folgendes:

npm run build

Hinweis

Wenn beim Ausführen von npm run build einen solcher Fehler zurückgegeben wird:

[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

Öffnen Sie index.ts Datei, und fügen Sie Folgendes hinzu: // eslint-disable-next-line no-undefdirekt über der Zeile:
import DataSetInterfaces = ComponentFramework.PropertyHelper.DataSetApi;

Führen Sie npm run build erneut aus.

Nachdem die Komponente erstellt wurde, sehen Sie Folgendes:

  • Ihrem Projekt wird eine automatisch generierte Datei CanvasGrid\generated\ManifestTypes.d.ts hinzugefügt. Diese wird im Rahmen des Erstellen-Prozesses aus dem ControlManifest.Input.xml und stellt die Typen für die Interaktion mit den Eingabe-/Ausgabeeigenschaften bereit.

  • Die Buildausgabe wird dem out Ordner hinzugefügt. Das bundle.js ist das transpilierte JavaScript, das im Browser ausgeführt wird, und das ControlManifest.xml ist eine neu formatierte Version der ControlManifest.Input.xml-Datei, die während der Bereitstellung verwendet wird.

    Hinweis

    Ändern Sie den Inhalt der Ordner generated und out nicht direkt. Sie werden im Rahmen des Build-Prozesses überschrieben.

Hinzufügen der React-Komponente "Grid Fluent UI"

Wenn die Codekomponente React verwendet, muss eine einzelne Stammkomponente vorhanden sein, die innerhalb der UpdateView-Methode gerendert wird. Fügen Sie im CanvasGrid Ordner eine neue TypeScript-Datei namens Grid.tsxhinzu, und fügen Sie den folgenden Inhalt hinzu:

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';

Hinweis

Die Datei hat die Erweiterung tsx , die eine TypeScript-Datei ist, die die von React verwendete XML-Stilsyntax unterstützt. Sie wird durch den Buildprozess in Standard-JavaScript kompiliert.

Rasterentwurfsnotizen

Dieser Abschnitt enthält Allgemeines zum Entwurf der Grid.tsx Komponente.

Es handelt sich um eine funktionale Komponente

Dies ist eine React-Funktionskomponente, aber auch eine Klassenkomponente. Dies basiert auf Ihrem bevorzugten Codierungsstil. Klassenkomponenten und funktionsbezogene Komponenten können auch im selben Projekt gemischt werden. Sowohl Funktions- als auch Klassenkomponenten verwenden die tsx von React verwendete XML-Stilsyntax. Weitere Informationen: Funktions- und Klassenkomponenten

Minimieren Sie die Größe von bundle.js

Beim Importieren der Fluent UI-Komponente ChoiceGroup mit pfadbasierten Importen anstelle von:

import { 
    DetailsList, 
    ConstrainMode, 
    DetailsListLayoutMode, 
    IColumn, 
    IDetailsHeaderProps, 
    Stack 
} from "@fluentui/react";

Dieser Code verwendet:

import {
    DetailsList,
    ConstrainMode,
    DetailsListLayoutMode,
    IColumn,
    IDetailsHeaderProps,
} from '@fluentui/react/lib/DetailsList';
import { Stack } from '@fluentui/react/lib/Stack';

Auf diese Weise wird die Größe des Bundles kleiner, was zu geringeren Kapazitätsanforderungen und einer besseren Laufzeitleistung führt.

Eine Alternative wäre Tree Shaking.

Destrukturierungszuweisung

Dieser Code:

export const Grid = React.memo((props: GridProps) => {
    const {
        records,
        sortedRecordIds,
        columns,
        width,
        height,
        hasNextPage,
        hasPreviousPage,
        sorting,
        filtering,
        currentPage,
        itemsLoading,
    } = props;

Verwendet die Destrukturierungszuweisung. Auf diese Weise extrahieren Sie die zum Rendern erforderlichen Attribute aus den Props, anstatt diese bei jeder Verwendung mit props. zu versehen.

Dieser Code verwendet darüber hinaus React.memo, um die Funktionskomponente so zu verpacken, dass sie nicht gerendert wird, es sei denn, die Eingabeeigenschaften werden geändert.

Verwendung von React.useMemo

React.useMemo wird an verschiedenen Stellen verwendet, um sicherzustellen, dass das erstellte Item-Array nur verändert wird, wenn die Eingabe-Props options oder configuration geändert werden. Dies ist eine bewährte Methode für Funktionskomponenten, die unnötiges Rendering der untergeordneten Komponenten reduziert.

Weitere zu beachtende Elemente:

  • Die DetailsList in einem Stack wird verpackt, da Sie später ein Fußzeilenelement mit den Paging-Steuerelementen hinzufügen.
  • Die Fluent UI-Komponente Sticky wird verwendet, um die Kopfzeilenspalten (mithilfe von onRenderDetailsHeader) umzuschließen, damit sie beim Scrollen des Rasters sichtbar bleiben.
  • setKey wird zusammen mit initialFocusedIndex an DetailsList weitergegeben, sodass beim Wechsel der aktuellen Seite die Bildlaufposition und Auswahl zurückgesetzt werden.
  • Die Funktion onRenderItemColumn wird zum Rendern des Zellinhalts verwendet. Es akzeptiert Zeilenelement und verwendet getFormattedValue , um den Anzeigewert der Spalte zurückzugeben. Die getValue-Methode gibt einen Wert zurück, den Sie verwenden können, um ein alternatives Rendering bereitzustellen. Der Vorteil getFormattedValue besteht darin, dass sie eine formatierte Zeichenfolge für Spalten von Nicht-Zeichenfolgentypen wie Datumsangaben und Nachschlagevorgängen enthält.
  • Der gridColumns Block ordnet die Objektform der Spalten, die vom Dataset-Kontext bereitgestellt werden, dem Shape zu, das von der DetailsList Spaltenprop erwartet wird. Da dies im React.useMemo-Hook umschlossen ist, ändert sich die Ausgabe nur, wenn sich die columns- oder sorting-Props ändern. Sie können die Sortier- und Filtersymbole in den Spalten anzeigen, in denen die vom Codekomponentenkontext bereitgestellten Sortier- und Filterdetails mit der zugeordneten Spalte übereinstimmen. Die Spalten werden mithilfe der column.order Eigenschaft sortiert, um sicherzustellen, dass sie sich im Raster wie vom App-Maker definiert in der richtigen Reihenfolge befinden.
  • Sie erhalten einen internen Status für isComponentLoading in unserer React-Komponente. Dies liegt daran, dass Sie, wenn der Benutzer Sortier- und Filteraktionen auswählt, das Raster als visuellen Hinweis ausgrauen können, bis die sortedRecordIds aktualisiert werden und der Zustand zurückgesetzt wird. Es gibt eine zusätzliche Eingabeeigenschaft, itemsLoading die der dataset.loading-Eigenschaft zugeordnet wird, die vom Datasetkontext bereitgestellt wird. Beide Flags werden verwendet, um den visuellen Ladehinweis zu steuern, der mithilfe der Fluent-UI-Komponente Overlay implementiert wird.

Aktualisieren von index.ts

Der nächste Schritt besteht darin, Änderungen an der index.ts-Datei vorzunehmen, um die in Grid.tsx. definierten Eigenschaften abzugleichen.

Hinzufügen von Importanweisungen und Initialisieren von Symbolen

Ersetzen Sie im Header von index.ts die bestehenden Importe durch Folgendes:

import {IInputs, IOutputs} from './generated/ManifestTypes';
import DataSetInterfaces = ComponentFramework.PropertyHelper.DataSetApi;
type DataSet = ComponentFramework.PropertyTypes.DataSet;

Hinweis

Der Import von initializeIcons ist erforderlich, da dieser Code den Fluent UI-Symbolsatz verwendet. Sie rufen initializeIcons auf, um die Symbole im Testkabelbaum zu laden. Innerhalb von Canvas-Apps werden sie bereits initialisiert.

Hinzufügen von Feldern zur CanvasGrid-Klasse

Fügen Sie der Klasse die folgenden Felder hinzu CanvasGrid :

export class CanvasGrid implements ComponentFramework.StandardControl<IInputs, IOutputs> {

    /**
     * Empty constructor.
     */
    constructor() {

    }

Aktualisieren der Init-Methode

Fügen Sie Folgendes hinzu:init

public init(
    context: ComponentFramework.Context<IInputs>, 
    notifyOutputChanged: () => void, 
    state: ComponentFramework.Dictionary, 
    container: HTMLDivElement): void {
    // Add control initialization code
}

Die init Funktion wird aufgerufen, wenn die Codekomponente zuerst auf einem App-Bildschirm initialisiert wird. Sie speichern einen Verweis auf Folgendes:

  • notifyOutputChanged: Dies ist der Rückruf, vorausgesetzt, der Aufruf dient dazu, die Canvas-App zu benachrichtigen, dass sich eine der Eigenschaften geändert hat.
  • container:Dies ist das DOM-Element, dem Sie die Benutzeroberfläche Ihrer Code-Komponente hinzufügen.
  • resources: Dies wird verwendet, um lokalisierte Zeichenfolgen in der Sprache des aktuellen Benutzers abzurufen.

Die Eigenschaft "context.mode.trackContainerResize(true)" wird verwendet, damit updateView aufgerufen wird, wenn sich die Größe der Codekomponente ändert.

Hinweis

Derzeit gibt es keine Möglichkeit, festzustellen, ob die Codekomponente innerhalb der Testumgebung ausgeführt wird. Sie müssen erkennen, ob das control-dimensionsdiv Element als Indikator vorhanden ist.

Aktualisieren der UpdateView-Methode

Fügen Sie Folgendes hinzu:updateView

public updateView(context: ComponentFramework.Context<IInputs>): void {
    // Add code to update control view
}

Sie können folgendes sehen:

  • Sie rufen React.createElement auf, und übergeben den Verweis auf den DOM-Container, den Sie innerhalb der init Funktion erhalten haben.
  • Die Grid Komponente ist innerhalb Grid.tsx der Datei definiert und wird am Anfang der Datei importiert.
  • Der allocatedWidth und allocatedHeight werden vom übergeordneten Kontext bereitgestellt, wenn sie sich ändern (z. B. ändert die App die Größe der Code-Komponente oder Sie den Vollbildmodus aktivieren), da Sie einen Aufruf von trackContainerResize(true) innerhalb der init Funktion vorgenommen haben.
  • Sie können erkennen, wann neue Zeilen angezeigt werden sollen, wenn das updatedProperties-Array die dataset Zeichenfolge enthält.
  • In der Testumgebung ist das updatedProperties-Array nicht ausgefüllt, Sie können also das isTestHarness-Kennzeichen verwenden, das Sie in der init-Funktion zum Kurzschließen der Logik festlegen, die sortedRecordId und records bestimmt. Sie behalten einen Verweis auf die aktuellen Werte, bis sie sich ändern, damit Sie diese bei der Übergabe an die untergeordnete Komponente nicht ändern, es sei denn, die Daten müssen erneut gerendert werden.
  • Da die Codekomponente den Zustand der angezeigten Seite verwaltet, wird die Seitenzahl zurückgesetzt, wenn der übergeordnete Kontext die Datensätze auf die erste Seite zurücksetzt. Sie wissen, wann Sie wieder auf der ersten Seite sind, wenn hasPreviousPage "false" ist.

Aktualisieren der Zerstörungsmethode

Schließlich müssen Sie aufräumen, wenn die Codekomponente zerstört wird:

public destroy(): void {
    // Add code to cleanup control if necessary
}

Starten des Testgerüsts

Stellen Sie sicher, dass alle Dateien gespeichert sind und verwenden Sie am Terminal Folgendes:

npm start watch

Sie müssen die Breite und Höhe festlegen, um das Codekomponentenraster anzuzeigen, das mit den drei Beispieldatensätzen aufgefüllt wird. Sie können dann eine Reihe von Datensätzen in eine CSV-Datei aus Dataverse exportieren und dann mit in der Testumgebung mit Dateneingaben>Datensatzbereich laden:

Test-Harnisch

Hier sind einige durch Trennzeichen getrennte Beispieldaten, die Sie in einer .csv-Datei speichern und verwenden können:

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)

Hinweis

Es gibt nur eine einzelne Spalte, die in der Testumgebung angezeigt wird, unabhängig von den Spalten, die Sie in der geladenen CSV-Datei angeben. Dies liegt daran, dass das Testgerüst nur property-set zeigt, wenn eines definiert ist. Wenn keine property-set definiert ist, werden alle Spalten in der geladenen CSV-Datei aufgefüllt.

Zeilenauswahl hinzufügen

Obwohl die Fluent-Benutzeroberfläche DetailsList das Auswählen von Datensätzen standardmäßig zulässt, sind die ausgewählten Datensätze nicht mit der Ausgabe der Codekomponente verknüpft. Sie benötigen die Eigenschaften Selected und SelectedItems, um die ausgewählten Datensätze in einer Canvas-App anzuzeigen, damit verwandte Komponenten aktualisiert werden können. In diesem Beispiel erlauben Sie die Auswahl nur eines einzelnen Elements gleichzeitig, sodass SelectedItems nur ein einzelner Datensatz enthalten wird.

Aktualisieren von Grid.tsx-Importen

Fügen Sie Folgendes zu den Importen innerhalb Grid.tsx hinzu:

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';

Hinzufügen von setSelectedRecords zu GridProps

Fügen Sie der Schnittstelle GridProps im Inneren von Grid.tsx Folgendes hinzu:

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;
}

Hinzufügen der eigenschaft setSelectedRecords zu Grid

Aktualisieren Sie in der Grid.tsx-Funktionskomponente die Destrukturierung der props, um die neue Eigenschaft setSelectedRecords hinzuzufügen.

export const Grid = React.memo((props: GridProps) => {
    const {
        records,
        sortedRecordIds,
        columns,
        width,
        height,
        hasNextPage,
        hasPreviousPage,
        sorting,
        filtering,
        currentPage,
        itemsLoading,
    } = props;

Fügen Sie direkt darunter Folgendes hinzu:

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,
  });
});

Die React.useCallback- und die useConst-Hooks werden verwendet, um sicherzustellen, dass diese Werte zwischen Renderings nicht mutieren und kein unnötiges Rendering untergeordneter Komponenten verursachen.

Der useForceUpdate-Hook stellt sicher, dass die Komponente beim Aktualisieren der Auswahl erneut gerendert wird, um die aktualisierte Auswahlanzahl widerzuspiegeln.

Auswahl zur Detailliste hinzufügen

Das selection erstellte Objekt, um den Status der Auswahl beizubehalten, wird dann an die DetailsList Komponente übergeben:

<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>

Definieren des setSelectedRecords-Rückrufs

Sie müssen den neuen setSelectedRecords Rückruf innerhalb von index.ts definieren und an die Komponente Grid übergeben. Fügen Sie am Anfang der CanvasGrid Klasse Folgendes hinzu:

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;

Hinweis

Die Methode wird als Pfeilfunktion definiert, um sie an die aktuelle this Instanz der Codekomponente zu binden.

Der Aufruf von setSelectedRecordIds informiert die Canvas-App, dass sich die Auswahl geändert hat, sodass andere Komponenten, die auf SelectedItems und Selected verweisen, aktualisiert werden.

Neuen Callback zu den Eingabeprops hinzufügen

Fügen Sie schließlich den neuen Rückruf den Eingabeeigenschaften der Grid-Komponente in der updateView-Methode hinzu:

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
);

Aufrufen des OnSelect Ereignisses

Es gibt ein Muster für Canvas-Apps, bei dem, wenn eine Galerie oder ein Raster eine Elementauswahl aufgerufen hat (z. B. das Auswählen eines Chevron-Symbols), das OnSelect Ereignis ausgelöst wird. Sie können dieses Muster mithilfe der openDatasetItem-Methode des Datasets implementieren.

Hinzufügen von onNavigate zur GridProps-Schnittstelle

Wie zuvor fügen Sie eine zusätzliche Rückrufeigenschaft auf der Grid-Komponente hinzu, indem Sie Folgendes zu der GridProps-Schnittstelle in Grid.tsx hinzufügen:

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;
}

Add onNavigate to Grid props

Auch hier müssen Sie die neue Eigenschaft der Destrukturierung der Eigenschaften hinzufügen:

export const Grid = React.memo((props: GridProps) => {
  const {
    records,
    sortedRecordIds,
    columns,
    width,
    height,
    hasNextPage,
    hasPreviousPage,
    sorting,
    filtering,
    currentPage,
    itemsLoading,
    setSelectedRecords,
  } = props;

OnItemInvoked zu DetailsList hinzufügen

Die DetailList hat eine Rückrufeigenschaft namens onItemInvoked, an die Sie Ihren Rückruf wiederum weiterleiten:

<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>

Hinzufügen der onNavigate-Methode in index.ts

Fügen Sie die onNavigate-Methode direkt unter die setSelectedRecords-Methode ein:index.ts

onNavigate = (
  item?: ComponentFramework.PropertyHelper.DataSetApi.EntityRecord
): void => {
  if (item) {
    this.context.parameters.records.openDatasetItem(item.getNamedReference());
  }
};

Dadurch wird einfach die openDatasetItem Methode für den Datasetdatensatz aufgerufen, sodass die Codekomponente das OnSelect Ereignis auslöst. Die Methode wird als Pfeilfunktion definiert, um sie an die aktuelle this Instanz der Codekomponente zu binden.

Sie müssen diesen Rückruf an die Grid-Komponenteneigenschaften in der updateView-Methode übergeben:

    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
    );

Wenn Sie alle Dateien speichern, wird das Testgerüst neu geladen. Verwenden Sie Ctrl + Shift + I (oder F12) und Datei öffnen (Ctrl + P) für die Suche nach index.ts und Sie können einen Haltepunkt in der onNavigate-Methode platzieren. Machen Sie einen Doppelklick auf eine Zeile (oder markieren Sie sie mit den Cursortasten und Enter) und der Haltepunkt erreicht, da die DetailsList einen onNavigate-Rückruf auslöst.

OnNavigate in index.ts mit Canvasdatenraster debuggen

Es gibt einen Verweis auf _this, da die Funktion als Pfeilfunktion definiert ist und in eine JavaScript-Schließung transpiliert wurde, um die Instanz von this aufzugreifen.

Lokalisierung hinzufügen

Bevor Sie fortfahren, müssen Sie der Codekomponente Ressourcenzeichenfolgen hinzufügen, damit Sie lokalisierte Zeichenfolgen für Nachrichten wie Paging, Sortieren und Filtern verwenden können. Fügen Sie eine neue Datei CanvasGrid\strings\CanvasGrid.1033.resx hinzu, und verwenden Sie den Visual Studio-Ressourcen-Editor oder Visual Studio Code mit einer Erweiterung, um Folgendes einzugeben:

Name Wert
Records_Dataset_Display Datensätze
FilteredRecordCount_Disp Gefilterte Datensatzanzahl
FilteredRecordCount_Desc Die Anzahl der Datensätze nach dem Filtern
HighlightValue_Disp Hervorhebungswert
HighlightValue_Desc Der Wert, der angibt, dass eine Zeile hervorgehoben werden soll
HighlightColor_Disp Hervorhebungsfarbe
HighlightColor_Desc Die Farbe, mit der eine Zeile hervorgehoben werden soll
HighlightIndicator_Disp Indikatorfeld hervorheben
HighlightIndicator_Desc Festlegen auf den Namen des Felds, das mit dem Hervorhebungswert verglichen werden soll
Label_Grid_Footer Seite {0} ({1} ausgewählt)
Label_SortAZ A bis Z
Label_SortZA Z bis A
Label_DoesNotContainData Enthält keine Daten
Label_ShowFullScreen Vollbild anzeigen

Tipp

Es wird nicht empfohlen, resx Dateien direkt zu bearbeiten. Verwenden Sie stattdessen entweder den Ressourcen-Editor von Visual Studio oder eine Erweiterung für Visual Studio Code. Eine Visual Studio Code-Erweiterung finden: den Visual Studio Marketplace nach einem Resx-Editor durchsuchen

Die Daten für diese Datei können auch festgelegt werden, indem sie die CanvasGrid.1033.resx Datei im Editor öffnen und den folgenden XML-Inhalt kopieren:

<?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>

Sie haben Ressourcenzeichenfolgen für die input/output-Eigenschaften und das dataset und den verbundenen property-set. Diese werden bei der Entwurfszeit in Power Apps Studio basierend auf der Browsersprache des Herstellers verwendet. Sie können auch Bezeichnungszeichenfolgen hinzufügen, die zur Laufzeit mithilfe von getString abgerufen werden können. Weitere Informationen: Implementieren der Lokalisierungs-API-Komponente.

Fügen Sie diese neue Ressourcendatei der ControlManifest.Input.xml Datei innerhalb des resources Elements hinzu:

<resources>
   <code path="index.ts"
      order="1" />
</resources>

Hinzufügen von Spaltensortierung und -filterung

Wenn Sie es dem Benutzer ermöglichen möchten, mithilfe von Rasterspaltenüberschriften zu sortieren und zu filtern, bietet die Fluent-Benutzeroberfläche DetailList eine einfache Möglichkeit zum Hinzufügen von Kontextmenüs zu den Spaltenüberschriften.

Hinzufügen von "onSort" und "onFilter" zu "GridProps"

Fügen Sie zunächst onSort und onFilter der GridProps-Schnittstelle innerhalb von Grid.tsx hinzu, um Rückruffunktionen zum Sortieren und Filtern bereitzustellen.

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, onFilter und Ressourcen zu Eigenschaften hinzufügen

Fügen Sie dann diese neuen Eigenschaften zusammen mit dem resources-Verweis (damit Sie lokalisierte Beschriftungen zum Sortieren und Filtern abrufen können) auf die Destrukturierung der Eigenschaften hinzu:

export const Grid = React.memo((props: GridProps) => {
  const {
    records,
    sortedRecordIds,
    columns,
    width,
    height,
    hasNextPage,
    hasPreviousPage,
    sorting,
    filtering,
    currentPage,
    itemsLoading,
    setSelectedRecords,
    onNavigate,
  } = props;

KontextbezogeneMenu-Komponenten importieren

Sie müssen einige Importe am Anfang Grid.tsx hinzufügen, damit Sie die ContextualMenu-Komponente, die von Fluent UI bereitgestellt wird, verwenden können. Sie können pfadbasierte Importe verwenden, um die Größe des Bündels zu verringern.

import { ContextualMenu, DirectionalHint, IContextualMenuProps } from '@fluentui/react/lib/ContextualMenu';

Hinzufügen von Renderingfunktionen für Kontextmenüs

Fügen Sie nun die Funktionalität des Kontextmenü-Renderings zu Grid.tsx direkt unterhalb der Zeile hinzu.
const [isComponentLoading, setIsLoading] = React.useState<boolean>(false);:

const [isComponentLoading, setIsLoading] = React.useState<boolean>(false);

Sie sehen Folgendes:

  • Der contextualMenuProps Zustand steuert die Sichtbarkeit des Kontextmenüs, das mithilfe der Fluent-UI-Komponente ContextualMenu gerendert wird.
  • Dieser Code stellt einen einfachen Filter bereit, um nur Werte anzuzeigen, bei denen das Feld keine Daten enthält. Sie können dies erweitern, um zusätzliche Filterung bereitzustellen.
  • Dieser Code verwendet resources.getString , um Bezeichnungen im Kontextmenü anzuzeigen, die lokalisiert werden können.
  • Der React.useCallback Hook, ähnlich wie React.useMemo, stellt sicher, dass die Rückrufe nur verändert werden, wenn sich die abhängigen Werte ändern. Dadurch wird das Rendering von untergeordneten Komponenten optimiert.

Fügen Sie neuen Kontextmenüfunktionen zu den Spaltenauswahl- und Kontextmenüereignissen hinzu

Fügen Sie diese neuen Kontextmenüfunktionen zu den Spaltenauswahl- und Kontextmenüereignissen hinzu. Aktualisieren Sie const gridColumns, um die onColumnContextMenu und onColumnClick Callbacks hinzuzufügen.

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]);

Hinzufügen eines Kontextmenüs zur gerenderten Ausgabe

Damit das Kontextmenü angezeigt werden kann, müssen Sie es der gerenderten Ausgabe hinzufügen. Fügen Sie Folgendes direkt unterhalb der DetailsList Komponente in der zurückgegebenen Ausgabe hinzu:

<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>

Fügen Sie Funktionen zur Sortierung und Filterung hinzu.

Nachdem Sie die Sortier- und Filterbenutzeroberfläche hinzugefügt haben, müssen Sie die Callbacks zu index.ts hinzufügen, um die Sortier- und Filtervorgänge für die Datensätze auszuführen, die an die Codekomponente gebunden sind. Fügen Sie Folgendes index.ts direkt unterhalb der onNavigate Funktion hinzu:

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();
};

Sie sehen Folgendes:

  • Die Sortierung und der Filter werden mithilfe der Sortier - und Filtereigenschaften auf das Dataset angewendet.
  • Beim Ändern der Sortierspalten müssen die vorhandenen Sortierdefinitionen mithilfe von Pop entfernt werden, anstatt das Sortierarray selbst zu ersetzen.
  • Die Aktualisierung muss aufgerufen werden, nachdem Sortierung und Filterung angewendet wurden. Wenn gleichzeitig ein Filter und eine Sortierung angewendet werden, muss die Aktualisierung nur einmal aufgerufen werden.

Hinzufügen von OnSort- und OnFilter-Rückrufen zum Rasterrendering

Schließlich können Sie diese beiden Callbacks an den Grid Render-Aufruf übergeben:

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
);

Hinweis

An diesem Punkt können Sie das Testgerüst nicht mehr verwenden, da es keine Unterstützung für Sortierung und Filterung bietet. Später können Sie mithilfe von pac pcf push bereitstellen und dann zum Testen zu einer Canvas-App hinzufügen. Wenn Sie möchten, können Sie mit diesem Schritt fortfahren, um zu sehen, wie die Codekomponente in Canvas-Apps aussieht.

Aktualisieren Sie die Ausgabeeigenschaft FilteredRecordCount

Da das Raster jetzt Datensätze intern filtern kann, ist es wichtig, der Canvas-App zu melden, wie viele Datensätze angezeigt werden. Dies ist so, dass Sie eine Meldung vom Typ "Keine Datensätze" anzeigen können.

Tipp

Sie können dies intern in der Codekomponente implementieren, allerdings wird empfohlen, dass möglichst viele Aspekte der Benutzeroberfläche in der Canvas-App überlassen werden, da dies dem App-Entwickler mehr Flexibilität bietet.

Sie haben bereits eine Ausgabeeigenschaft namens FilteredRecordCount im ControlManifest.Input.xml definiert. Wenn die Filterung stattfindet und die gefilterten Datensätze geladen werden, wird die updateView Funktion mit Zeichenfolge dataset im updatedProperties-Array aufgerufen. Wenn sich die Anzahl der Datensätze geändert hat, müssen Sie einen Aufruf durchführen notifyOutputChanged , damit die Canvas-App weiß, dass sie alle Steuerelemente aktualisieren muss, die die FilteredRecordCount Eigenschaft verwenden. Fügen Sie in der updateView-Methode von index.ts Folgendes direkt über dem ReactDOM.render und unter dem allocatedHeight hinzu:

const allocatedHeight = parseInt(
    context.mode.allocatedHeight as unknown as string
);

Hinzufügen von "FilteredRecordCount" zu "getOutputs"

Dadurch wird die filteredRecordCount Code-Komponentenklasse aktualisiert, die Sie zuvor definiert haben, falls diese von den neu empfangenen Daten abweicht. Nachdem notifyOutputChanged aufgerufen ist, müssen Sie sicherstellen, dass der Wert zurückgegeben wird, wenn getOutputs aufgerufen ist. Aktualisieren Sie daher die getOutputs-Methode folgendermaßen:

public getOutputs(): IOutputs {
    return {};
}

Hinzufügen von Paging zum Raster

Bei großen Datasets teilen Canvas-Apps die Datensätze auf mehrere Seiten auf. Sie können eine Fußzeile hinzufügen, die Seitennavigationssteuerelemente anzeigt. Jede Schaltfläche wird mithilfe einer Fluent-Benutzeroberfläche IconButtongerendert, die Sie importieren müssen.

IconButton zu den Importen hinzufügen

Fügen Sie dies zu den Importen innerhalb Grid.tsxhinzu:

import { IconButton } from '@fluentui/react/lib/Button';

StringFormat-Funktion hinzufügen

Im folgenden Schritt werden Möglichkeiten hinzugefügt, um das Format für die Seitenindikatorbezeichnung aus den Ressourcenzeichenfolgen ("Page {0} ({1} Selected)") zu laden und das Format mithilfe einer einfachen stringFormat-Funktion zu verwenden. Diese Funktion kann sich auch in einer separaten Datei befinden und aus Gründen der Einfachheit zwischen Ihren Komponenten gemeinsam genutzt werden:

Fügen Sie es in diesem Tutorial oben in Grid.tsx direkt unter type DataSet ... hinzu.

function stringFormat(template: string, ...args: string[]): string {
  for (const k in args) {
    template = template.replace("{" + k + "}", args[k]);
  }
  return template;
}

Hinzufügen von Paging-Schaltflächen

Grid.tsx Fügen Sie das folgende Stack.Item unter dem vorhandenen Stack.Item hinzu, das das ScrollablePane enthält:

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>
);

Sie sehen Folgendes:

  • Der Stack stellt sicher, dass die Fußzeile unterhalb der DetailsList erscheint. Das grow Attribut wird verwendet, um sicherzustellen, dass das Raster erweitert wird, um den verfügbaren Platz auszufüllen.
  • Sie laden das Format für die Seitenindikatorbezeichnung aus den Ressourcenzeichenfolgen ("Page {0} ({1} Selected)") und formatieren sie mithilfe der stringFormat Funktion, die Sie im vorherigen Schritt hinzugefügt haben.
  • Sie können alt-Text für Barrierefreiheit auf den Paging-IconButtons bereitstellen.
  • Die Formatvorlage auf der Fußzeile kann auch mithilfe eines CSS-Klassennamens angewendet werden, der auf eine CSS-Datei verweist, die der Codekomponente hinzugefügt wurde.

Rückrufeigenschaften zur Unterstützung von Paging hinzufügen

Als Nächstes müssen Sie die fehlenden loadFirstPage, loadNextPage und loadPreviousPage Callback-Props hinzufügen.

Fügen Sie der GridProps Schnittstelle Folgendes hinzu:

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;
}

Hinzufügen neuer Paging-Eigenschaften zum Raster

Fügen Sie diese neuen Eigenschaften zu den Eigenschaften zur Destrukturierung hinzu:

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;

Hinzufügen von Rückrufen zu index.ts

Fügen Sie diese Rückrufe zu index.ts unterhalb der onFilter-Methode hinzu:

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);
};

Aktualisieren Sie dann den Grid Renderingaufruf so, dass diese Rückrufe eingeschlossen werden:

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
);

Unterstützung im Vollbildmodus hinzufügen

Codekomponenten bieten die Möglichkeit, im Vollbildmodus anzuzeigen. Dies ist besonders bei kleinen Bildschirmgrößen hilfreich oder wenn der verfügbare Platz für die Codekomponente innerhalb eines Canvas-App-Bildschirms begrenzt ist.

Um den Vollbildmodus zu starten, können Sie die Fluent UI-Komponente Link verwenden. Fügen Sie sie zu den Importen am Anfang von Grid.tsx:

import { Link } from '@fluentui/react/lib/Link';

Um einen Vollbild-Link hinzuzufügen, fügen Sie Folgendes zum vorhandenen Stack hinzu, der die Paging-Steuerelemente enthält.

Hinweis

Achten Sie darauf, dies zu dem verschachtelten Stack hinzuzufügen, und nicht dem Stamm-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>

Sie sehen Folgendes:

  • Dieser Code verwendet Ressourcen, um die Bezeichnung zur Unterstützung der Lokalisierung anzuzeigen.
  • Wenn der Vollbildmodus geöffnet ist, wird der Link nicht angezeigt. Stattdessen rendert der übergeordnete App-Kontext automatisch ein Schließen-Symbol.

Fügen Sie Props zur Unterstützung des Vollbildmodus zu GridProps hinzu

Fügen Sie die onFullScreen- und isFullScreen-Eigenschaften zur GridProps-Schnittstelle innerhalb von Grid.tsx hinzu, um Rückruffunktionen zum Sortieren und Filtern bereitzustellen.

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;
}

Hinzufügen von Props zum Grid zur Unterstützung des Vollbildmodus

Fügen Sie diese neuen Eigenschaften zu den Eigenschaften zur Destrukturierung hinzu:

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;

Aktualisieren Sie die Datei index.ts, um den Vollbildmodus im Raster zu unterstützen

Um diese neuen Eigenschaften bereitzustellen, fügen wir in index.ts die folgende Rückrufmethode unter loadPreviousPage hinzu:

onFullScreen = (): void => {
  this.context.mode.setFullScreen(true);
};

Der Aufruf von setFullScreen bewirkt, dass die Codekomponente in den Vollbildmodus versetzt wird und allocatedHeight und allocatedWidth entsprechend dem Aufruf an trackContainerResize(true) in der init-Methode angepasst werden. Sobald der Vollbildmodus geöffnet ist, wird updateView aufgerufen, um das Rendern der Komponente mit der neuen Größe zu aktualisieren. Die updatedProperties enthält fullscreen_open oder fullscreen_close, abhängig von dem Übergang, der stattfindet.

Um den Status des Vollbildmodus zu speichern, fügen Sie der isFullScreen Klasse innerhalb CanvasGridein neues index.ts Feld hinzu:

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;

UpdateView bearbeiten, um den Zustand nachzuverfolgen

Fügen Sie der Methode Folgendes hinzu updateView , um den Zustand nachzuverfolgen:

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;
    }

Den Rückruf und das isFullScreen-Feld zum Rendern im Raster hinzufügen

Jetzt können Sie den Rückruf und das isFullScreen-Feld in die Grid-Rendering-Eigenschaften weitergeben:

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
);

Hervorheben von Zeilen

Nun sind Sie bereit, die Funktion zur Hervorhebung bedingter Zeilen hinzuzufügen. Sie haben bereits die Eingabeeigenschaften HighlightValue und HighlightColor sowie die HighlightIndicatorproperty-set definiert. Der property-set ermöglicht es der die App erstellenden Person, ein Feld auszuwählen, das zum Vergleich mit dem von ihr in HighlightValue bereitgestellten Wert verwendet wird.

Import-Typen zur Unterstützung der Hervorhebung

Für das Rendern benutzerdefinierter Zeilen in der DetailsList Datei sind einige zusätzliche Importe erforderlich. Es gibt bereits einige Typen von @fluentui/react/lib/DetailsList, daher fügen Sie IDetailsListProps, IDetailsRowStyles und DetailsRow zu dieser Import-Anweisung in Grid.tsx hinzu.

import {
    DetailsList,
    ConstrainMode,
    DetailsListLayoutMode,
    IColumn,
    IDetailsHeaderProps
} from '@fluentui/react/lib/DetailsList';

Erstellen Sie nun den benutzerdefinierten Zeilenrenderer, indem Sie folgendes direkt unterhalb des const rootContainerStyle Blocks hinzufügen:

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;
};

Sie sehen Folgendes:

  • Sie können den Wert des vom Hersteller ausgewählten Felds über den HighlightIndicator Alias abrufen:
    item?.getValue('HighlightIndicator').
  • Wenn der Wert des HighlightIndicator Felds dem Wert der highlightValue von der Eingabeeigenschaft in der Codekomponente bereitgestellten Eigenschaft entspricht, können Sie der Zeile eine Hintergrundfarbe hinzufügen.
  • Die DetailsRow Komponente wird von DetailsList verwendet, um die von Ihnen definierten Spalten zu rendern. Sie müssen das Verhalten nicht ändern, außer der Hintergrundfarbe.

Zusätzliche Eigenschaften zur Unterstützung der Hervorhebung hinzufügen

Fügen Sie einige zusätzliche Eigenschaften für highlightColor und highlightValue hinzu, die durch das Rendering innerhalb von updateView bereitgestellt werden. Sie haben sie bereits zur GridProps-Schnittstelle hinzugefügt, also müssen Sie sie nur zur Destrukturierung der Eigenschaften hinzufügen:

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;

Hinzufügen der onRenderRow-Methode zur DetailsList

Geben Sie die onRenderRow-Methode in die DetailsList-Eigenschaften weiter:

<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>

Bereitstellen und Konfigurieren der Komponente

Nachdem Sie nun alle Features implementiert haben, müssen Sie die Codekomponente zu Testzwecken in Microsoft Dataverse bereitstellen.

  1. Stellen Sie in Ihrer Dataverse-Umgebung sicher, dass ein Herausgeber mit dem Präfix "samples" erstellt wurde.

    Neuen Herausgeber hinzufügen

    Dies könnte auch Ihr eigener Herausgeber sein, vorausgesetzt, Sie aktualisieren den Herausgeber-Präfix-Parameter im Aufruf von pac pcf push unten. Weitere Informationen: Erstellen eines Lösungsherausgebers.

  2. Nachdem Sie den Publisher gespeichert haben, können Sie die CLI für Ihre Umgebung autorisieren, sodass wir die kompilierte Code-Komponente übertragen können. Verwenden Sie an der Befehlszeile Folgendes:

    pac auth create --url https://myorg.crm.dynamics.com
    

    Ersetzen Sie myorg.crm.dynamics.com durch die URL Ihrer eigenen Dataverse-Umgebung. Melden Sie sich mit einem Administrator-/Customizer-Benutzer an, wenn Sie dazu aufgefordert werden. Die von diesen Benutzerrollen bereitgestellten Berechtigungen sind erforderlich, um Codekomponenten auf Dataverse bereitzustellen.

  3. Verwenden Sie Folgendes, um Ihre Codekomponente bereitzustellen:

    pac pcf push --publisher-prefix samples
    

    Hinweis

    Wenn Sie die Fehlermeldung erhalten, Missing required tool: MSBuild.exe/dotnet.exe. Please add MSBuild.exe/dotnet.exe in Path environment variable or use 'Developer Command Prompt for VSmüssen Sie entweder Visual Studio 2019 für Windows & Mac oder Buildtools für Visual Studio 2019 installieren, wobei Sie unbedingt die Workload ".NET-Buildtools" auswählen müssen, wie in den Voraussetzungen beschrieben.

  4. Nach Abschluss dieses Prozesses wurde eine kleine temporäre Lösung namens PowerAppTools_samples in Ihrer Umgebung erstellt, und die CanvasGrid Codekomponente wird dieser Lösung hinzugefügt. Sie können die Codekomponente bei Bedarf später in Ihre eigene Lösung verschieben. Weitere Informationen: Code Component Application Lifecycle Management (ALM).

    PowerAppsTools_samples Lösung

  5. Um Codekomponenten in Canvas-Apps zu verwenden, müssen Sie das Power Apps-Komponentenframework für Canvas-Apps in der von Ihnen verwendeten Umgebung aktivieren.

    a) Öffnen Sie das Admin Center (admin.powerplatform.microsoft.com), und navigieren Sie zu Ihrer Umgebung. b. Navigieren Sie zu Einstellungen>Produkt>Features. Stellen Sie sicher, dass das Power Apps-Komponentenframework für Canvas-Appsaktiviert ist:

    Aktivieren von Codekomponenten

  6. Erstellen Sie eine neue Canvas-App mit dem Tablet-Layout .

  7. Wählen Sie im Bereich "Einfügen " die Option "Weitere Komponenten abrufen" aus.

  8. Wählen Sie im Bereich "Komponenten importieren" die Registerkarte "Code" aus.

  9. Wählen Sie die CanvasGrid Komponente aus.

  10. Klicken Sie auf Importieren. Die Codekomponente wird nun unter Codekomponenten im Bereich "Einfügen " angezeigt.

  11. Ziehen Sie die CanvasGrid Komponente auf den Bildschirm, und binden Sie sie an die Contacts Tabelle in Microsoft Dataverse.

  12. Legen Sie die folgenden Eigenschaften für die CanvasGrid Codekomponente mithilfe des Eigenschaftenbereichs fest:

    • Hervorhebungswert = 1 - Dies ist der Wert, den statecode hat, wenn der Datensatz inaktiv ist.
    • Hervorhebungsfarbe = #FDE7E9 - Dies ist die Farbe, die verwendet werden soll, wenn der Datensatz inaktiv ist.
    • HighlightIndicator = "statecode" - Dies ist das Feld, mit dem verglichen werden soll. Dies befindet sich im Bereich "Erweitert " im Abschnitt "DATEN ".

    Eigenschaften-Panel

  13. Fügen Sie eine neue TextInput Komponente hinzu, und nennen Sie sie txtSearch.

  14. Aktualisieren Sie die CanvasGrid.Items Eigenschaft so, dass sie sein soll Search(Contacts,txtSearch.Text,"fullname").

    Während Sie die Texteingabe eingeben, sehen Sie, dass die Kontakte im Raster gefiltert werden.

  15. Fügen Sie eine neue Beschriftung hinzu , und legen Sie den Text auf "Keine Datensätze gefunden" fest. Positionieren Sie die Beschriftung oben im Canvas-Raster.

  16. Legen Sie die Visible-Eigenschaft der Textbeschriftung auf den Wert fest CanvasGrid1.FilteredRecordCount=0.

Dies bedeutet, dass, wenn keine Datensätze vorhanden sind, die dem txtSearch Wert entsprechen, oder wenn ein Spaltenfilter mithilfe des Kontextmenüs angewendet wird, das keine Datensätze zurückgibt (z. B. " Vollständiger Name " enthält keine Daten), wird die Beschriftung angezeigt.

  1. Hinzufügen eines Anzeigeformulars (aus der Gruppe "Eingabe " im Bereich "Einfügen ").

  2. Legen Sie das Formular DataSource auf die Contacts Tabelle fest, und fügen Sie einige Formularfelder hinzu.

  3. Legen Sie die Formulareigenschaft Item auf CanvasGrid1.Selected.

    Nun sollten Sie sehen, dass beim Auswählen von Elementen im Raster das ausgewählte Element im Formular angezeigt wird.

  4. Fügen Sie der Canvas-App einen neuen Bildschirm mit dem Namen scrDetails hinzu.

  5. Kopieren Sie das Formular aus dem vorherigen Bildschirm, und fügen Sie es auf dem neuen Bildschirm ein.

  6. Legen Sie die Eigenschaft CanvasGrid1.OnSelect auf Navigate(scrDetails) fest.

    Wenn Sie die Aktion zum Auswählen einer Rasterzeile aufrufen, sollten Sie nun sehen, dass die App zum zweiten Bildschirm navigiert und das Element ausgewählt ist.

Debuggen nach der Bereitstellung

Sie können Ihre Codekomponente ganz einfach debuggen, während sie in der Canvas-App ausgeführt wird, indem Sie Die Entwicklertools mithilfe von Ctrl+Shift+I. öffnen.

Wählen Sie Ctrl+P aus und geben Sie Grid.tsx oder Index.ts ein. Anschließend können Sie einen Breakpoint festlegen und schrittweise durch den Code gehen.

Debuggen in Canvas-Apps

Wenn Sie weitere Änderungen an Ihrer Komponente vornehmen müssen, brauchen Sie sie nicht jedes Mal zu deployen. Verwenden Sie stattdessen die in Codekomponenten debuggen beschriebene Technik, um einen Fiddler AutoResponder zu erstellen, um die Datei von Ihrem lokalen Dateisystem zu laden, während npm start watch läuft.

Der AutoResponder sieht ähnlich wie folgt aus:

REGEX:(.*?)((?'folder'css|html)(%252f|\/))?SampleNamespace\.CanvasGrid[\.\/](?'fname'[^?]*\.*)(.*?)$
C:\repos\CanvasGrid\out\controls\CanvasGrid\${folder}\${fname}

AutoResponder-Regel

Außerdem müssen Sie die Filter aktivieren, um die Access-Control-Allow-Origin Kopfzeile hinzuzufügen. Mehr Informationen: Debuggen nach der Bereitstellung in Microsoft Dataverse

Sie müssen in Ihrer Browsersitzung das Cache leeren und Aktualisieren erzwingen, damit die AutoResponder-Datei abgeholt wird. Nach dem Laden können Sie den Browser einfach aktualisieren, da Fiddler der Datei einen Cache-Steuerelementheader hinzufügt, um zu verhindern, dass er zwischengespeichert wird.

Sobald Sie mit Ihren Änderungen zufrieden sind, können Sie die Patchversion im Manifest erhöhen und dann mit pac pcf push erneut bereitstellen.

Bisher haben Sie einen Entwicklungsbuild bereitgestellt, der nicht optimiert ist und zur Laufzeit langsamer ausgeführt wird. Sie können einen optimierten Build mithilfe von pac pcf push bereitstellen, indem Sie die Datei CanvasGrid.pcfproj bearbeiten. Fügen Sie unter OutputPath das Folgende hinzu: <PcfBuildMode>production</PcfBuildMode>

  <PropertyGroup>
    <Name>CanvasGrid</Name>
    <ProjectGuid>a670bba8-e0ae-49ed-8cd2-73917bace346</ProjectGuid>
    <OutputPath>$(MSBuildThisFileDirectory)out\controls</OutputPath>
  </PropertyGroup>

Application Lifecycle Management (ALM) mit Microsoft Power Platform
Power Apps component framework – API-Referenz
Erstellen Sie Ihre erste Komponente
Debuggen von Code-Komponenten