Freigeben über


Tutorial: Erstellen einer DataSet-Komponente für Canvas-App

In diesem Tutorial erstellen Sie eine Codekomponente für die Canvas-App, stellen sie bereit, fügen sie einem Bildschirm hinzu und testen die Komponente mit Visual Studio Code. Die Codekomponente zeigt ein seitenweises, scrollbares DataSet-Raster an, das sortierbare und filterbare Spalten bereitstellt. Es ermöglicht auch das Hervorheben bestimmter Zeilen durch Konfigurieren einer Indikatorspalte. Dies ist eine häufige Anfrage von Apps erstellenden Personen und ist mit nativen Canvas-App-Komponenten eventuell komplex zu implementieren. Codekomponenten können so geschrieben werden, dass sie sowohl auf Canvas als auch auf modellgesteuerten Apps funktionieren. Diese Komponente wurde jedoch speziell für die Verwendung in Canvas-Apps geschrieben.

Darüber hinaus stellen Sie sicher, dass die Codekomponente den Best-Practice-Leitlinien folgt:

  1. Verwendung der Microsoft Fluent UI
  2. Lokalisierung der Codekomponentenbezeichnungen sowohl beim Entwurf als auch zur Runtime
  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

Canvasraster-Demo

Hinweis

Bevor Sie beginnen, stellen Sie sicher, dass Sie alle Voraussetzungskomponenten installiert haben.

Code

Sie können den vollständigen Code für das Beispiel hier herunterladen: PowerApps-Samples/component-framework/CanvasGridControl/.

Ein neues pcfproj-Projekt anlegen

  1. Erstellen Sie einen neuen Ordner, den Sie für Ihre Codekomponente verwenden möchten. Zum Beispiel: C:\repos\CanvasGrid.

  2. Öffnen Sie Visual Studio Code und dann Datei > Ordner öffnen und wählen Sie den Ordner CanvasGrid 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 einen beliebigen Ordner in Visual Studio Code mit code . bei der Eingabeaufforderung laden, wenn das aktuelle Verzeichnis auf diesen Speicherort festgelegt ist.

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

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

    Alternativ verwenden Sie die Kurzform:

    pac pcf init -ns SampleNamespace -n CanvasGrid -t dataset
    
  4. Dies fügt dem aktuellen Ordner ein neues pcfproj und die dazugehörigen Dateien hinzu, einschließlich ein packages.json, dass die Module festlegt. Um die erforderlichen Module zu installieren, verwenden Sie NPM installieren:

    npm install
    

    Hinweis

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

    Canvas-DataSet-Raster

Die Vorlage enthält eine index.ts-Datei sowie verschiedene Konfigurationsdateien. Dies ist der Ausgangspunkt Ihrer Codekomponente und enthält die in Komponentenimplementierung beschriebenen Lebenszyklusmethoden.

Microsoft Fluent UI installieren

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

npm install react react-dom @fluentui/react

Dadurch werden die Module zu den packages.json hinzugefügt und sie im node_modules-Ordner installiert. node_modules wird nicht in die Quellcodeverwaltung eingeschrieben, da alle erforderlichen Module mit npm install wiederhergestellt werden können.

Einer der Vorteile der Microsoft Fluent UI ist, dass sie eine konsistente und extrem barrierefreie Benutzeroberfläche ist.

Konfiguration von 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 die Codierungsstile TypeScript und React. Weitere Informationen: Linting – Best Practices und Anleitungen für Codekomponenten.

Die DataSet-Eigenschaften festlegen

Die CanvasGrid\ControlManifest.Input.xml-Datei legt die Metadaten fest, die das Verhalten der Codekomponente beschreiben. Das Steuerelement-Attribut hat bereits den Namenspace und den Namen der Komponente.

Tipp

Sie können das XML leichter lesen, wenn Sie es so formatieren, dass die Attribute in separaten Zeilen erscheinen. Suchen und installieren Sie ein XML-Formatierungstool Ihrer Wahl auf dem Visual Studio Code Marketplace: Nach XML-Formatierungserweiterungen suchen.

Die folgenden Beispiele wurden so formatiert, dass die Attribute in separaten Zeilen stehen, um sie besser lesbar zu machen.

Sie müssen die Datensätze definieren, an die die Codekomponente gebunden werden kann, indem Sie Folgendes im control-Element hinzufügen, das das vorhandene data-set-Element ersetzt:

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

Das Datensatz-DataSet wird an Datenquelle gebunden, wenn die Codekomponente zu einer Canvas-App hinzugefügt wird. Der Eigenschaftensatz gibt an, dass der Benutzer eine der Spalten dieses DataSet konfigurieren muss, um als Zeilenhervorhebungsindikator verwendet zu werden.

Tipp

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

Eingabe- und Ausgabeeigenschaften festlegen

Zusätzlich zum DataSet können Sie Eingabe-Eigenschaften bereitstellen:

  • HighlightValue: Erlaubt der die App erstellenden Person, einen Wert bereitzustellen, der mit der als HighlightIndicator property-set festgelegten Spalte verglichen werden kann. Wenn die Werte gleich sind, sollte die Zeile hervorgehoben werden.
  • HighlightColor: Erlaubt der die App erstellenden Person, eine Farbe zum Hervorheben von Zeilen auszuwählen.

Tipp

Es wird empfohlen, Eingabeeigenschaften für die Gestaltung allgemeiner Aspekte Ihrer Codekomponenten bereitzustellen, wenn Sie Codekomponenten für die Verwendung in Canvas-Apps erstellen.

Neben den Eingabeeigenschaften wird eine Ausgabe-Eigenschaft namens FilteredRecordCount aktualisiert (das das OnChange-Ereignis auslöst), wenn die Zeilenanzahl aufgrund einer in der Codekomponente angewendeten Filteraktion geändert wird. Dies ist hilfreich, wenn Sie eine No Rows Found-Nachricht in der übergeordneten App anzeigen lassen 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:

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 die index.ts-Datei und fügen Sie direkt über der Zeile Folgendes hinzu: // eslint-disable-next-line no-undef:
import DataSetInterfaces = ComponentFramework.PropertyHelper.DataSetApi;

Führen Sie npm run build erneut aus.

Nachdem die Komponente erstellt wurde, sehen Sie Folgendes:

  • Eine automatisch generierte Datei CanvasGrid\generated\ManifestTypes.d.ts wird Ihrem Projekt 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 Erstellen-Ausgabe wird dem out-Ordner hinzugefügt. Das bundle.js ist das transpilierte JavaScript, das im Browser ausgeführt wird, und die ControlManifest.xml ist eine neu formatierte Version der ControlManifest.Input.xml-Datei, die während der Bereitstellung verwendet wird.

    Hinweis

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

Die „Grid Fluent UI React“-Komponente hinzufügen

Wenn die Codekomponente React verwendet, muss es eine einzelne Stammkomponente geben, die innerhalb der updateView-Methode gerendert wird. Innerhalb des CanvasGrid-Ordners fügen Sie eine neue TypeScript-Datei mit dem Namen Grid.tsx hinzu und 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. Dies ist eine TypeScript-Datei, die die von React verwendete Syntax im XML-Stil unterstützt. Sie wird durch den Erstellenprozess in Standard-JavaScript kompiliert.

Anmerkungen zum Rasterdesign

Dieser Abschnitt enthält Anmerkungen zum Design der Grid.tsx-Komponente.

Es ist eine funktionale Komponente

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

Die Größe von bundle.js minimieren

Beim Importieren der Fluent-Benutzeroberflächenkomponente ChoiceGroup mit pfadbasierten Importen anstelle von:

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

Dieser Code verwendet Folgendes:

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

Dadurch wird Ihre Paketgröße kleiner, was zu geringeren Kapazitätsanforderungen und einer besseren Runtimeleistung 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. So extrahieren Sie die Attribute, die zum Rendern erforderlich sind, aus den Eigenschaften, anstatt ihnen jedes Mal das Präfix props. voranzustellen, wenn sie verwendet werden.

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 mehreren Stellen verwendet, um sicherzustellen, dass das erstellte Elementarray nur mutiert wird, wenn die Eingabeeigenschaften 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 Punkte:

  • Die DetailsList in einem Stack wird verpackt, da Sie später ein Fußzeilenelement mit den Paging-Steuerelementen hinzufügen.
  • Die Fluent UI Sticky-Komponente wird verwendet, um die Kopfzeilenspalten (mit onRenderDetailsHeader) zu verpacken, damit sie beim Scrollen des Rasters sichtbar bleiben.
  • setKey wird zusammen mit initialFocusedIndex an DetailsList weitergeben, sodass beim Wechsel der aktuellen Seite die Bildlaufposition und Auswahl zurückgesetzt werden.
  • Die Funktion onRenderItemColumn wird verwendet, um den Zellinhalt zu rendern. Sie akzeptiert Zeilenelemente und verwendet getFormattedValue, um den Anzeigewert der Spalte zurückzugeben. Die getValue-Methode gibt einen Wert zurück, den Sie verwenden könnten, um ein alternatives Rendering bereitzustellen. Der Vorteil von getFormattedValue besteht darin, dass es eine formatierte Zeichenfolge für Spalten von Nicht-Zeichenfolgentypen wie Datumsangaben und Suchen enthält.
  • Der gridColumns-Block ordnet die Objektform der Spalten, die vom DataSet-Kontext bereitgestellt werden, der Form zu, die von der DetailsList-Spalteneigenschaft erwartet wird. Da diese im React.useMemo-Hook verpackt ist, ändert sich die Ausgabe nur, wenn sich die columns- oder sorting-Eigenschaften ä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 mit der column.order-Eigenschaft sortiert, um sicherzustellen, dass sie sich in der Reihenfolge im Raster befinden, die von der die App erstellende Person festgelegt wurde.
  • 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 sind und der Status zurückgesetzt ist. Es gibt eine zusätzliche Eingabeeigenschaft namens itemsLoading, die der dataset.loading -Eigenschaft zugeordnet ist, die vom DataSet-Kontext bereitgestellt wird. Beide Kennzeichen werden verwendet, um den visuellen Ladehinweis zu steuern, der mit der Fluent UI Overlay-Komponente implementiert wird.

Index.ts aktualisieren

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

Importanweisungen hinzufügen und Symbole initialisieren

Ersetzen Sie die vorhandenen Importe durch Folgendes in der Kopfzeile von index.ts:

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-Benutzeroberflächensymbolsatz verwendet. Sie rufen initializeIcons auf, um die Symbole im Testkabelbaum zu laden. In Canvas-Apps sind sie bereits initialisiert.

Der CanvasGrid-Klasse Felder hinzufügen

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

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

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

    }

Die init-Methode aktualisieren

Fügen Sie init Folgendes hinzu:

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 zum ersten Mal 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 Ihre Codekomponenten-UI hinzufügen.
  • resources: Dies wird verwendet, um lokalisierte Zeichenfolgen in der Sprache des aktuellen Benutzers abzurufen.

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

Hinweis

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

Die updateView-Methode aktualisieren

Fügen Sie updateView Folgendes hinzu:

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

Sie sehen, dass:

  • Sie rufen React.createElement ab und übergeben den Verweis auf den DOM-Container, den Sie in der init-Funktion erhalten haben.
  • Die Grid-Komponente ist in Grid.tsx definiert und wird am Anfang der Datei importiert.
  • allocatedWidth und allocatedHeight werden vom übergeordneten Kontext bereitgestellt, wenn sie sich ändern (z. B. wenn die App die Größe der Codekomponente ändert oder Sie in den Vollbildmodus wechseln), nachdem Sie einen Aufruf an trackContainerResize(true) in der init-Funktion gemacht haben.
  • Sie können erkennen, wann neue Zeilen angezeigt werden müssen, wenn die 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 Status der angezeigten Seite beibehält, wird die Seitennummer 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 falsch ist.

Die destroy-Methode aktualisieren

Schließlich müssen Sie eine Bereinigung durchführen, wenn die Codekomponente zerstört wird:

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

Die Testumgebung starten

Stellen Sie sicher, dass alle Dateien gespeichert sind und am Terminal verwendet werden:

npm start watch

Sie müssen die Breite und Höhe festlegen, um das Codekomponentenraster anzuzeigen, das mit den drei Beispieldatensätzen ausgefü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:

Testumgebung

Hier sind einige durch Kommas 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

In der Testumgebung wird nur eine einzige Spalte angezeigt, unabhängig von den Spalten, die Sie in der geladenen CSV-Datei angeben. Dies liegt daran, dass die Testumgebung nur dann property-set anzeigt, wenn einer definiert ist. Wenn kein property-set definiert ist, werden alle Spalten in der geladenen CSV-Datei aufgefüllt.

Zeichenauswahl hinzufügen

Obwohl die Fluent UI DetailsList standardmäßig die Auswahl von Datensätzen erlaubt, sind die ausgewählten Datensätze nicht mit der Ausgabe der Codekomponente verknüpft. Sie brauchen die Selected- und SelectedItems-Eigenschaften, um die ausgewählten Datensätze in einer Canvas-App widerzuspiegeln, sodass dazugehörende Komponenten aktualisiert werden können. In diesem Beispiel erlauben Sie jeweils nur die Auswahl eines einzelnen Elements, sodass SelectedItems immer nur einen einzigen Datensatz enthält.

Grid.tsx-Importe aktualisieren

Fügen Sie den Importen in Grid.tsx Folgendes 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';

SetSelectedRecords zu GridProps hinzufügen

Fügen Sie der GridProps-Schnittstelle in 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;
}

Die setSelectedRecords-Eigenschaft zum Raster hinzufügen

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 beim Aktualisieren der Auswahl die Komponente neu gerendert wird, um die aktualisierte Auswahlanzahl widerzuspiegeln.

Auswahl zur DetailsList hinzufügen

Das zur Beibehaltung des Auswahlstaatus erstellte selection-Objekt 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>

Den setSelectedRecords-Rückruf definieren

Sie müssen den neuen setSelectedRecords-Rückruf in index.ts festlegen und an die Grid-Komponente übergeben. Fügen Sie oben auf 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 ist als Pfeilfunktion festgelegt, um sie an die aktuelle this-Instanz der Codekomponente zu binden.

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

Fügen Sie den Eingabeeigenschaften einen neuen Rückruf hinzu

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

Das OnSelect-Ereignis aufrufen

Es gibt ein Muster in Canvas-Apps, bei dem, wenn ein Katalog oder ein Raster eine Elementauswahl aufgerufen hat (z. B. Auswahl eines Chevron-Symbols), ein OnSelect-Ereignis aufgerufen wird. Sie können dieses Muster mit der openDatasetItem-Methode des DataSet implementieren.

OnNavigate zur GridProps-Schnittstelle hinzufügen

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

OnNavigate zu Rastereigenschaften hinzufügen

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

OnNavigate-Methode zu index.ts hinzufügen

Ergänzen Sie die onNavigate-Methode zum index.ts direkt unter der setSelectedRecords-Methode:

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

Dies ruft einfach die openDatasetItem-Methode für den DataSet-Datensatz auf, damit die Codekomponente das OnSelect-Ereignis auslöst. Die Methode ist als Pfeilfunktion festgelegt, 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 die Testumgebung 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, weil die Funktion als Pfeilfunktion festgelegt und in einen JavaScript-Abschluss transpiliert wurde, um die Instanz von this zu erfassen.

Lokalisierung hinzufügen

Bevor Sie fortfahren, müssen Sie der Codekomponente Ressourcenzeichenfolgen hinzufügen, damit Sie lokalisierte Zeichenfolgen für Nachrichten wie Paging, Sortierung 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 des Dataflows Wert
Records_Dataset_Display Einträge
FilteredRecordCount_Disp Gefilterte Datensatzanzahl
FilteredRecordCount_Desc Die Anzahl der Datensätze nach der Filterung
HighlightValue_Disp Hervorhebenwert
HighlightValue_Desc Der Wert, der angibt, dass eine Zeile hervorgehoben werden sollte
HighlightColor_Disp Hervorhebungsfarbe
HighlightColor_Desc Die Farbe zum Hervorheben einer Zeile
HighlightIndicator_Disp Hervorhebenanzeige-Feld
HighlightIndicator_Desc Legen Sie den Namen des Felds fest, das mit dem Hervorhebenwert 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. So finden Sie eine Erweiterung des Visual Studio Codes: In Visual Studio Marketplace nach einem resx-Editor suchen

Die Daten für diese Datei können auch eingestellt werden, indem Sie die CanvasGrid.1033.resx-Datei in Notepad ö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 Beschriftungszeichenfolgen hinzufügen, die zur Ruftime mithilfe von getString abgerufen werden. Weitere Informationen: Implementieren der Lokalisierungs-API-Komponente.

Fügen sie diese neue Ressourcendatei der ControlManifest.Input.xml-Datei im resources-Element hinzu:

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

Spaltensortierung und -filterung hinzufügen

Wenn Sie dem Benutzer das Sortieren und Filtern mithilfe von Rasterspaltenüberschriften ermöglichen möchten, bietet die Fluent-Benutzeroberfläche DetailList eine einfache Möglichkeit, den Spaltenüberschriften Kontextmenüs hinzuzufügen.

OnSort und onFilter zu GridProps hinzufügen

Zuerst fügen Sie der GridProps-Schnittstelle in Grid.tsx onSort und onFilter 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;

ContextualMenu-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 die pfadbasierten Importe verwenden, um die Paketgröße zu reduzieren.

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

Rendering-Funktionalität für Kontextmenüs hinzufügen

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

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

Sie sehen, dass:

  • Der contextualMenuProps-Status steuert die Sichtbarkeit des Kontextmenüs, das mit der Fluent-Benutzeroberflächenkomponente 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 Filter bereitzustellen.
  • Dieser Code verwendet resources.getString, um Beschriftungen im Kontextmenü anzuzeigen, die lokalisiert werden können.
  • Der React.useCallback-Hook stellt auf ähnliche Art und Weise wie React.useMemo sicher, dass die Rückrufe nur mutiert 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-Rückrufe 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]);

Kontextmenü zur gerenderten Ausgabe hinzufügen

Damit das Kontextmenü angezeigt wird, müssen Sie es der gerenderten Ausgabe hinzufügen. Fügen Sie Folgendes direkt unter 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>

Die Funktionen onSort und OnFilter hinzufügen

Nachdem Sie nun die Sortier- und Filterbenutzeroberfläche hinzugefügt haben, müssen Sie die Rückrufe zu index.ts hinzufügen, um das Sortieren und Filtern der Datensätze durchzuführen, die an die Codekomponente gebunden sind. Fügen Sie Folgendes zu index.ts direkt unter 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, dass:

  • Die Sortierung und der Filter werden dem DataSet mit den Eigenschaften Sortieren und Filterung angewendet.
  • Beim Ändern der Sortierspalten müssen die vorhandenen Sortierdefinitionen mithilfe von Pop entfernt und nicht das Sortierarray selbst ersetzt werden.
  • Nach der Anwendung von Sortieren und Filtern muss Aktualisieren aufgerufen werden. Wenn ein Filter und eine Sortierung gleichzeitig angewendet werden, muss „Aktualisieren“ nur einmal aufgerufen werden.

OnSort- und OnFilter-Rückrufe zum Rasterrendering hinzufügen

Schließlich können Sie diese beiden Rückrufe an den Grid-Rendering-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 dieser Stelle können Sie nicht mehr mit der Testumgebung testen, da sie keine Unterstützung für das Sortieren und Filtern 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.

Die Ausgabeeigenschaft FilteredRecordCount aktualisieren

Da das Raster jetzt Datensätze intern filtern kann, ist es wichtig, der Canvas-App zu melden, wie viele Datensätze angezeigt werden. Auf diese Weise können Sie eine Meldung vom Typ „Keine Datensätze“ anzeigen.

Tipp

Sie können dies intern in der Codekomponente implementieren, es wird jedoch empfohlen, der Canvas-App so viel Benutzeroberfläche zu überlassen, da dies der die App erstellenden Person mehr Flexibilität bietet.

Sie haben bereits eine Ausgabeeigenschaft namens FilteredRecordCount im ControlManifest.Input.xml festgelegt. Wenn die Filterung durchgeführt wird und die gefilterten Datensätze geladen werden, wird die updateView-Funktion mit dem Zeichenfolgen-dataset updatedProperties-Array aufgerufen. Wenn sich die Anzahl der Datensätze geändert hat, müssen Sie notifyOutputChanged aufrufen, 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
);

FilteredRecordCount zu getOutputs hinzufügen

Dadurch wird der filteredRecordCount auf der Codekomponentenklasse aktualisiert, die Sie vorher definiert haben, wenn sie sich von den neu empfangenen Daten unterscheidet. Nachdem notifyOutputChanged aufgerufen wurde, müssen Sie sicherstellen, dass der Wert zurückgegeben wird, wenn getOutputs aufgerufen wird. Aktualisieren Sie die getOutputs-Methode also auf:

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

Paging zum Raster hinzufügen

Bei großen Datasets teilen Canvas-Apps die Datensätze auf mehrere Seiten auf. Sie können eine Fußzeile hinzufügen, die Steuerelemente für die Seitennavigation anzeigt. Jede Schaltfläche wird mit einer Fluent-Benutzeroberflächen IconButton gerendert, die Sie importieren müssen.

IconButton zu Importen hinzufügen

Fügen Sie diese den Importen in Grid.tsx hinzu:

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

StringFormat-Funktion hinzufügen

Der folgende Schritt führt Funktionsweisen hinzu, um das Format für die Seitenindikatorbeschriftung aus den Ressourcenzeichenfolgen ("Page {0} ({1} Selected)") hinzuzufügen und mit einer einfachen Funktion stringFormat-Funktion zu formatieren. Diese Funktion könnte sich gleichermaßen in einer separaten Datei befinden und der Einfachheit halber zwischen Ihren Komponenten geteilt 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;
}

Paging-Schaltflächen hinzufügen

Fügen Sie in Grid.tsx das folgende Stack.Item unter dem vorhandenen Stack.Item hinzu, das den 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, dass:

  • 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 Seitenindikatorbeschriftung aus den Ressourcenzeichenfolgen ("Page {0} ({1} Selected)") und nehmen Sie die Formatierung mit der stringFormat-Funktion hinzu, die Sie im letzten Schritt hinzugefügt haben.
  • Sie können alt-Text für Barrierefreiheit auf den Paging-IconButtons bereitstellen.
  • Der Stil in der Fußzeile kann auch mit einem CSS-Klassennamen verwendet werden, der auf eine CSS-Datei zur Codekomponente hinzugefügt wird.

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

Als Nächstes müssen Sie die fehlenden loadFirstPage-, loadNextPage- und loadPreviousPage-Rückrufeigenschaften 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;
}

Neue Pagingeigenschaften dem Raster hinzufügen

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;

Rückrufe zu index.ts hinzufügen

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, um diese Rückrufe einzuschließen:

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

Vollbildunterstützung hinzufügen

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

Um den Vollbildmodus zu starten, können Sie die Link-Komponente der Fluent-Benutzeroberfläche verwenden. Fügen Sie sie zu den Importen oben in Grid.tsx hinzu:

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, dass:

  • Dieser Code verwendet Ressourcen, um die Beschriftung anzuzeigen, um die Lokalisierung zu unterstützen.
  • Wenn der Vollbildmodus geöffnet ist, wird der Link nicht angezeigt. Stattdessen rendert der Kontext der übergeordneten App automatisch ein Schließensymbol.

Eigenschaften zur Unterstützung des Vollbildmodus zu GridProps hinzufügen

Fügen Sie die Eigenschaften onFullScreen und isFullScreen zur GridProps-Schnittstelle in 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;
}

Eigenschaften zur Unterstützung des Vollbildmodus dem Raster hinzufügen

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;

Index.ts zur Unterstützung des Vollbildmodus für das Raster aktualisieren

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 an setFullScreen bewirkt, dass die Codekomponente den Vollbildmodus öffnet und die allocatedHeight und allocatedWidth entsprechend anpasst, weil trackContainerResize(true) in der init-Methode aufgerufen wurde. Sobald der Vollbildmodus geöffnet ist, wird updateView aufgerufen und somit das Rendering der Komponente mit der neuen Größe aktualisiert. Die updatedProperties enthält fullscreen_open oder fullscreen_close je nach Übergang, der gerade stattfindet.

Fügen Sie der CanvasGrid-Klasse in index.ts ein neues isFullScreen-Feld hinzu, um den Zustand des Vollbildmodus zu speichern:

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

Fügen Sie Folgendes zur updateView-Methode hinzu, um den Status 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
);

Zeilen hervorheben

Jetzt können Sie die Funktionalität zur bedingten Zeilenhervorhebung hinzufügen. Sie haben bereits die HighlightValue- und HighlightColor-Eingabeeigenschaften und den HighlightIndicator property-set festgelegt. 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.

Importtypen zur Unterstützung der Hervorhebung

Das benutzerdefinierte Zeilenrendering in DetailsList erfordert einige zusätzliche Importe. Es gibt bereits einige @fluentui/react/lib/DetailsList-Typen, fügen Sie also IDetailsListProps, IDetailsRowStyles und DetailsRow zu der Importanweisung in Grid.tsx hinzu.

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

Jetzt erstellen Sie den benutzerdefinierten Zeilenrenderer, indem Sie Folgendes direkt unter dem const rootContainerStyle-Block hinzu:

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, dass:

  • Sie können den Wert des Feldes, das von der die App erstellenden Person ausgewählt wurde, über das HighlightIndicator-Alias abrufen.
    item?.getValue('HighlightIndicator').
  • Wenn der Wert des HighlightIndicator-Felds dem Wert von highlightValue entspricht, den die Eingabeeigenschaft der Codekomponente bereitgestellt hat, können Sie der Zeile eine Hintergrundfarbe hinzufügen.
  • Die DetailsRow-Komponente wird von der DetailsList verwendet, um die von Ihnen definierten Spalten zu rendern. Sie brauchen das Verhalten außer der Hintergrundfarbe nicht ändern.

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

Die onRenderRow-Methode zur DetailsList hinzufügen

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>

Die Komponente bereitstellen und konfigurieren

Nachdem Sie alle Funktionen implementiert haben, müssen Sie die Codekomponente an Microsoft Dataverse zum Testen 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 Herausgeber gespeichert haben, können Sie die CLI für Ihre Umgebung autorisieren, damit wir die kompilierte Codekomponente pushen können. Verwenden Sie Folgendes in der Befehlszeile:

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

    Ersetzen Sie myorg.crm.dynamics.com mit der URL Ihrer eigenen Dataverse-Umgebung. Melden Sie sich mit Administrator-/Anpasserbenutzerdaten an, wenn Sie dazu aufgefordert werden. Die von diesen Benutzerrollen bereitgestellten Berechtigungen werden benötigt, um Codekomponenten für Dataverse bereitzustellen.

  3. Verwenden Sie zum Bereitstellen Ihrer Codekomponente:

    pac pcf push --publisher-prefix samples
    

    Hinweis

    Wenn Sie den Fehler Missing required tool: MSBuild.exe/dotnet.exe. Please add MSBuild.exe/dotnet.exe in Path environment variable or use 'Developer Command Prompt for VS erhalten, müssen Sie entweder Visual Studio 2019 für Windows und Mac oder Build Tools für Visual Studio 2019 installieren und sicherstellen, dass Sie den Workload „.NET-Build Tools“ auswählen, wie in den Voraussetzungen beschrieben.

  4. Nach Abschluss dieses Vorgangs wird 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: Application Lifecycle Management (ALM) für Codekomponenten.

    PowerAppsTools_samples-Lösung

  5. Um Codekomponenten in Canvas-Apps zu verwenden, müssen Sie das Power Apps Component Framework für Canvas-Apps auf der Umgebung aktivieren, die Sie verwenden.

    a. Öffnen Sie das Admin Center (admin.powerplatform.microsoft.com) und navigieren Sie zu Ihrer Umgebung. b. Gehen Sie zu Einstellungen > Produkt > Funktionen . Stellen Sie sicher, dass das Power Apps Component Framework für Canvas-Apps auf An gestellt ist:

    Codekomponenten aktivieren

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

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

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

  9. Wählen Sie die CanvasGrid-Komponente aus.

  10. Wählen Sie Importieren aus. Die Codekomponente erscheint nun unter Codekomponenten im Bereich Einfügen.

  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 auf der 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 wird, wenn der Datensatz inaktiv ist.
    • HighlightIndicator = "statecode": Dies ist das Feld, mit dem verglichen werden soll. Dies ist im Bereich Erweitert im Abschnitt DATEN.

    Eigenschaftenbereich

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

  14. Aktualisieren Sie die CanvasGrid.Items-Eigenschaft auf Search(Contacts,txtSearch.Text,"fullname").

    Sie werden feststellen, dass beim Eingeben der Texteingabe die Kontakte im Raster gefiltert werden.

  15. Fügen sie eine neue Textbeschriftung hinzu und setzen Sie den Text auf „Keine Datensätze gefunden“. Positionieren Sie die Beschriftung oben auf dem Canvas-Raster.

  16. Legen Sie die Eigenschaft Sichtbar der Textbeschriftung auf CanvasGrid1.FilteredRecordCount=0 fest.

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

  1. Fügen Sie ein Anzeigeformular hinzu (aus der Gruppe Eingabe im Bereich Einfügen).

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

  3. Legen Sie die Item-Eigenschaft des Formulars auf CanvasGrid1.Selected fest.

    Wenn Sie jetzt Elemente im Raster auswählen, sollten Sie sehen, dass das Formular die ausgewählten Elemente anzeigt.

  4. Fügen Sie einen neuen Bildschirm zu der Canvas-App namens scrDetails hinzu.

  5. Kopieren Sie das Formular vom vorherigen Bildschirm und fügen Sie es in den neuen Bildschirm ein.

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

    Sie sollten jetzt sehen, dass die App beim Aufrufen der Rasterzeilenauswahlaktion zum zweiten Bildschirm mit dem ausgewählten Element navigiert.

Debuggen nach der Bereitstellung

Sie können Ihre Codekomponente problemlos debuggen, während sie in der Canvas-App ausgeführt wird, indem Sie die Entwicklertools mit Ctrl+Shift+I öffnen.

Wählen Sie Ctrl+P und geben Sie Grid.tsx oder Index.ts ein. Sie können dann einen Haltepunkt setzen und Ihren Code schrittweise durchgehen.

Debuggen in Canvas-App

Wenn Sie weitere Änderungen an Ihrer Komponente vornehmen müssen, müssen Sie sie nicht jedes Mal bereitstellen. 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 würde ungefähr so aussehen:

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

AutoResponder-Regel

Sie müssen auch die Filter aktivieren, um die Access-Control-Allow-Origin-Kopfzeile hinzufü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 der Fiddler der Datei eine Cache-Steuerelementkopfzeile hinzufügt, um zu verhindern, dass sie 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 Entwicklungs-Build bereitgestellt, der nicht optimiert ist und zur Runtime langsamer ausgeführt wird. Sie können einen optimierten Build bereitstellen, indem Sie pac pcf push durch Bearbeiten der CanvasGrid.pcfproj verwenden. Fügen Sie unter dem OutputPath Folgendes 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 Codekomponente
Debuggen von Code-Komponenten

Hinweis

Können Sie uns Ihre Präferenzen für die Dokumentationssprache mitteilen? Nehmen Sie an einer kurzen Umfrage teil. (Beachten Sie, dass diese Umfrage auf Englisch ist.)

Die Umfrage dauert etwa sieben Minuten. Es werden keine personenbezogenen Daten erhoben. (Datenschutzbestimmungen).