logoEnterprise-level component library based on Bootstrap and Blazor
gitee
version
license
download
repo
commit
build
coverage

Table

Commonly used in single table maintenance, simple addition, deletion, modification, search, sorting, filtering, search and other common functions can be realized through attribute configuration, and very complex business requirements can be realized through advanced usage of Template

Set ShowSearch to show the query component

Demo

In this example, the right alignment of Count is set, the Complete column is set to center alignment, and the Boolean column is automatically rendered as a Switch component

When the OnAddAsync or OnSaveAsync callback delegate method is set, if the EditTemplate is not set to edit the template, the component will try to automatically generate the form maintenance UI

Demo

Client-side validation can be achieved by setting validation tags such as [Required] of the Foo attribute of the class instance Foo of the generic constraint of TItem

When the data binding type is a nullable type, it is automatically allowed to be null, such as the date binding column is of type DateTime?

When the data binding type is a numerical type, for example, if the quantity binding column is of type int, numerical verification is performed automatically

Some data columns presented in the table are calculated results. This type of column cannot be edited. By setting Editable=false to automatically generate the editing UI, the editing component of this column will not be generated, such as In this example, the Count column does not appear in the edit popup

By setting Readonly=true to automatically generate editing UI, this field will be read-only, please assign the default value to Model when creating a new one

Make the extension buttons appear in front of the row by setting IsExtendButtonsInRowHeader=true

Make the edit popup draggable by setting EditDialogDraggable='true'

When the EditTemplate of the column is set, the component automatically generates the form maintenance UI using this template as the rendered UI

Demo

Use the drop-down box to select the name when custom editing by setting the EditTemplate of the name column
In this example, Name is listed as a custom component TableNameDrop, which defaults to please select ... when creating a new one

By setting the EditMode property of the table, set the component to edit the row data in the pop-up window or edit the data in the row

Demo

EditMode is an enumeration type and its values ​​are: Popup EditForm InCell Its default value is Popup pop-up window to edit row data

In this example, the data source Items is set as two-way binding, which is especially suitable for parent-child table entry. You can directly use the data source when saving.

EditForm Pattern example

InCell Pattern example

When the data operation callback method is not provided, the component automatically finds the registered data service to add, delete, modify and check the data.

Demo

The database operations of adding, deleting, modifying and checking are performed by registering the data service without assigning the following callback delegates. The priority level is that the callback method is called first if there is a callback method.

  • OnAddAsync
  • OnDeleteAsync
  • OnSaveAsync
  • OnQueryAsync
Startup file injects data service Implementation principle and usage introduction
custom data service
After enabling the use of the injected data service, you can set the component individually by setting the <code>DataServices</code> parameter, if the instance provided by the internal use of the injected service is not set

services.AddTableDemoDataService();

By setting the DataService attribute of the table, use an independent data service to add, delete, modify and check data

Demo

custom data service

After enabling the use of the injected data service, you can set the component individually by setting the DataServices parameter, if the instance provided by the internal use of the injected service is not set
In this example, by setting the EditDialogShowMaximizeButton parameter, the Maximize button is displayed in the edit pop-up window

services.AddTableDemoDataService();

Set EditFooterTemplate custom button in EditDialog Footer

Demo

Click the Edit button in the table to pop up the Edit pop-up window. The buttons in the pop-up window Footer are user-defined buttons. In order to keep the functions of the original Close and Save buttons while facilitating the second opening, two corresponding components DialogCloseButton DialogSaveButton These two buttons do not need to write the click related processing methods

<EditFooterTemplate Context="model">
    <Button Text="Popup" Color="Color.Danger" Icon="fa-regular fa-comment-dots" OnClick="() => OnClick(model)"></Button>
    <DialogCloseButton Color="Color.Secondary" Icon="fa-solid fa-xmark" Text="Close" />
    <DialogSaveButton Color="Color.Primary" Icon="fa-solid fa-floppy-disk" Text="Save" />
</EditFooterTemplate>
@page "/tables/edit"

<h3>@Localizer["H1"]</h3>
<h4>@((MarkupString)Localizer["H2"].Value)</h4>

<DemoBlock Title="@Localizer["P1"]" Introduction="@Localizer["P2"]" Name="ShowSearch">
    <p>@((MarkupString)Localizer["P3"].Value)</p>
    <Table TItem="Foo" IsAutoCollapsedToolbarButton="false"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true" IsExtendButtonsInRowHeader="true"
           OnQueryAsync="@OnQueryAsync" OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name" />
            <TableColumn @bind-Field="@context.Address" />
            <TableColumn @bind-Field="@context.Education" />
            <TableColumn @bind-Field="@context.Count" Align="Alignment.Right" />
            <TableColumn @bind-Field="@context.Complete" Align="Alignment.Center" />
        </TableColumns>
        <EditTemplate>
            <div class="row g-3 form-inline">
                <div class="col-12 col-sm-6">
                    <BootstrapInput @bind-Value="@context.Name" placeholder="@PlaceHolderString" maxlength="50" />
                </div>
                <div class="col-12 col-sm-6">
                    <BootstrapInput @bind-Value="@context.Address" placeholder="@PlaceHolderString" maxlength="50" />
                </div>
                <div class="col-12 col-sm-6">
                    <DateTimePicker @bind-Value="@context.DateTime" />
                </div>
                <div class="col-12 col-sm-6">
                    <Select @bind-Value="@context.Education" />
                </div>
                <div class="col-12 col-sm-6">
                    <BootstrapInputNumber @bind-Value="@context.Count" ShowButton="true" />
                </div>
                <div class="col-12 col-sm-6">
                    <Switch @bind-Value="@context.Complete" ShowInnerText="true" />
                </div>
            </div>
        </EditTemplate>
    </Table>
</DemoBlock>

<DemoBlock Title="@Localizer["P5"]" Introduction="@Localizer["P6"]" Name="OnAddAsync">
    <p id="anchor2">@((MarkupString)Localizer["P7"].Value)</p>
    <p>@((MarkupString)Localizer["P8"].Value)</p>
    <p>@((MarkupString)Localizer["P9"].Value)</p>
    <p>@((MarkupString)Localizer["P10"].Value)</p>
    <p>@((MarkupString)Localizer["P11"].Value)</p>
    <p>@((MarkupString)Localizer["P12"].Value)</p>
    <p>@((MarkupString)Localizer["P13"].Value)</p>
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true" EditDialogIsDraggable="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true" IsExtendButtonsInRowHeader="true"
           OnQueryAsync="@OnQueryAsync"
           OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name" />
            <TableColumn @bind-Field="@context.Address" Readonly="true" />
            <TableColumn @bind-Field="@context.Education" />
            <TableColumn @bind-Field="@context.Count" Editable="false" />
            <TableColumn @bind-Field="@context.Complete" />
        </TableColumns>
    </Table>
</DemoBlock>

<DemoBlock Title="@Localizer["P14"]" Introduction="@Localizer["P15"]" Name="EditTemplate">
    <p>
        <div>@((MarkupString)Localizer["P16"].Value)</div>
        <div>@((MarkupString)Localizer["P17"].Value)</div>
    </p>
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true" IsExtendButtonsInRowHeader="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true"
           OnQueryAsync="@OnQueryAsync"
           OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name">
                <EditTemplate Context="value">
                    <div class="col-12 col-sm-6">
                        <TablesNameDrop Model="value" />
                    </div>
                </EditTemplate>
            </TableColumn>
            <TableColumn @bind-Field="@context.Address" Rows="3" />
            <TableColumn @bind-Field="@context.Count" />
            <TableColumn @bind-Field="@context.Complete" />
        </TableColumns>
    </Table>
</DemoBlock>

<DemoBlock Title="@Localizer["P18"]" Introduction="@Localizer["P19"]" Name="EditMode">
    <p id="anchor4">@((MarkupString)Localizer["P20"].Value)</p>
    <p>@((MarkupString)Localizer["P21"].Value)</p>
    <p>@((MarkupString)Localizer["P22"].Value)</p>
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true"
           AddModalTitle="@Localizer["P23"]" EditModalTitle="@Localizer["P24"]"
           OnQueryAsync="@OnQueryAsync" EditMode="EditMode.EditForm"
           OnAddAsync="@OnAddAsync" OnSaveAsync="@OnSaveAsync" OnDeleteAsync="@OnDeleteAsync">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name" />
            <TableColumn @bind-Field="@context.Address" />
            <TableColumn @bind-Field="@context.Education" />
            <TableColumn @bind-Field="@context.Count" />
            <TableColumn @bind-Field="@context.Complete" />
        </TableColumns>
    </Table>

    <p id="anchor5" class="mt-3">@((MarkupString)Localizer["P25"].Value)</p>
    <RadioList @bind-Value="InsertMode" class="mb-3" />
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true"
           AddModalTitle="@Localizer["P23"]" EditModalTitle="@Localizer["P24"]"
           EditMode="EditMode.InCell" @bind-Items="@BindItems" InsertRowMode="InsertMode">
        <TableColumns>
            <TableColumn @bind-Field="@context.DateTime" Width="180" />
            <TableColumn @bind-Field="@context.Name" />
            <TableColumn @bind-Field="@context.Address" />
            <TableColumn @bind-Field="@context.Education" />
            <TableColumn @bind-Field="@context.Count" />
            <TableColumn @bind-Field="@context.Complete" />
        </TableColumns>
    </Table>
</DemoBlock>

<DemoBlock Title="@Localizer["P26"]" Introduction="@Localizer["P27"]" Name="InjectDataService">
    <p id="anchor6">
        @((MarkupString)Localizer["P28"].Value)
        <ul class="ul-demo mb-3">
            <li><code>OnAddAsync</code></li>
            <li><code>OnDeleteAsync</code></li>
            <li><code>OnSaveAsync</code></li>
            <li><code>OnQueryAsync</code></li>
        </ul>
        <div class="mb-3">@Localizer["P29"] <a href="@DataServiceUrl" target="_blank">@Localizer["P30"]</a></div>
        <b>@Localizer["P31"]</b>
        <div class="mt-1">@Localizer["P32"]</div>
    </p>
    <Pre class="no-highlight mt-3">services.AddTableDemoDataService();</Pre>
    <Table TItem="Foo"
           IsPagination="true" PageItemsSource="@PageItemsSource"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true" AutoGenerateColumns="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true">
        <TableColumns>
            <TableColumn @bind-Field="@context.Hobby" Items="@Hobbys" />
        </TableColumns>
    </Table>
</DemoBlock>

<DemoBlock Title="@Localizer["P33"]" Introduction="@Localizer["P34"]" Name="DataService">
    <p id="anchor7">
        <b>@Localizer["P35"]</b>
        <div class="mt-1">@((MarkupString)Localizer["P36"].Value)</div>
        <div class="mt-1">@((MarkupString)Localizer["P37"].Value)</div>
    </p>
    <Pre class="no-highlight mt-3">services.AddTableDemoDataService();</Pre>
    <Table TItem="Foo" EditDialogShowMaximizeButton="true"
           IsPagination="true" PageItemsSource="@PageItemsSource" DataService="@CustomerDataService"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true" AutoGenerateColumns="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true">
        <TableColumns>
            <TableColumn @bind-Field="@context.Hobby" Items="@Hobbys" />
        </TableColumns>
    </Table>
</DemoBlock>

<DemoBlock Title="@Localizer["FooterTemplateTitle"]" Introduction="@Localizer["FooterTemplateIntro"]" Name="EditFooterTemplate">
    <p>@((MarkupString)Localizer["FooterTemplateDesc"].Value)</p>
    <Pre><EditFooterTemplate Context="model">
    <Button Text="Popup" Color="Color.Danger" Icon="fa-regular fa-comment-dots" OnClick="() => OnClick(model)"></Button>
    <DialogCloseButton Color="Color.Secondary" Icon="fa-solid fa-xmark" Text="Close" />
    <DialogSaveButton Color="Color.Primary" Icon="fa-solid fa-floppy-disk" Text="Save" />
</EditFooterTemplate></Pre>
    <Table TItem="Foo" EditDialogShowMaximizeButton="true"
           IsPagination="true" PageItemsSource="@PageItemsSource" DataService="@CustomerDataService"
           IsStriped="true" IsBordered="true" IsMultipleSelect="true" AutoGenerateColumns="true"
           ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true">
        <TableColumns>
            <TableColumn @bind-Field="@context.Hobby" Items="@Hobbys" />
        </TableColumns>
        <EditFooterTemplate Context="model">
            <Button Text="Popup" Color="Color.Danger" Icon="fa-regular fa-comment-dots" OnClick="() => OnClick(model)"></Button>
            <DialogCloseButton Color="Color.Secondary" Icon="fa-solid fa-xmark" Text="Close" />
            <DialogSaveButton Color="Color.Primary" Icon="fa-solid fa-floppy-disk" Text="Save" />
        </EditFooterTemplate>
    </Table>
</DemoBlock>
// Copyright (c) Argo Zhang (argo@163.com). All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Website: https://www.blazor.zone or https://argozhang.github.io/

using BootstrapBlazor.Shared.Services;
using Microsoft.Extensions.Options;

namespace BootstrapBlazor.Shared.Samples.Table;

/// <summary>
/// 表单编辑功能示例
/// </summary>
public partial class TablesEdit
{
    [Inject]
    [NotNull]
    private IStringLocalizer<Foo>? LocalizerFoo { get; set; }

    [Inject]
    [NotNull]
    private IStringLocalizer<TablesEdit>? Localizer { get; set; }

    [Inject]
    [NotNull]
    private IOptionsMonitor<WebsiteOptions>? WebsiteOption { get; set; }

    [Inject]
    [NotNull]
    private ToastService? ToastService { get; set; }

    private static IEnumerable<int> PageItemsSource => new int[] { 4, 10, 20 };

    [NotNull]
    private IEnumerable<SelectedItem>? Hobbys { get; set; }

    [NotNull]
    private List<Foo>? Items { get; set; }

    [NotNull]
    private IEnumerable<Foo>? BindItems { get; set; }

    private InsertRowMode InsertMode { get; set; } = InsertRowMode.Last;

    private string DataServiceUrl => $"{WebsiteOption.CurrentValue.BootstrapBlazorLink}/wikis/Table%20%E7%BB%84%E4%BB%B6%E6%95%B0%E6%8D%AE%E6%9C%8D%E5%8A%A1%E4%BB%8B%E7%BB%8D?sort_id=3207977";

    private string? PlaceHolderString { get; set; }

    /// <summary>
    /// OnInitialized 方法
    /// </summary>
    protected override void OnInitialized()
    {
        base.OnInitialized();

        Hobbys = Foo.GenerateHobbys(LocalizerFoo);
        Items = Foo.GenerateFoo(LocalizerFoo);

        BindItems = Foo.GenerateFoo(LocalizerFoo).Take(5).ToList();

        CustomerDataService = new FooDataService<Foo>(LocalizerFoo);

        PlaceHolderString ??= Localizer["P4"];
    }

    private static Task<Foo> OnAddAsync() => Task.FromResult(new Foo() { DateTime = DateTime.Now, Address = $"Custom address  {DateTime.Now.Second}" });

    private Task<bool> OnSaveAsync(Foo item, ItemChangedType changedType)
    {
        if (changedType == ItemChangedType.Add)
        {
            item.Id = Items.Max(i => i.Id) + 1;
            Items.Add(item);
        }
        else
        {
            var oldItem = Items.FirstOrDefault(i => i.Id == item.Id);
            if (oldItem != null)
            {
                oldItem.Name = item.Name;
                oldItem.Address = item.Address;
                oldItem.DateTime = item.DateTime;
                oldItem.Count = item.Count;
                oldItem.Complete = item.Complete;
                oldItem.Education = item.Education;
            }
        }
        return Task.FromResult(true);
    }

    private Task<bool> OnDeleteAsync(IEnumerable<Foo> items)
    {
        items.ToList().ForEach(i => Items.Remove(i));
        return Task.FromResult(true);
    }

    private Task<QueryData<Foo>> OnQueryAsync(QueryPageOptions options)
    {
        IEnumerable<Foo> items = Items;

        // 过滤
        var isFiltered = false;
        if (options.Filters.Any())
        {
            items = items.Where(options.Filters.GetFilterFunc<Foo>());
            isFiltered = true;
        }

        // 排序
        var isSorted = false;
        if (!string.IsNullOrEmpty(options.SortName))
        {
            var invoker = Foo.GetNameSortFunc();
            items = invoker(items, options.SortName, options.SortOrder);
            isSorted = true;
        }

        // 设置记录总数
        var total = items.Count();

        // 内存分页
        items = items.Skip((options.PageIndex - 1) * options.PageItems).Take(options.PageItems).ToList();

        return Task.FromResult(new QueryData<Foo>()
        {
            Items = items,
            TotalCount = total,
            IsSorted = isSorted,
            IsFiltered = isFiltered,
            IsSearch = true
        });
    }

    [NotNull]
    private IDataService<Foo>? CustomerDataService { get; set; }

    private class FooDataService<TModel> : TableDemoDataService<TModel> where TModel : class, new()
    {
        public FooDataService(IStringLocalizer<TModel> localizer) : base(localizer)
        {

        }
    }

    private Task OnClick(Foo foo) => ToastService.Information("Custom button fucntion", $"{foo.Address}");
}

B station related video link

交流群

QQ Group:BootstrapAdmin & Blazor 795206915 675147445 Welcome to join the group discussion
img
Themes
Bootstrap
Motronic
Ant Design (完善中)
DevUI (制作中)
LayUI (完善中)
An error has occurred. This application may no longer respond until reloaded. Reload
Seems like the connection with the server has been lost. It can be due to poor or broken network. Please hang on while we're trying to reconnect...
Oh snap! Failed to reconnect with the server. This is typically caused by a longer network outage, or if the server has been taken down. You can try to reconnect, but if that does not work, you need to reload the page.
Oh man! The server rejected the attempt to reconnect. The only option now is to reload the page, but be prepared that it won't work, since this is typically caused by a failure on the server.
Bootstrap Blazor Component library updated to 7.0.8-beta05

Bootstrap Blazor at present has more than 120 components. This component is based on Bootstrap Blazor An enterprise-level component library that provides several types of common components such as layout, navigation, form, data, notification, icon, voice, etc. Each component has been carefully designed with modularity, responsiveness and excellent performance. Starting from more practical scenarios, meeting the needs of various scenarios, greatly reducing the time cost of developers, greatly shortening the development cycle, greatly improving development efficiency, and providing a set of General Rights Management System Example project。Bootstrap Blazor Products are maintained by a professional full-time technical team, with efficient response speed, diversified solutions, long-term support, and enterprise-level support. At present, it has been used in many well-known state-owned enterprises, and the project is running stably with a maximum of 1200 people online. On the right is the QR code of the Chinese Blazor QQ community with the largest number of people in China, welcome to scan and join the group.

component updated to 6.6.0 Change log [portal] If the component brings you convenience, please help to light up the project Star github gitee

QQGroup
QQ 795206915