Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este tutorial, creará un componente de código para el conjunto de datos de una aplicación de lienzo, lo implementará, lo agregará a una pantalla y probará el componente usando Visual Studio Code. El componente de código muestra una cuadrícula de conjunto de datos paginada y desplazable que proporciona columnas ordenables y filtrables. También permite el resaltado de filas específicas mediante la configuración de una columna de indicador. Esta es una solicitud común de los creadores de aplicaciones y puede ser compleja de implementar utilizando componentes de aplicaciones de lienzo nativos. Los componentes de código se pueden escribir para trabajar tanto en aplicaciones basadas en lienzo como en aplicaciones controladas por modelos. Sin embargo, este componente está diseñado para usarse específicamente dentro de las aplicaciones canvas.
Además de estos requisitos, también garantizará que el componente de código siga las instrucciones de procedimientos recomendados:
- Uso de la interfaz de usuario de Microsoft Fluent
- Localización de las etiquetas de componentes de código en el diseño y el entorno de ejecución
- Asegurarse de que el componente de código se representa con el ancho y el alto proporcionados por la pantalla de la aplicación de lienzo principal
- Consideración para que el creador de aplicaciones personalice la interfaz de usuario mediante propiedades de entrada y elementos de aplicación externos en la medida de lo posible
Demostración de Canvas Grid
Nota:
Antes de empezar, asegúrese de que ha instalado todos los componentes de requisitos previos.
Código
Puede descargar el ejemplo completo de PowerApps-Samples/component-framework/CanvasGridControl/.
Creación de un nuevo proyecto
Cree una nueva carpeta que se usará para el componente de código. Por ejemplo: .
Abra Visual Studio Code y, después, File>Abrir carpeta y seleccione la carpeta
CanvasGrid. Si ha agregado las extensiones Windows Explorer durante la instalación de Visual Studio Code, puede usar la opción de menú contextual Open with Code dentro de la carpeta. También puede cargar cualquier carpeta en Visual Studio Code mediantecode .en el símbolo del sistema cuando el directorio actual esté configurado en esa ubicación.Dentro de un nuevo terminal de PowerShell Visual Studio Code (Terminal>New Terminal), use el comando pac pcf init para crear un nuevo proyecto de componente de código:
pac pcf init --namespace SampleNamespace --name CanvasGrid --template dataseto usando la forma corta:
pac pcf init -ns SampleNamespace -n CanvasGrid -t datasetEsto agrega archivos nuevos y relacionados a la carpeta actual, incluido un que define los módulos necesarios. Para instalar los módulos necesarios, use npm install:
npm installNota:
Si recibe el mensaje , asegúrese de que ha instalado todos los requisitos previos, específicamente node.js (se recomienda la versión de LTS).
Cuadrícula de lienzo del conjunto de datos
La plantilla incluye un archivo junto con varios archivos de configuración. Este es el punto de partida del componente de código y contiene los métodos de ciclo de vida descritos en Implementación de componentes.
Instalación de la interfaz de usuario de Microsoft Fluent
Usará la interfaz de usuario de Microsoft Fluent y React para crear la interfaz de usuario, por lo que debe instalarlas como dependencias. Use lo siguiente en el terminal:
npm install react react-dom @fluentui/react
Esto agrega los módulos a e los instala en la carpeta . No vas a confirmar en el control de versiones ya que todos los módulos necesarios se pueden restaurar mediante .
Una de las ventajas de la interfaz de usuario de Microsoft Fluent es que proporciona una interfaz de usuario coherente y altamente accesible .
Configuración
La plantilla usada por pac pcf init instala el módulo en el proyecto y lo configura agregando un archivo. ahora requiere configurar los estilos de codificación TypeScript y React. Más información: Linting: procedimientos recomendados e instrucciones para componentes de código.
Definición de las propiedades del conjunto de datos
El archivo define los metadatos que describen el comportamiento del componente de código. El atributo de control ya contendrá el espacio de nombres y el nombre del componente.
Sugerencia
Puede encontrar el XML más fácil de leer si lo formatea para que los atributos aparezcan en líneas separadas. Busque e instale una herramienta de formato XML que prefiera en Visual Studio Code Marketplace: Search para extensiones de formato xml.
Los ejemplos a continuación se han formateado con atributos en líneas separadas para que sean más fáciles de leer.
Debe definir los registros a los que se puede enlazar el componente de código; para ello, agregue lo siguiente dentro del elemento y reemplace el elemento existente :
- antes de
- después de
<data-set name="sampleDataSet"
display-name-key="Dataset_Display_Key">
</data-set>
El conjunto de datos de registros se enlazará a un origen de datos cuando el componente de código se agregue a una aplicación de lienzo. El conjunto de propiedades indica que el usuario debe configurar una de las columnas de ese conjunto de datos que se van a usar como indicador de resaltado de fila.
Sugerencia
Puede especificar varios elementos del conjunto de datos. Esto podría ser útil si desea buscar un dataset, pero mostrar una lista de registros utilizando un segundo conjunto de datos.
Definición de las propiedades de entrada y salida
Además del conjunto de datos, puede proporcionar las siguientes propiedades de entrada :
- : permite que el creador de aplicaciones proporcione un valor que se va a comparar con la columna definida como . Cuando los valores son iguales, la fila debe resaltarse.
- - Permite que el creador de la aplicación seleccione un color que se usará para resaltar las filas.
Sugerencia
Al crear componentes de código para usarlos en aplicaciones tipo canvas, se recomienda proporcionar propiedades de entrada para el estilo de los aspectos comunes de sus componentes de código.
Además de las propiedades de entrada, se actualizará una propiedad de salida denominada (y desencadenará el evento) cuando se cambie el recuento de filas debido a una acción de filtro aplicada dentro del componente de código. Esto resulta útil cuando desea mostrar un mensaje dentro de la aplicación primaria.
Nota:
En el futuro, los componentes de código admitirán eventos personalizados para que pueda definir un evento específico en lugar de usar el evento genérico .
Para definir estas tres propiedades, agregue lo siguiente al archivo, debajo del elemento :
<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"/>
Guarde este archivo y, a continuación, en la línea de comandos, use:
npm run build
Nota:
Si recibe un error similar al siguiente al ejecutar :
[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
Abra index.ts archivo y agregue esto: , directamente encima de la línea:
import DataSetInterfaces = ComponentFramework.PropertyHelper.DataSetApi;
Ejecuta nuevamente.
Una vez compilado el componente, verá lo siguiente:
Se agrega un archivo generado automáticamente al proyecto. Esto se genera como parte del proceso de compilación a partir de y proporciona los tipos para interactuar con las propiedades de entrada y salida.
La salida de compilación se agrega a la carpeta . es el JavaScript transpilado que se ejecuta dentro del explorador y es una versión reformateada del archivo que se usa durante la implementación .
Nota:
No modifique directamente el contenido de las carpetas y . Se sobrescribirán como parte del proceso de compilación.
Adición del componente Grid Fluent UI React
Cuando el componente de código usa React, debe haber un único componente raíz que se represente en el método updateView. Dentro de la carpeta , agregue un nuevo archivo TypeScript denominado y agregue el siguiente contenido:
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';
Nota:
El archivo tiene la extensión que es un archivo TypeScript que admite la sintaxis de estilo XML usada por React. Se compila en JavaScript estándar mediante el proceso de compilación.
Notas de diseño de cuadrícula
En esta sección se incluyen comentarios sobre el diseño del componente.
Es un componente funcional
Se trata de un componente funcional de React, pero también podría ser un componente de clase. Esto se basa en su estilo de codificación preferido. Los componentes de clase y los componentes funcionales también se pueden mezclar en el mismo proyecto. Tanto los componentes de función como de clase usan la sintaxis de estilo XML usada por React. Más información: Componentes de función y clase
Minimizar el tamaño de bundle.js
Al importar los componentes Fluent UI de la interfaz de usuario mediante importaciones basadas en rutas, en lugar de:
import {
DetailsList,
ConstrainMode,
DetailsListLayoutMode,
IColumn,
IDetailsHeaderProps,
Stack
} from "@fluentui/react";
Este código usa:
import {
DetailsList,
ConstrainMode,
DetailsListLayoutMode,
IColumn,
IDetailsHeaderProps,
} from '@fluentui/react/lib/DetailsList';
import { Stack } from '@fluentui/react/lib/Stack';
De este modo, el tamaño de la agrupación será menor, lo que dará lugar a requisitos de capacidad más bajos y un mejor rendimiento en tiempo de ejecución.
Una alternativa sería usar la agitación de árboles.
Asignación de desestructuración
Este código:
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
} = props;
Usa la asignación de estructuración. De este modo, se extraen los atributos necesarios para representar desde las propiedades, en lugar de prefijarlos con cada vez que se usan.
El código también usa React.memo para envolver el componente funcional de modo que no se renderice a menos que alguna de las propiedades de entrada haya cambiado.
Uso de React.useMemo
React.useMemo se usa en varios lugares para asegurarse de que la matriz de elementos creada solo se muta cuando la propiedad de entrada o cambia. Ésta es una práctica recomendada de los componentes de función que reduce las representaciones innecesarias de los componentes secundarios.
Otros elementos a tener en cuenta:
- En un , el se ajusta porque más adelante se agregará un elemento de pie de página con los controles de paginación.
- El componente fluent UI se usa para encapsular las columnas de encabezado (mediante ) para que permanezcan visibles al desplazarse por la cuadrícula.
- se pasa al junto con para que cuando cambie la página actual, se restablecerá la posición de desplazamiento y la selección.
- La función se usa para representar el contenido de la celda. Acepta el elemento de fila y usa getFormattedValue para devolver el valor para mostrar de la columna. El método getValue devuelve un valor que puede usar para proporcionar una representación alternativa. La ventaja de es que contiene una cadena con formato para columnas de tipos que no son de cadena, como fechas y búsquedas.
- El bloque asigna la forma de objeto de las columnas proporcionadas por el contexto del conjunto de datos a la forma esperada por el prop de columnas. Dado que esto se ajusta en el hook React.useMemo, la salida solo cambiará cuando cambien los o props. Puede mostrar los iconos de ordenación y filtro en las columnas donde los detalles de ordenación y filtrado proporcionados por el contexto del componente de código coinciden con la columna que se asigna. Las columnas se ordenan mediante la propiedad para asegurarse de que están en el orden correcto en la cuadrícula, tal como lo define el creador de la aplicación.
- Estás manteniendo un estado interno para en nuestro componente de React. Esto es porque cuando el usuario selecciona acciones de ordenación y filtrado, puede dejar la cuadrícula en gris como señal visual hasta que la cuadrícula se actualice y el estado se restablezca. Hay una propiedad de entrada adicional denominada que se asigna a la propiedad dataset.loading proporcionada por el contexto del conjunto de datos. Ambas banderas se utilizan para controlar la indicación visual de carga que se implementa mediante el componente Fluent UI .
Actualizar index.ts
El siguiente paso es realizar cambios en el archivo para que coincidan con las propiedades definidas en
Agregar instrucciones de importación e inicializar iconos
Para el encabezado de , reemplace las importaciones existentes por lo siguiente:
- antes de
- después de
import {IInputs, IOutputs} from './generated/ManifestTypes';
import DataSetInterfaces = ComponentFramework.PropertyHelper.DataSetApi;
type DataSet = ComponentFramework.PropertyTypes.DataSet;
Nota:
La importación de es necesaria porque este código usa el conjunto de iconos Fluent UI. Llamas a para cargar los iconos dentro del arnés de prueba. Dentro de las aplicaciones de lienzo, ya están inicializadas.
Agregar campos a la clase CanvasGrid
Agregue los siguientes campos a la clase :
- antes de
- después de
export class CanvasGrid implements ComponentFramework.StandardControl<IInputs, IOutputs> {
/**
* Empty constructor.
*/
constructor() {
}
Actualización del método init
Agregue lo siguiente a :
- antes de
- después de
public init(
context: ComponentFramework.Context<IInputs>,
notifyOutputChanged: () => void,
state: ComponentFramework.Dictionary,
container: HTMLDivElement): void {
// Add control initialization code
}
Se llama a la función cuando el componente de código se inicializa por primera vez en una pantalla de la app. Puede almacenar una referencia a lo siguiente:
- : Este es el callback que se proporciona para que lo llames y notifiques a la app de lienzo que una de las propiedades ha cambiado.
- :este es el elemento DOM al que se agrega la interfaz de usuario del componente de código.
- :se usa para recuperar cadenas localizadas en el idioma del usuario actual.
Se usa context.mode.trackContainerResize(true)) para que se llame cuando el componente de código cambie el tamaño.
Nota:
Actualmente, no hay ninguna manera de determinar si el componente de código se ejecuta dentro del arnés de pruebas. Debe detectar si el elemento está presente como indicador.
Actualizar el método updateView
Agregue lo siguiente a :
- antes de
- después de
public updateView(context: ComponentFramework.Context<IInputs>): void {
// Add code to update control view
}
Puede ver lo siguiente:
- Llame a React.createElement y pase la referencia al contenedor DOM que recibió dentro de la función.
- El componente se define dentro de y se importa en la parte superior del archivo.
- El y lo proporcionará el contexto primario cada vez que cambien (por ejemplo, la aplicación cambia el tamaño del componente de código o entras en modo de pantalla completa), ya que dentro de la función realizaste una llamada a trackContainerResize(true).
- Puede detectar cuándo hay nuevas filas que mostrar cuando la matriz updatedProperties contiene la cadena.
- En el arnés de prueba, la matriz no se rellena, por lo que puede usar la marca que usted establece en la función para anular la lógica que establece y . Se mantiene una referencia a los valores actuales hasta que cambian, de modo que no se mutan cuando se pasan al componente secundario a menos que se requiera una nueva representación de los datos.
- Dado que el componente de código mantiene el estado de la página que se muestra, el número de página se restablece cuando el contexto primario restablece los registros a la primera página. Sabes cuándo vuelves a la primera página cuando es false.
Actualización del método destroy
Por último, debe ordenar cuando se destruye el componente de código:
- antes de
- después de
public destroy(): void {
// Add code to cleanup control if necessary
}
Inicie la herramienta de ejecución de pruebas
Asegúrese de que todos los archivos se guarden y en el terminal use:
npm start watch
Debe establecer el ancho y el alto para ver la cuadrícula del componente de código que se completa con los tres registros de muestra. A continuación, puede exportar un conjunto de registros a un archivo CSV desde Dataverse y luego cargarlo en el arnés de pruebas usando el panel de Registros de Entradas de Datos:
Arnés de prueba
Estos son algunos datos de ejemplo separados por comas que puede guardar en un archivo .csv y usar:
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)
Nota:
Solo hay una sola columna que se muestra en el arnés de pruebas independientemente de las columnas que proporcione en el archivo CSV cargado. Esto se debe a que el arnés de prueba solo se muestra cuando hay uno definido. Si no se define , se rellenarán todas las columnas del archivo CSV cargado.
Agregar selección de fila
Aunque la interfaz de usuario de Fluent permite seleccionar registros de forma predeterminada, los registros seleccionados no están vinculados a la salida del componente de código. Necesita que las propiedades y reflejen los registros elegidos dentro de una aplicación canvas, de modo que se puedan actualizar los componentes relacionados. En este ejemplo, solo se permite la selección de un solo elemento a la vez, por lo que solo contendrá un único registro.
Actualizar las importaciones de Grid.tsx
Agregue lo siguiente a las importaciones dentro de :
- antes de
- después de
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';
Agregar setSelectedRecords a GridProps
En la interfaz , dentro , agregue lo siguiente:
- antes de
- después de
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;
}
Agregue la propiedad setSelectedRecords a Grid
Dentro del componente de función , actualice la desestructuración de para agregar la nueva propiedad .
- antes de
- después de
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
} = props;
Directamente debajo de eso, agregue:
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,
});
});
Los React.useCallback y useConst hooks garantizan que estos valores no muten entre renderizaciones y eviten la renderización innecesaria de componentes hijos.
El enlace useForceUpdate garantiza que, cuando se actualiza la selección, el componente se vuelve a representar para reflejar el recuento de selección actualizado.
Agregar selección a DetailsList
El objeto creado para mantener el estado de la selección se pasa al componente .
- antes de
- después de
<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>
Definir la devolución de llamada setSelectedRecords
Debe definir la nueva devolución de llamada dentro de y pasarla al componente . Cerca de la parte superior de la clase, agregue lo siguiente:
- antes de
- después de
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;
Nota:
El método se define como una función de flecha para enlazarlo a la instancia actual del componente de código.
La llamada a setSelectedRecordIds informa a la aplicación de lienzo que la selección ha cambiado, de modo que otros componentes que hagan referencia a y se actualizarán.
Agregue una nueva devolución de llamada a las propiedades de input
Por último, agregue la nueva devolución de llamada a las propiedades de entrada del componente
- antes de
- después de
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
);
Invocación del evento
Hay un patrón en las aplicaciones de lienzo en el que, si una galería o cuadrícula tiene una selección de elementos invocada (por ejemplo, al seleccionar un icono de cheurón), se activa el evento . Puede implementar este patrón mediante el método openDatasetItem del conjunto de datos.
Adición de onNavigate a la interfaz GridProps
Como antes, agregue una propiedad de devolución de llamada adicional en el componente agregando lo siguiente a la interfaz dentro de :
- antes de
- después de
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;
}
Agregar onNavigate a los accesorios de Grid
Nuevamente, debe agregar el nuevo accesorio a la desestructuración de los accesorios:
- antes de
- después de
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
setSelectedRecords,
} = props;
Agregar onItemInvoked a DetailsList
tiene una propiedad de devolución de llamada denominada que, a su vez, pasa la devolución de llamada a:
- antes de
- después de
<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>
Añadir el método onNavigate a index.ts
Agregue el método al método justo debajo del método :
onNavigate = (
item?: ComponentFramework.PropertyHelper.DataSetApi.EntityRecord
): void => {
if (item) {
this.context.parameters.records.openDatasetItem(item.getNamedReference());
}
};
Esto simplemente invoca el método en el registro del conjunto de datos para que el componente de código genere el evento. El método se define como una función de flecha para enlazarlo a la instancia actual del componente de código.
Debe pasar esta devolución de llamada a las propiedades del componente dentro del método :
- antes de
- después de
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
);
Al guardar todos los archivos, el arnés de pruebas se volverá a cargar. Utilice (o ) y seleccione Abrir archivo () para buscar y puede colocar un punto de interrupción dentro del método . Haga doble clic en una fila (o resáltela con las teclas de cursor y presionando ) y el proceso alcanzará el punto de interrupción porque realizará la llamada de retorno .
Depuración del evento OnNavigate de Canvas Data Grid en index.ts
Hay una referencia a porque la función se define como una función de flecha y se ha transpilado en un cierre de JavaScript para capturar la instancia de .
Agregar localización
Antes de continuar, debe agregar cadenas de recursos al componente de código para que pueda usar cadenas localizadas para mensajes como paginación, ordenación y filtrado. Agregue un nuevo archivo CanvasGrid\strings\CanvasGrid.1033.resx y use el editor de recursos de Visual Studio o Visual Studio Code con una extensión para escribir lo siguiente:
| Nombre | Importancia |
|---|---|
Records_Dataset_Display |
Registros |
FilteredRecordCount_Disp |
Recuento de registros filtrados |
FilteredRecordCount_Desc |
Número de registros después del filtrado |
HighlightValue_Disp |
Valor de resaltado |
HighlightValue_Desc |
El valor que indica que una fila debe ser resaltada |
HighlightColor_Disp |
Color de resaltado |
HighlightColor_Desc |
El color para resaltar una fila usando |
HighlightIndicator_Disp |
Resaltar campo indicador |
HighlightIndicator_Desc |
Configure el nombre del campo para compararlo con el Valor de Resaltado. |
Label_Grid_Footer |
Página ( seleccionada) |
Label_SortAZ |
De la A a la Z |
Label_SortZA |
de Z a A |
Label_DoesNotContainData |
No contiene datos |
Label_ShowFullScreen |
Mostrar pantalla completa |
Sugerencia
No se recomienda editar archivos directamente. En su lugar, use el editor de recursos de Visual Studio o una extensión para Visual Studio Code. Busque una extensión de Visual Studio Code: Busque en el Marketplace de Visual Studio un editor resx
Los datos de este archivo también se pueden establecer abriendo el archivo en el Bloc de notas y copiando el contenido XML siguiente:
<?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>
Tiene cadenas de recursos para propiedades y y asociadas . Estos se usarán en Power Apps Studio en tiempo de diseño en función del lenguaje del explorador del creador. También puede agregar cadenas de etiqueta que se pueden recuperar en tiempo de ejecución mediante getString. Más información: Implementación del componente de la API de localización.
Agregue este nuevo archivo de recursos al archivo dentro del elemento :
- antes de
- después de
<resources>
<code path="index.ts"
order="1" />
</resources>
Agregar ordenación y filtrado de columnas
Si desea permitir que el usuario ordene y filtre mediante encabezados de columna de cuadrícula, la interfaz de usuario de Fluent proporciona una manera sencilla de agregar menús contextuales a los encabezados de columna.
Agregar onSort y onFilter a GridProps
En primer lugar, agregue y a la interfaz en para proporcionar funciones de devolución de llamada para ordenar y filtrar.
- antes de
- después de
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;
}
Agregar onSort, onFilter y recursos a props
A continuación, agregue estas nuevas propiedades junto con la referencia (de modo que pueda recuperar etiquetas localizadas para ordenar y filtrar) a la desestructuración de propiedades.
- antes de
- después de
export const Grid = React.memo((props: GridProps) => {
const {
records,
sortedRecordIds,
columns,
width,
height,
hasNextPage,
hasPreviousPage,
sorting,
filtering,
currentPage,
itemsLoading,
setSelectedRecords,
onNavigate,
} = props;
Importación de componentes ContextualMenu
Debe agregar algunas importaciones a la parte superior de para que pueda usar el componente proporcionado por fluent UI. Puede usar importaciones basadas en rutas para reducir el tamaño del paquete.
import { ContextualMenu, DirectionalHint, IContextualMenuProps } from '@fluentui/react/lib/ContextualMenu';
Adición de la funcionalidad de representación del menú contextual
Ahora, agregue la funcionalidad de representación del menú contextual a justo debajo de la línea.
:
- antes de
- después de
const [isComponentLoading, setIsLoading] = React.useState<boolean>(false);
Verá lo siguiente:
- El estado controla la visibilidad del menú contextual que se representa mediante el componente fluent UI .
- Este código proporciona un filtro sencillo para mostrar solo los valores en los que el campo no contiene ningún dato. Puede ampliar esto para proporcionar filtrado adicional.
- Este código usa para mostrar etiquetas en el menú contextual que pueden localizarse.
- El hook, similar a , garantiza que los callbacks solo cambian cuando cambian los valores dependientes. Esto optimiza la representación de componentes secundarios.
Agregar nuevas funciones de menú contextual a los eventos de menú contextual y selección de columna
Agregue estas nuevas funciones de menú contextual a los eventos de menú contextual y selección de columna. Actualice el para agregar las devoluciones de llamada de y .
- antes de
- después de
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]);
Agregar menú contextual a la salida representada
Para que se muestre el menú contextual, debe agregarlo a la salida representada. Agregue lo siguiente directamente debajo del componente en la salida devuelta:
- antes de
- después de
<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>
Agregar funciones onSort y OnFilter
Ahora que has agregado la interfaz de usuario de clasificación y filtro, necesitas agregar las funciones de callback a para realizar la clasificación y el filtrado en los registros enlazados al componente de código. Agregue lo siguiente a justo debajo de la función :
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();
};
Verá lo siguiente:
- La ordenación y el filtro se aplican al conjunto de datos mediante las propiedades de ordenación y filtrado .
- Al modificar las columnas de ordenación, las definiciones de ordenación existentes deben quitarse mediante la función 'pop', en lugar de reemplazar la matriz de ordenación.
- Se debe llamar a Refresh después de aplicar la ordenación y el filtrado. Si se aplica un filtro y una ordenación al mismo tiempo, solo es necesario llamar a la actualización una vez.
Agregue devoluciones de llamada OnSort y OnFilter a la representación de Grid
Por último, puede pasar estas dos devoluciones de llamada a la llamada de renderizado.
- antes de
- después de
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
);
Nota:
En este momento, ya no puede probar con el arnés de pruebas porque no proporciona compatibilidad con la ordenación y el filtrado. Más adelante, puede implementar usando pac pcf push y, a continuación, agregar a una aplicación de lienzo para pruebas. Si lo desea, puede avanzar directamente a ese paso para ver cómo se ve el componente de código dentro de las aplicaciones de Canvas.
Actualiza la propiedad de salida FilteredRecordCount
Dado que la cuadrícula ahora puede filtrar registros internamente, es importante informar a la aplicación de lienzo cuántos registros se muestran. Esto es para que pueda mostrar un mensaje de tipo "Sin registros".
Sugerencia
Puede implementar esto internamente dentro del componente de código pero, sin embargo, se recomienda que la mayor parte de la interfaz de usuario se deje en manos de la aplicación de lienzo, ya que le dará al creador de la aplicación más flexibilidad.
Ya ha definido una propiedad de salida denominada en . Cuando se realiza el filtrado y se cargan los registros filtrados, se llamará a la función con cadena en la matriz updatedProperties . Si el número de registros ha cambiado, debe realizar una llamada a para que la aplicación Canvas sepa que debe actualizar los controles que utilicen la propiedad . Dentro del método de , agregue lo siguiente justo encima de y debajo de :
- antes de
- después de
const allocatedHeight = parseInt(
context.mode.allocatedHeight as unknown as string
);
Agregar el FilteredRecordCount a getOutputs
Esto actualiza la clase de componente de código que usted definió anteriormente, cuando estos son diferentes de los nuevos datos recibidos. Después de llamar a , debe asegurarse de que se devuelva el valor cuando se llame a , por lo tanto, debe actualizar el método para que sea:
- antes de
- después de
public getOutputs(): IOutputs {
return {};
}
Agregar paginación a la cuadrícula
En el caso de grandes conjuntos de datos, las aplicaciones canvas separarán los registros en múltiples páginas. Puede agregar un pie de página que muestre los controles de navegación de página. Cada botón se representará mediante una interfaz de usuario de Fluent, que debe importar.
Agregar IconButton a las importaciones
Agregue esto a las importaciones dentro de .
import { IconButton } from '@fluentui/react/lib/Button';
Agregar función stringFormat
En el siguiente paso se agregarán capacidades para cargar el formato de la etiqueta del indicador de página desde las cadenas de recursos () y formatear usando una función sencilla . Esta función podría estar igualmente en un archivo independiente y compartirse entre los componentes para mayor comodidad:
En este tutorial, agréguelo en la parte superior de , justo debajo de .
function stringFormat(template: string, ...args: string[]): string {
for (const k in args) {
template = template.replace("{" + k + "}", args[k]);
}
return template;
}
Agregar botones de paginación
En , agregue lo siguiente debajo del existente que contiene :
- antes de
- después de
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>
);
Verá lo siguiente:
- garantiza que el pie de página se apilará debajo de . El atributo se usa para asegurarse de que la cuadrícula se expande para rellenar el espacio disponible.
- Cargas el formato de la etiqueta de indicador de página de las cadenas de recursos () y lo estructuras utilizando la función que añadiste en el paso anterior.
- Puede proporcionar texto para accesibilidad en la paginación .
- El estilo del pie de página podría aplicarse igualmente mediante un nombre de clase CSS que haga referencia a un archivo CSS agregado al componente de código.
Agregar props de devolución de llamada para admitir la paginación
A continuación, debe agregar los props de devolución de llamada , y que faltan.
En la interfaz , agregue lo siguiente:
- antes de
- después de
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;
}
Agregar nuevos props de paginación a Grid
Agregue estas nuevas propiedades a la desestructuración de las propiedades:
- antes de
- después de
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;
Agregar devoluciones de llamada a index.ts
Agregue estas devoluciones de llamada a debajo del método :
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);
};
Luego, actualice la llamada de para incluir estos callbacks:
- antes de
- después de
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
);
Adición de compatibilidad con pantalla completa
Los componentes de código ofrecen la capacidad de mostrarse en modo de pantalla completa. Es especialmente útil en tamaños de pantalla pequeños o donde hay espacio limitado para el componente de código dentro de una pantalla de aplicación de tipo lienzo.
Importar componente de Fluent UI Link
Para iniciar el modo de pantalla completa, puede usar el componente fluent UI . Agréguelo a las importaciones en la parte superior de :
import { Link } from '@fluentui/react/lib/Link';
Agregar el control Link
Para agregar un vínculo de pantalla completa, agregue lo siguiente al existente que contiene los controles de paginación.
Nota:
Asegúrese de agregar esto al anidado y no a la raíz .
- antes de
- después de
<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>
Verá lo siguiente:
- Este código usa recursos para mostrar la etiqueta para admitir la localización.
- Si el modo de pantalla completa está abierto, no se muestra el vínculo. En su lugar, el contexto de la aplicación principal representa automáticamente un icono de cierre.
Agregar propiedades para permitir compatibilidad con pantalla completa en GridProps
Agrega los props y a la interfaz dentro de para ofrecer funciones de callback que ordenen y filtren.
- antes de
- después de
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;
}
Agregar props para admitir pantalla completa a Grid
Agregue estas nuevas propiedades a la desestructuración de las propiedades:
- antes de
- después de
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;
Actualizar index.ts para admitir la pantalla completa en Grid
Para proporcionar estas nuevas propiedades, dentro de , agregue el siguiente método de devolución de llamada :
onFullScreen = (): void => {
this.context.mode.setFullScreen(true);
};
La llamada a setFullScreen hace que el componente de código abra el modo de pantalla completa y ajuste y en consecuencia, debido a la llamada a en el método . Una vez abierto el modo de pantalla completa, se llamará a, actualizando el renderizado del componente con el nuevo tamaño. contiene o , en función de la transición que se está produciendo.
Para almacenar el estado del modo de pantalla completa, agregue un nuevo campo a la clase dentro de :
- antes de
- después de
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;
Editar updateView para realizar un seguimiento del estado
Agregue lo siguiente al método para realizar el seguimiento del estado:
- antes de
- después de
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;
}
Pase la devolución de llamada y el campo isFullScreen para representar en la cuadrícula
Ahora puede pasar la devolución de llamada y el campo de a las propiedades de representación .
- antes de
- después de
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
);
Resaltando filas
Ya está listo para agregar la funcionalidad de resaltado de fila condicional. Ya ha definido las propiedades de entrada y , y las . permite al creador elegir un campo que se va a usar para comparar con el valor que proporcionan en .
Importación de tipos para admitir el resaltado
La representación de filas personalizada en requiere algunas importaciones adicionales. Ya hay algunos tipos de , por lo que agregue , y a esa instrucción de importación en .
- antes de
- después de
import {
DetailsList,
ConstrainMode,
DetailsListLayoutMode,
IColumn,
IDetailsHeaderProps
} from '@fluentui/react/lib/DetailsList';
Ahora, cree el representador de filas personalizado agregando lo siguiente justo debajo del bloque:
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;
};
Verá lo siguiente:
- Puede recuperar el valor del campo elegido por el creador mediante el alias mediante:
. - Cuando el valor del campo coincide con el valor de la propiedad de entrada proporcionada en el componente de código, puede agregar un color de fondo a la fila.
- utiliza el componente para renderizar las columnas que usted definió. No es necesario cambiar el comportamiento distinto del color de fondo.
Agregue accesorios adicionales para apoyar el resaltado
Agregue algunos props adicionales para y que serán proporcionados por la representación dentro de . Ya ha agregado a la interfaz, por lo que solo tiene que agregarlos a la estructuración de propiedades:
- antes de
- después de
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;
Agregue el método onRenderRow a DetailsList.
Pase el método a las propiedades:
- antes de
- después de
<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>
Implementación y configuración del componente
Ahora que ha implementado todas las características, debe implementar el componente de código en Microsoft Dataverse para las pruebas.
Dentro del entorno de Dataverse, asegúrese de que hay un publicador creado con un prefijo de :
Agregar nuevo publicador
Esto también podría ser su propio editor, siempre que actualice el parámetro de prefijo del editor en la llamada a pac pcf push que aparece a continuación. Más información: Creación de un publicador de soluciones.
Una vez que haya guardado el editor, estará listo para autorizar la CLI en su entorno para que podamos enviar el componente de código compilado. En la línea de comandos, use:
pac auth create --url https://myorg.crm.dynamics.comReemplace por la dirección URL de su propio entorno de Dataverse. Inicie sesión con un usuario administrador o personalizador cuando se le solicite. Los privilegios proporcionados por estos roles de usuario son necesarios para implementar cualquier componente de código en Dataverse.
Para implementar el componente de código, use:
pac pcf push --publisher-prefix samplesNota:
Si recibe el error,
Missing required tool: MSBuild.exe/dotnet.exe. Please add MSBuild.exe/dotnet.exe in Path environment variable or use 'Developer Command Prompt for VS, debe instalar Visual Studio 2019 para Windows y Mac o Build Tools for Visual Studio 2019, asegurándose de seleccionar la carga de trabajo "herramientas de compilación .NET", tal como se describe en los requisitos previos.Una vez completado, este proceso habrá creado una solución temporal pequeña denominada PowerAppTools_samples en el entorno y el componente de código se agregará a esta solución. Puede mover el componente de código a su propia solución más adelante si es necesario. Más información: Administración del ciclo de vida de las aplicaciones (ALM) de componentes de código.
solución PowerAppsTools_samples
Para usar componentes de código dentro de aplicaciones tipo lienzo, debe habilitar el marco de componentes de Power Apps para aplicaciones tipo lienzo en el entorno que usa.
a) Abra el Centro de administración (admin.powerplatform.microsoft.com) y vaya al entorno. b. Vaya a ConfiguraciónCaracterísticas . Asegúrese de que Power Apps componente marco para aplicaciones de lienzo está Activado:
Habilitación de componentes de código
Cree una nueva aplicación de lienzo utilizando el diseño Tablet.
En el panel Insertar , seleccione Obtener más componentes.
Seleccione la pestaña Código en el panel Importar componentes .
Seleccione el componente.
Seleccione Importar. El componente de código aparecerá ahora en Componentes de código en el panel Insertar .
Arrastre el componente
CanvasGrida la pantalla y enlázalo a la tablaContactsen Microsoft Dataverse.Establezca las siguientes propiedades en el componente de código mediante el panel de propiedades:
- Valor : este es el valor que tiene cuando el registro está inactivo.
- Color : este es el color que se va a usar cuando el registro está inactivo.
- : este es el campo con el que se va a comparar. Esto estará en el panel Avanzado de la sección DATA .
Panel de propiedades
Agregue un nuevo componente y asígnele el nombre .
Actualice la propiedad para que sea .
Cuando escriba la entrada de texto, verá que los contactos se filtran en la cuadrícula.
Agregue una nueva etiqueta Text y establezca el texto como "No se encontraron registros". Coloque la etiqueta en la parte superior de la cuadrícula de lienzo.
Establezca la propiedad Visible de la etiqueta Texto como .
Esto significa que cuando no hay registros que coincidan con el valor o si se aplica un filtro de columna mediante el menú contextual que no devuelve ningún registro (por ejemplo, Nombre completo no contiene datos), se mostrará la etiqueta.
Agregue un formulario para mostrar (desde el grupo Entrada en el panel Insertar ).
Establezca el formulario en la tabla y agregue algunos campos de formulario.
Establezca la propiedad form en .
Ahora debería ver que al seleccionar elementos en la cuadrícula, el formulario muestra el elemento seleccionado.
Agregue una nueva pantalla a la aplicación de lienzo llamada .
Copie el formulario de la pantalla anterior y péguelo en la nueva pantalla.
Establezca la propiedad como .
Cuando invoca la acción de selección de fila de la cuadrícula, ahora debería ver que la aplicación navega a la segunda pantalla con el elemento seleccionado.
Depurar después de implementar
Puede depurar fácilmente el componente de código mientras se ejecuta dentro de la Canvas App abriendo las Herramientas de Desarrollo mediante .
Seleccione y escriba o . A continuación, puede establecer un punto de interrupción y ejecutar el código paso a paso.
Depuración en aplicaciones de lienzo
Si necesita realizar más cambios en el componente, no es necesario implementarlos cada vez. En su lugar, use la técnica descrita en Depurar componentes de código para crear un AutoResponder en Fiddler para cargar el archivo desde el sistema de archivos local mientras está en ejecución.
AutoResponder tendría un aspecto similar al siguiente:
REGEX:(.*?)((?'folder'css|html)(%252f|\/))?SampleNamespace\.CanvasGrid[\.\/](?'fname'[^?]*\.*)(.*?)$
C:\repos\CanvasGrid\out\controls\CanvasGrid\${folder}\${fname}
Regla AutoResponder
También deberá habilitar los filtros para agregar el encabezado. Más información: Debugging después de realizar la implementación en Microsoft Dataverse.
Tendrá que vaciar la memoria caché y actualizar forzadamente en la sesión del navegador para que el archivo AutoResponder sea reconocido. Una vez cargado, simplemente puede actualizar el explorador, ya que Fiddler agregará un encabezado de control de caché al archivo para evitar que se almacene en caché.
Una vez que esté satisfecho con los cambios, puede incrementar la versión de revisión en el manifiesto y, a continuación, volver a implementar mediante pac pcf push.
Hasta ahora, ha implementado una compilación de desarrollo, que no está optimizada y se ejecutará más lentamente en tiempo de ejecución. Puede optar por implementar una compilación optimizada usando pac pcf push editando el . Debajo de , agregue lo siguiente:
- antes de
- después de
<PropertyGroup>
<Name>CanvasGrid</Name>
<ProjectGuid>a670bba8-e0ae-49ed-8cd2-73917bace346</ProjectGuid>
<OutputPath>$(MSBuildThisFileDirectory)out\controls</OutputPath>
</PropertyGroup>
Artículos relacionados
Administración del ciclo de vida de la aplicación (ALM) con Microsoft Power Platform
Referencia de la API del marco de componentes de Power Apps
Crear el primer componente
Depurar componentes de código