Blazor Grid Overview and Configuration

    The Blazor Grid is used as a feature-rich control for displaying data in a tabular format quickly and easily. Modern grids are complex and are usually packed with a set of features like data selection, excel style filtering, sorting, paging, templating, column moving, exporting to Excel and CSV, and more.

    Grid

    Blazor Grid Example

    In this grid example, you can see how users can do both basic and excel-style filtering, live-data sorting, as well as the use of grid summaries and cell templating. The demo also includes paging set to display 10 items per page.

    Getting Started with Blazor Grid

    Dependencies

    To get started with the Blazor grid, first you need to install the Ignite UI for Blazor package.

    Please refer to these topics on adding the IgniteUI.Blazor package:

    You also need to include the following CSS link in the index.html file of your application to provide the necessary styles to the grid:

    <link href="_content/IgniteUI.Blazor/themes/grid/light/bootstrap.css" rel="stylesheet" />
    

    Afterwards, you may start implementing the control by adding the following namespaces:

    @using IgniteUI.Blazor.Controls
    

    Component Modules

    // in Program.cs file
    
    builder.Services.AddIgniteUIBlazor(typeof(IgbGridModule));
    

    Usage

    Now that we have the grid packages imported, let’s get started with the basic configuration and bind to local data:

    <IgbGrid Id="grid1" Data="data" AutoGenerate="true"></IgbGrid>
    

    The Id property is a string value and is the unique identifier of the grid which will be auto-generated if not provided, while data binds the grid, in this case to local data.

    The AutoGenerate property tells the grid to auto generate the grid's IgbColumn components based on the data source fields. It will also try to deduce the appropriate data type for the column if possible. Otherwise, the developer needs to explicitly define the columns and the mapping to the data source fields.

    Editable Blazor Grid

    Each operation for grid editing includes batch operations, meaning the API gives you the option to group edits into a single server call, or you can perform grid edit / update operations as they occur with grid interactions. Along with a great developer experience as an editable grid with CRUD operations, the grid includes Excel-like keyboard navigation. Common default grid navigation is included, plus the option to override any navigation option to meet the needs of your customers. An editable grid in with a great navigation scheme is critical to any modern line of business application, with the Ignite UI grid we make it easy.

    Following this topic you will learn more about cell template and cell editing template and editing.

    Grid Column Configuration

    IgbColumn is used to define the grid's columns collection and to enable features per column like sorting and filtering. Cell, header, and footer templates are also available.

    Defining Columns

    Let's turn the AutoGenerate property off and define the columns collection in the markup:

    <IgbGrid AutoGenerate=false AllowFiltering=true>
        <IgbColumn Field="Name" Sortable=true />
        <IgbColumn Field="AthleteNumber" Sortable=true Header="Athlete Number" Filterable=false/>
        <IgbColumn Field="TrackProgress" Header="Track Progress" Filterable=false />
    </IgbGrid>
    

    Header Template

    The header template can be set to modify the column headers. The snippets below show you how to format the header text to upper case.

    <IgbColumn Field="Name" HeaderTemplateScript="UpperCaseTemplate" />
    
    //In JavaScript:
    igRegisterScript("UpperCaseTemplate", (ctx) => {
    
        var html = window.igTemplating.html;
    
        return html`${this.formatUppercase(ctx.column.field)}`;
    
    }, false)
    
    function formatUppercase(value) {
        return value.toUpperCase();
    }
    
    Note

    Whenever a header template is used along with grouping/moving functionality the column header area becomes draggable and you cannot access the custom elements part of the header template until you mark them as not draggable. Example below.

    <IgbColumn Field="ProductName" Header="Product Name" Groupable=true HasSummary=true HeaderTemplateScript="ProductNameHeaderTemplate" />
    
    //In JavaScript:
    igRegisterScript("ProductNameHeaderTemplate", (ctx) => {
    
        var html = window.igTemplating.html;
    
        return html`
            <div class="text">${ctx.cell.column.field}</div>
            <igc-icon [attr.draggable]="false">functions</igc-icon>
        `;
    }, false)
    

    As you can see, we are adding Draggable attribute set to false.

    Cell Template

    When cell template is set it changes all the cells in the column. The context object provided in the template consists of the cell value provided implicitly and the cell object itself. It can be used to define a template where the cells' text could be formatted e.g. as title case.

    <IgbColumn Field="Name" BodyTemplateScript="NameCellTemplate"/>
    
    //In JavaScript:
    igRegisterScript("NameCellTemplate", (ctx) => {
        var html = window.igTemplating.html;
    
        return html`${this.formatTitleCase(ctx.cell.value)}`;
    }, false);
    
    function formatTitleCase(value) {
        return value;
    }
    

    In the snippet above we take a reference to the implicitly provided cell value. This is sufficient if you just want to present some data and maybe apply some custom styling or pipe transforms over the value of the cell. However even more useful is to take the GridCell instance itself as shown below:

    <IgbGrid AutoGenerate=false>
        <IgbColumn Field="Name" BodyTemplateScript="NameCellTemplate" />
        <IgbColumn Field="Subscription" BodyTemplateScript="SubscriptionCellTemplate" />
    </IgbGrid>
    
    //In JavaScript:
    igRegisterScript("NameCellTemplate", (ctx) => {
        var html = window.igTemplating.html;
        return html`
            <span tabindex="0" onkeydown="${this.deleteRow(ctx.cell.id)}">${this.formatTitleCase(ctx.cell.value)}</span>
        `;
    }, false);
    
    igRegisterScript("SubscriptionCellTemplate", (ctx) => {
        var html = window.igTemplating.html;
        return html`
            <input type="checkbox" value="${ctx.cell.value}" onchange="${this.updateValue(ctx.cell.value)}" />
        `;
    }, false);
    
    function updateValue(value) {
    }
    
    function deleteRow(rowId) {
    }
    
    function formatTitleCase(value) {
    }
    
    Note

    The grid exposes a default handling for number, string, date and boolean column types. For example, the column will display check or close icon, instead of true/false by default, for boolean column type.

    When properly implemented, the cell editing template also ensures that the cell's EditValue will correctly pass through the grid editing event cycle.

    Cell Editing Template

    The column also accepts one last template that will be used when a cell is in edit mode. As with the other column templates, the provided context object is again the cell value and the cell object itself. Of course in order to make the edit-mode template accessible to end users, you need to set the Editable property of the column to true.

    <IgbColumn Field="Price" Editable=true DataType="GridColumnDataType.Number" InlineEditorTemplateScript="PriceCellTemplate" />
    
    //In JavaScript:
    igRegisterScript("PriceCellTemplate", (ctx) => {
        var html = window.igTemplating.html;
    
        return html`
            <label>
                Enter the new price tag
            </label>
            <input name="price" type="number" value="${ctx.cell.value}" onchange="${this.updateValue(ctx.cell.value)}"  />
        `;
    }, false);
    
    function updateValue(value) {
    }
    

    Make sure to check the API for the GridCell in order to get accustomed with the provided properties you can use in your templates.

    Column Template API

    Each of the column templates can be changed programmatically at any point through the IgbColumn object itself. For example in the code below, we have declared two templates for our user data. In our TypeScript code we'll get references to the templates themselves and then based on some condition we will render the appropriate template for the column in our application.

    <IgbGrid ColumnInit=OnColumnInit />
    
    @code {
        public void OnColumnInit(IgbColumnComponentEventArgs args)
        {
            IgbColumn column = args.Detail;
            // Return the appropriate template based on some condition.
            // For example saved user settings, viewport size, etc.
            column.BodyTemplateScript = "NormalViewTemplate";
        }
    }
    
    //In JavaScript:
    igRegisterScript("NormalViewTemplate", (ctx) => {
        var html = window.igTemplating.html;
        return html`
            <div class="user-details">${ctx.cell.value}</div>
            <user-details-component></user-details-component>
        `;
    }, false);
    
    igRegisterScript("SmallViewTemplate", (ctx) => {
        var html = window.igTemplating.html;
        return html`
            <div class="user-details-small" style="color: blue">${ctx.cell.value}</div>
        `;
    }, false);
    

    Column properties can also be set in code in the ColumnInit event which is emitted when the columns are initialized in the grid.

    <IgbGrid ColumnInit=OnColumnInit />
    
    @code {
        public void OnColumnInit(IgbColumnComponentEventArgs args)
        {
            IgbColumn column = args.Detail;
            if(column.Field == "ProductName"){
                column.Sortable = true;
                column.Editable = true;
            }
        }
    }
    

    The code above will make the ProductName column sortable and editable and will instantiate the corresponding features UI (like inputs for editing, etc.).

    Custom Display Format

    There are optional parameters for formatting:

    • Format - determines what date/time parts are displayed, defaults to 'mediumDate', equivalent to 'MMM d, y'
    • Timezone - the timezone offset for dates. By default uses the end-user's local system timezone
    • DigitsInfo - decimal representation objects. Default to 1.0-3

    To allow customizing the display format by these parameters, the PipeArgs input is exposed. A column will respect only the corresponding properties for its data type, if PipeArgs is set. Example:

    <IgbColumn Field="OrderDate"
               DataType=GridColumnDataType.Date
               PipeArgs=@(new IgbColumnPipeArgs(){ Timezone="UTC+0", DigitsInfo="1.2-2", Format = "longDate" }) />
    
    <IgbColumn Field="UnitPrice"
               DataType=GridColumnDataType.Date
               PipeArgs=@(new IgbColumnPipeArgs(){ Timezone="UTC+0", DigitsInfo="1.2-2", Format = "longDate" }) />
    

    The OrderDate column will respect only the Format and Timezone properties, while the UnitPrice will only respect the DigitsInfo.

    All available column data types could be found in the official Column types topic.

    Complex Data Binding

    The IgbGrid supports binding to complex objects (including nesting deeper than one level) through a "path" of properties in the data record.

    Take a look at the following data model:

    public class AminoAcid
    {
        public string Name { get; set; }
        public AminoAbbreviation Abbreviation { get; set; }
        public AminoWeight Weight { get; set; }
    }
    
    public class AminoAbbreviation
    {
        public string Short { get; set; }
        public string Long { get; set; }
    }
    
    public class AminoWeight
    {
        public double Molecular { get; set; }
        public double Residue { get; set; }
    }
    

    For example, in order to display the weights of a given amino acid in the grid the following snippet will suffice

    <IgbColumn Field="Weight.Molecular" />
    <IgbColumn Field="Weight.Residue" />
    

    An alternative way to bind complex data, or to visualize composite data (from more than one column) in the IgbGrid is to use a custom body template for the column. Generally, one can: - use the value of the cell, that contains the nested data - use the cell object in the template, from which to access the row.data, therefore retrieve any value from it, i.e cell.row.data[field] and cell.row.data[field][nestedField] and interpolate those in the template.

    <IgbColumn Field="Abbreviation.Long" BodyTemplateScript="AbbreviationLongCellTemplate"/>
    
    //In JavaScript:
    igRegisterScript("AbbreviationLongCellTemplate", (ctx) => {
        var html = window.igTemplating.html;
        return html`
            <div>
                <div>
                    ${ctx.cell.value}
                    ${this.GetName(ctx.cell)}
                    ${this.GetWeight(ctx.cell)}
                </div>
            </div>
        `;
    }, false);
    
    function GetName(value) {
    
    }
    
    function GetWeight(value) {
    
    }
    

    Here is an example on how body template is used to display complex data. Below is the data that we are going to use:

    public class EmployeesNestedData : List<EmployeesNestedDataItem>
    {
        public EmployeesNestedData()
        {
            this.Add(new EmployeesNestedDataItem()
            {
                Age = 55,
                Employees = new List<EmployeesNestedDataItem_EmployeesItem>()
                {
                    new EmployeesNestedDataItem_EmployeesItem()
                    {
                        Age = 43,
                        Salary = 70000,
                        Productivity = 80,
                        City = @"Hamburg",
                        Country = @"Germany",
                        Phone = @"609-444-555",
                        HireDate = @"2011, 6, 3",
                        ID = 3,
                        Name = @"Michael Burke",
                        Title = @"Senior Software Developer"
                    },
                    new EmployeesNestedDataItem_EmployeesItem()
                    {
                        Age = 29,
                        Salary = 60000,
                        Productivity = 80,
                        City = @"Munich",
                        Country = @"Germany",
                        Phone = @"609-333-444",
                        HireDate = @"2009, 6, 19",
                        ID = 2,
                        Name = @"Thomas Anderson",
                        Title = @"Senior Software Developer"
                    },
                    new EmployeesNestedDataItem_EmployeesItem()
                    {
                        Age = 31,
                        Salary = 90000,
                        Productivity = 80,
                        City = @"Warasw",
                        Country = @"Poland",
                        Phone = @"609-222-205",
                        HireDate = @"2014, 8, 18",
                        ID = 11,
                        Name = @"Monica Reyes",
                        Title = @"Software Development Team Lead"
                    },
                    new EmployeesNestedDataItem_EmployeesItem()
                    {
                        Age = 35,
                        Salary = 70000,
                        Productivity = 70,
                        City = @"Koln",
                        Country = @"Germany",
                        Phone = @"609-502-525",
                        HireDate = @"2015, 9, 17",
                        ID = 6,
                        Name = @"Roland Mendel",
                        Title = @"Senior Software Developer"
                    }}
                });
            }
        }
    }
    

    The custom template for the column, that will render the nested data:

    <IgbColumn Header="Employees" Field="Employees" BodyTemplateScript="WebGridNestedDataCellTemplate" />
    
    //In JavaScript:
    igRegisterScript("WebGridNestedDataCellTemplate", (ctx) => {
        var html = window.igTemplating.html;
        window.keyUpHandler = function () {
            ctx.cell.row.data[window.event.target.id] = window.event.target.value;
        }
        const people = ctx.cell.value;
        if (people != null) {
            if (people.length === 0) return html``;
            const person = people[0];
            return html`
        <igc-expansion-panel>
            <h3 slot="title">
            ${person.Name}
            </h3>
            <div class="description">
                <div>
                    <label for="title">Title</label>
                    <input id='Title' type="text" name="title" value="${person.Title}" style="text-overflow: ellipsis;" />
                </div>
                <div>
                    <label for="age">Age</label>
                    <input id='Age' type="text" name="title" value="${person.Age}" style="text-overflow: ellipsis;" />
                </div>
            </div>
        </igc-expansion-panel>
            `;
        }
    }, false);
    

    And the result from this configuration is:

    Working with Flat Data Overview

    The flat data binding approach is similar to the one that we already described above, but instead of cell value we are going to use the Data property of the IgbGridRow.

    Since the Blazor grid is a component for rendering, manipulating and preserving data records, having access to every data record gives you the opportunity to customize the approach of handling it. The data property provides you this opportunity.

    Below is the data that we are going to use:

    public class CustomersData : List<CustomersDataItem>
    {
        public CustomersData()
        {
            this.Add(new CustomersDataItem()
            {
                ID = "ALFKI",
                CompanyName = "Alfreds Futterkiste",
                ContactName = "Maria Anders",
                ContactTitle = "Sales Representative",
                Address = "Obere Str. 57",
                City = "Berlin",
                Region = "East",
                PostalCode = "12209",
                Country = "Germany",
                Phone = "030-0074321",
                Fax = "030-0076545"
            });
        }
    }
    

    The custom template:

    <IgbColumn Header="Address" Field="Address"
               Editable="true"
               BodyTemplateScript="AddressCellTemplate" />
    
    //In JavaScript:
    igRegisterScript("AddressCellTemplate", (ctx) => {
        var html = window.igTemplating.html;
        return html`<div class="address-container">
        <div class="country-city">
            <span><strong>Country:</strong> ${ctx.cell.row.data.Country}</span>
            <br>
            <span><strong>City:</strong> ${ctx.cell.row.data.City}</span>
        </div>
        <div class="phone-pscode">
            <span><strong>Postal Code:</strong> ${ctx.cell.row.data.PostalCode}</span>
            <br>
            <span><strong>Phone:</strong> ${ctx.cell.row.data.Phone}</span>
        </div>
        <br />
    </div>`;
    }, false);
    

    Keep in mind that with the above defined template you will not be able to make editing operations, so we need an editor template.

    <IgbColumn Header="Address" Field="Address"
               Editable="true"
               InlineEditorTemplateScript="AddressEditCellTemplate" />
    
    //In JavaScript:
    igRegisterScript("AddressEditCellTemplate", (ctx) => {
        var html = window.igTemplating.html;
        window.keyUpHandler = function () {
            ctx.cell.row.data[window.event.target.id] = window.event.target.value;
        }
    
        return html`<div class="address-container--edit">
        <div>
            <span><strong>Country:</strong></span>
            <input id='Country' onkeyup='keyUpHandler()' value="${ctx.cell.row.data.Country}"></input>
            <br>
            <span><strong>City:</strong></span>
            <input id='City' onkeyup='keyUpHandler()' value="${ctx.cell.row.data.City}"></input>
        </div>
        <div>
            <span><strong>Postal Code:</strong></span>
            <input id='PostalCode' onkeyup='keyUpHandler()' value="${ctx.cell.row.data.PostalCode}"></input>
            <br>
            <span><strong>Selected:</strong></span>
            <input id='Phone' onkeyup='keyUpHandler()' value="${ctx.cell.row.data.Phone}"></input>
        </div>
        <br>
    </div>`;
    }, false);
    

    Working with Flat Data Example

    Using code snippets from previous section will result in the following example of IgbGrid

    Keyboard Navigation

    Keyboard navigation of the IgbGrid provides a rich variety of keyboard interactions for the user. It enhances accessibility and allows intuitive navigation through any type of elements inside (cell, row, column header, toolbar, footer, etc.).

    Styling Blazor Grid

    Note

    The grid uses css grid layout, which is not supported in IE without prefixing, consequently it will not render properly.

    Known Limitations

    Limitation Description
    Column widths set in percentage and px Currently we do not support mixing of column widths with % and px.
    When trying to filter a column of type number If a value different than number is entered into the filtering input, NaN is returned due to an incorrect cast.
    Grid width does not depend on the column widths The width of all columns does not determine the spanning of the grid itself. It is determined by the parent container dimensions or the defined grid's width.
    Grid nested in parent container When grid's width is not set and it is placed in a parent container with defined dimensions, the grid spans to this container.
    Grid OnPush ChangeDetectionStrategy The grid operates with ChangeDetectionStrategy.OnPush so whenever some customization appears make sure that the grid is notified about the changes that happens.
    Columns have a minimum allowed column width. Depending on the displayDensity option, they are as follows:
    "compact": 56px
    "cosy": 64px
    "comfortable ": 80px
    If width less than the minimum allowed is set it will not affect the rendered elements. They will render with the minimum allowed width for the corresponding displayDensity. This may lead to an unexpected behavior with horizontal virtualization and is therefore not supported.
    Row height is not affected by the height of cells that are not currently rendered in view. Because of virtualization a column with a custom template (that changes the cell height) that is not in the view will not affect the row height. The row height will be affected only while the related column is scrolled in the view.

    API References

    Additional Resources

    Our community is active and always welcoming to new ideas.