Filtro de búsqueda de cuadrícula de árbol React

    La función de filtro de búsqueda Ignite UI for React en React Tree Grid permite el proceso de búsqueda de valores en la recopilación de datos. Facilitamos la configuración de esta funcionalidad y se puede implementar con un cuadro de entrada de búsqueda, botones, navegación con el teclado y otras funciones útiles para una experiencia de usuario aún mejor. Si bien los navegadores brindan de forma nativa la funcionalidad de búsqueda de contenido, la mayoría de las veces IgrTreeGrid virtualiza sus columnas y filas que están fuera de la vista. En estos casos, la búsqueda nativa del navegador no puede buscar datos en las celdas virtualizadas, ya que no son parte del DOM. Hemos ampliado la cuadrícula basada en tablas de React Material con una API de búsqueda que le permite buscar en el contenido virtualizado de IgrTreeGrid.

    React Search Example

    En el ejemplo siguiente se representa IgrTreeGrid un cuadro de entrada de búsqueda que permite buscar en todas las columnas y filas, así como opciones de filtrado específicas para cada columna.

    EXAMPLE
    DATA
    TSX
    CSS

    Like this sample? Get access to our complete Ignite UI for React toolkit and start building your own apps in minutes. Download it for free.

    React Search Usage

    Tree Grid Setup

    Comencemos creando nuestra cuadrícula y vinculándola a nuestros datos. ¡También agregaremos algunos estilos personalizados para los componentes que usaremos!

    <IgrTreeGrid ref={gridRef} data={data} autoGenerate="false" primaryKey="ID" foreignKey="ParentID" allowFiltering="true" height="100%" width="100%">
        <IgrColumn field="Name" dataType="string" sortable="true"></IgrColumn>        
        <IgrColumn field="ID" dataType="number" sortable="true"></IgrColumn>        
        <IgrColumn field="Title" dataType="string" sortable="true"></IgrColumn>        
        <IgrColumn field="Age" dataType="number" sortable="true"></IgrColumn>        
        <IgrColumn field="HireDate" dataType="date" sortable="true"></IgrColumn>  
    </IgrTreeGrid>
    tsx

    ¡Genial, y ahora preparémonos para la API de búsqueda de nuestro IgrTreeGrid! Podemos crear algunas propiedades, que se pueden usar para almacenar el texto buscado actualmente y si la búsqueda distingue entre mayúsculas y minúsculas y/o por una coincidencia exacta.

    const gridRef = useRef<IgrTreeGrid>(null);
    const searchIconRef = useRef<IgrIconButton>(null);
    const clearIconRef = useRef<IgrIconButton>(null);
    const iconButtonNextRef = useRef<IgrIconButton>(null);
    const iconButtonPrevRef = useRef<IgrIconButton>(null);
    const caseSensitiveChipRef = useRef<IgrChip>(null);
    const exactMatchChipRef = useRef<IgrChip>(null);
    const [searchText, setSearchText] = useState('');
    tsx

    React Search Box Input

    ¡Ahora vamos a crear nuestra entrada de búsqueda! Al vincular nuestra searchText propiedad a nuestra value entrada recién creada y suscribirnos al inputOccured evento, podemos detectar cada searchText modificación por parte del usuario. Esto nos permitirá usar los métodos y IgrTreeGrid​ ​findPrev para resaltar todas las ocurrencias findNext de y searchText desplazarnos hasta el siguiente/anterior (dependiendo del método que hayamos invocado).

    Tanto el método findNext como el findPrev tienen tres argumentos:

    • Text: cadena (el texto que estamos buscando)
    • (opcional) CaseSensitive: booleano (si la búsqueda distingue entre mayúsculas y minúsculas o no, el valor predeterminado es falso)
    • (opcional) ExactMatch: booleano (si la búsqueda se realiza por coincidencia exacta o no, el valor predeterminado es falso)

    Al buscar por coincidencia exacta, la API de búsqueda resaltará como resultados solo los valores de celda que coincidan completamente con SearchText teniendo en cuenta también la distinción entre mayúsculas y minúsculas. Por ejemplo, las cadenas 'software' y 'Software' coinciden exactamente sin tener en cuenta la distinción entre mayúsculas y minúsculas.

    Los métodos anteriores devuelven un valor numérico (el número de veces que contiene IgrTreeGrid la cadena dada).

    function handleOnSearchChange(input: IgrInput, event: IgrComponentValueChangedEventArgs) {
        setSearchText(event.detail);
        gridRef.current.findNext(event.detail, caseSensitiveChipRef.current.selected, exactMatchChipRef.current.selected);
    }
    
    function nextSearch() {
        gridRef.current.findNext(searchText, caseSensitiveChipRef.current.selected, exactMatchChipRef.current.selected);
    }
    
    
    <IgrInput name="searchBox" value={searchText} inputOcurred={handleOnSearchChange}>
    </IgrInput>
    tsx

    Add Search Buttons

    Para buscar y navegar libremente entre nuestros resultados de búsqueda, creemos un par de botones invocando los métodos findNext y findPrev dentro de los respectivos controladores de eventos de clic de los botones.

    function prevSearch() {
        gridRef.current.findPrev(searchText, caseSensitiveChipRef.current.selected, exactMatchChipRef.current.selected);
    }
    
    function nextSearch() {
        gridRef.current.findNext(searchText, caseSensitiveChipRef.current.selected, exactMatchChipRef.current.selected);
    }
    
    <IgrIconButton key="prevIconButton" ref={iconButtonPrevRef} variant="flat" name="prev" collection="material" clicked={prevSearch}>
    </IgrIconButton>
    <IgrIconButton key="nextIconButton" ref={iconButtonNextRef} variant="flat" name="next" collection="material" clicked={nextSearch}>
    </IgrIconButton>
    tsx

    También podemos permitir que los usuarios naveguen por los resultados utilizando las teclas de flecha del teclado y la tecla Intro. Para lograr esto, podemos manejar el evento de pulsación de tecla de nuestra entrada de búsqueda evitando el movimiento del cursor predeterminado de la entrada con el método PreventDefault e invocando los métodos findNext / findPrev dependiendo de qué tecla haya presionado el usuario.

    function searchKeyDown(e: KeyboardEvent<HTMLElement>) {
        if (e.key === 'Enter' || e.key === 'ArrowDown') {
            e.preventDefault();
            gridRef.current.findNext(searchText, caseSensitiveChipRef.current.selected, exactMatchChipRef.current.selected);
        } else if (e.key === 'ArrowUp') {
            e.preventDefault();
            gridRef.current.findPrev(searchText, caseSensitiveChipRef.current.selected, exactMatchChipRef.current.selected);
        }
    }
    
    function handleOnSearchChange(input: IgrInput, event: IgrComponentValueChangedEventArgs) {
        setSearchText(event.detail);
        gridRef.current.findNext(event.detail, caseSensitiveChipRef.current.selected, exactMatchChipRef.current.selected);
    }
    
    <div onKeyDown={searchKeyDown}>
        <IgrInput name="searchBox" value={searchText} inputOcurred={handleOnSearchChange}></IgrInput>
    </div>
    tsx

    Case Sensitive and Exact Match

    Ahora permitamos que el usuario elija si la búsqueda debe distinguir entre mayúsculas y minúsculas y/o por una coincidencia exacta. Para ello podemos utilizar el IgrChip y obtener su referencia y utilizar la propiedad selected.

    const caseSensitiveChipRef = useRef<IgrChip>(null);
    const exactMatchChipRef = useRef<IgrChip>(null);
    
    function updateSearch() {
        gridRef.current.findNext("searchValue", caseSensitiveChipRef.current.selected, exactMatchChipRef.current.selected);
    }
    
    <IgrChip ref={caseSensitiveChipRef} key="caseSensitiveChip" selectable="true">
        <span key="caseSensitive">Case Sensitive</span>
    </IgrChip>
    <IgrChip ref={exactMatchChipRef} key="exactMatchChip" selectable="true">
        <span key="exactMatch">Exact Match</span>
    </IgrChip>
    tsx

    Persistence

    ¿Qué pasa si queremos filtrar y ordenar nuestros IgrTreeGrid registros o incluso agregar y eliminar? Después de tales operaciones, los aspectos destacados de nuestra búsqueda actual se actualizan automáticamente y persisten sobre cualquier texto que coincida con el SearchText! Además, la búsqueda funcionará con paginación y conservará los aspectos destacados a través de cambios en la IgrTreeGrid propiedad del PerPage producto.

    Adding icons

    Al utilizar algunos de nuestros otros componentes, podemos crear una interfaz de usuario enriquecida y mejorar el diseño general de toda nuestra barra de búsqueda. Podemos tener un bonito icono de búsqueda o eliminación a la izquierda de la entrada de búsqueda, un par de chips para nuestras opciones de búsqueda y algunos iconos de diseño de materiales combinados con bonitos botones de estilo ondulado para nuestra navegación a la derecha. Podemos envolver estos componentes dentro de un grupo de entrada para un diseño más refinado.

    import { IgrTreeGridModule } from "igniteui-react-grids";
    import { IgrChipModule, IgrIconButtonModule, IgrInputModule } from "igniteui-react";
    
    const mods: any[] = [IgrTreeGridModule, IgrChipModule, IgrIconButtonModule, IgrInputModule];
    mods.forEach((m) => m.register());
    tsx

    Finalmente, ¡actualicemos nuestra plantilla con los nuevos componentes!

    const prevIconText =
      "<svg width='24' height='24' viewBox='0 0 24 24'><path d='M15.41 7.41 14 6l-6 6 6 6 1.41-1.41L10.83 12z'></path></svg>";
    const nextIconText =
      "<svg width='24' height='24' viewBox='0 0 24 24'><path d='M10 6 8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z'></path></svg>";
    const searchIconText =
    "<svg width='24' height='24' viewBox='0 0 24 24'><path d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z' /></svg>";
    const clearIconText =
      "<svg width='24' height='24' viewBox='0 0 24 24' title='Clear'><path d='M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z'></path></svg>";
    
    useEffect(() => {
        if (searchIconRef?.current) {
            searchIconRef.current.registerIconFromText("search", searchIconText, "material");
            searchIconRef.current.registerIconFromText("clear", clearIconText, "material");
        }
        if (iconButtonPrevRef?.current) {
            iconButtonPrevRef.current.registerIconFromText("prev", prevIconText,"material");
        }
        if (iconButtonNextRef?.current) {
            iconButtonNextRef.current.registerIconFromText("next", nextIconText, "material");
        }
    }, []);
    
    function clearSearch() {
      setSearchText('');
      gridRef.current.clearSearch();
    }
    
    <IgrInput name="searchBox" value={searchText} inputOcurred={handleOnSearchChange}>
        <div slot="prefix" key="prefix">
            {searchText.length === 0 ? (
              <IgrIconButton key="searchIcon" ref={searchIconRef} variant="flat" name="search" collection="material">
              </IgrIconButton>
            ) : (
              <IgrIconButton key="clearIcon" ref={clearIconRef} variant="flat" name="clear" collection="material" clicked={clearSearch}>
              </IgrIconButton>
            )}        
        </div>
        <div slot="suffix" key="chipSuffix">
            <IgrChip ref={caseSensitiveChipRef} key="caseSensitiveChip" selectable="true">
            <span key="caseSensitive">Case Sensitive</span>
            </IgrChip>
            <IgrChip ref={exactMatchChipRef} key="exactMatchChip" selectable="true">
            <span key="exactMatch">Exact Match</span>
            </IgrChip>
        </div>
        <div slot="suffix" key="buttonsSuffix">
            <IgrIconButton key="prevIconButton" ref={iconButtonPrevRef} variant="flat" name="prev" collection="material" clicked={prevSearch}>
            </IgrIconButton>
            <IgrIconButton key="nextIconButton" ref={iconButtonNextRef} variant="flat" name="next" collection="material" clicked={nextSearch}>
            </IgrIconButton>
        </div>
    </IgrInput>
    tsx

    A la derecha de nuestro grupo de entrada, creemos tres contenedores separados con los siguientes propósitos:

    • Para mostrar un par de chips que alternan las propiedades CaseSensitive y ExactMatch. Hemos reemplazado las casillas de verificación con dos chips elegantes que cambian de color según estas propiedades. Cada vez que se hace clic en un chip, invocamos su respectivo controlador.
    <div slot="suffix" key="chipSuffix">
        <IgrChip ref={caseSensitiveChipRef} key="caseSensitiveChip" selectable="true" select={handleCaseSensitiveChange}>
            <span key="caseSensitive">Case Sensitive</span>
        </IgrChip>
        <IgrChip ref={exactMatchChipRef} key="exactMatchChip" selectable="true" select={handleExactMatchChange}>
            <span key="exactMatch">Exact Match</span>
        </IgrChip>
    </div>
    
    function handleCaseSensitiveChange(chip: IgrChip, event: IgrComponentBoolValueChangedEventArgs) {
      gridRef.current.findNext(searchText, event.detail, exactMatchChipRef.current.selected);
    }
    function handleExactMatchChange(chip: IgrChip, event: IgrComponentBoolValueChangedEventArgs) {
      gridRef.current.findNext(searchText, caseSensitiveChipRef.current.selected, event.detail);
    }
    tsx
    • Para los botones de navegación de búsqueda, hemos transformado nuestras entradas en botones de estilo ondulado con íconos de materiales. Los controladores para los eventos de clic siguen siendo los mismos: invocando los métodos findNext / findPrev.
    function prevSearch() {
        gridRef.current.findPrev(searchText, caseSensitiveChipRef.current.selected, exactMatchChipRef.current.selected);
    }
    
    function nextSearch() {
        gridRef.current.findNext(searchText, caseSensitiveChipRef.current.selected, exactMatchChipRef.current.selected);
    }
    
    <div slot="suffix" key="buttonsSuffix">
        <IgrIconButton key="prevIconButton" ref={iconButtonPrevRef} variant="flat" name="prev" collection="material" clicked={prevSearch}>
        </IgrIconButton>
        <IgrIconButton key="nextIconButton" ref={iconButtonNextRef} variant="flat" name="next" collection="material" clicked={nextSearch}>
        </IgrIconButton>
    </div>
    tsx
    Ignite UI for React | CTA Banner

    Known Limitations

    Limitación Descripción
    Buscando en celdas con una plantilla Las funciones destacadas de búsqueda funcionan solo para las plantillas de celda predeterminadas. Si tiene una columna con una plantilla de celda personalizada, los resaltados no funcionarán, por lo que deberá utilizar enfoques alternativos, como un formateador de columnas, o configurar elsearchable propiedad en la columna a falso.
    Virtualización remota La búsqueda no funcionará correctamente al utilizar la virtualización remota
    Celdas con texto cortado Cuando el texto en la celda es demasiado grande para caber y el texto que estamos buscando está cortado por los puntos suspensivos, aún nos desplazaremos hasta la celda y la incluiremos en el recuento de coincidencias, pero no se resaltará nada.

    API References

    En este artículo, implementamos nuestra propia barra de búsqueda para IgrTreeGrid con algunas funciones adicionales para navegar entre los resultados de búsqueda. También usamos algunas funciones adicionales Ignite UI for React como íconos, chips y entradas. La API de búsqueda se detalla a continuación.

    IgrTreeGrid methods:

    IgrColumn properties:

    Componentes adicionales con API relativas que se utilizaron:

    Additional Resources

    Nuestra comunidad es activa y siempre da la bienvenida a nuevas ideas.