title: How to implement DataGrid component in server-side Blazor?
body: <p>I want to create a DataGrid component in Blazor that can be used like the following in a Razor component page. Someone asked the same question on the Blazor GitHub issue tracker at <a href="https://github.com/dotnet/aspnetcore/issues/14886" rel="nofollow noreferrer">https://github.com/dotnet/aspnetcore/issues/14886</a>. I tried using the code that is listed there, but, found that the columns aren't added to the DataTable until after the code at the top runs. As a result, none of the columns are rendered. I'm thinking that the person who posted the example code may have been using client-side Blazor (WebAssembly). That's the only reason I can think of that it would work for them, but, not me.</p>
  
  <p>My question is, what is the recommended approach to creating complex UI components in Blazor? I think part of the problem here is that the DataTableColumn is a component. However, it is not intended to actually be rendered. It is just being used as configuration/parameter data for DataTable which does the actual rendering. So, I am wondering if this is kind of an abuse of the design of Blazor. Telerik and other companies do this though. <a href="https://demos.telerik.com/blazor-ui/grid/overview" rel="nofollow noreferrer">https://demos.telerik.com/blazor-ui/grid/overview</a></p>
  
  <p>Index.razor</p>
  
  <pre><code>@page "/users"
  
  @using FolioLibrary
  
  &lt;fieldset&gt;
      &lt;legend&gt;Users&lt;/legend&gt;
  
      &lt;DataTable Class="table" Data="@(folioContext.User2s.Take(10).Cast&lt;dynamic&gt;().ToList())"&gt;
          &lt;DataTableColumn Name="Id" Field="Id" Type="typeof(Guid)"&gt;&lt;/DataTableColumn&gt;
          &lt;DataTableColumn Name="Name" Field="Name" Type="typeof(string)"&gt;&lt;/DataTableColumn&gt;
          &lt;DataTableColumn&gt;
              &lt;Template Context="item"&gt;
                  &lt;a href="/@item.Id" class="btn"&gt;text&lt;/a&gt;
              &lt;/Template&gt;
          &lt;/DataTableColumn&gt;
      &lt;/DataTable&gt;
  
  @code
  {
      private FolioContext folioContext = new FolioContext();
  }
  </code></pre>
  
  <p>DataTable.razor</p>
  
  <pre><code>&lt;div class="col-12"&gt;
      &lt;CascadingValue Value="this"&gt;
          &lt;table class="@Class" id="@Id"&gt;
              &lt;thead&gt;
                  &lt;tr&gt;
                      @ChildContent
                  &lt;/tr&gt;
              &lt;/thead&gt;
  
              &lt;tbody&gt;
                  @*@foreach (dynamic item in FilteredData.Skip((Page - 1) * PerPage).Take(PerPage))*@
                  @foreach (dynamic item in Data)
                  {
                      &lt;tr&gt;
                          @foreach (DataTableColumn column in Columns)
                          {
                              &lt;td class="align-middle" width="@(column.Type == null ? "1%" : "")"&gt;
                                  @if (column.Template is null)
                                  {
                                      @item.GetType().GetProperty(column.Field).GetValue(item, null)
                                  }
                                  else
                                  {
                                      @column.Template(item)
                                  }
                              &lt;/td&gt;
                          }
                      &lt;/tr&gt;
                  }
              &lt;/tbody&gt;
          &lt;/table&gt;
      &lt;/CascadingValue&gt;
  &lt;/div&gt;
  
  @code
  {
      [Parameter]
      public string Class { get; set; }
  
      [Parameter]
      public string Id { get; set; }
  
      [Parameter]
      public RenderFragment ChildContent { get; set; }
  
      [Parameter]
      public List&lt;dynamic&gt; Data
      {
          get =&gt; data;
          set
          {
              data = value;
              FilteredData = data;
          }
      }
  
      [Parameter]
      public int PerPage { get; set; } = 25;
  
      [Parameter]
      public int Page { get; set; } = 1;
  
      [Parameter]
      public RenderFragment&lt;dynamic&gt; Actions { get; set; }
  
      List&lt;DataTableColumn&gt; Columns { get; set; } = new List&lt;DataTableColumn&gt;();
  
      List&lt;dynamic&gt; FilteredData { get; set; }
  
      List&lt;dynamic&gt; data;
  
      public void AddColumn(DataTableColumn column)
      {
          Columns.Add(column);
      }
  
      public void RemoveColumn(DataTableColumn column)
      {
          Columns.Remove(column);
      }
  }
  
  </code></pre>
  
  <p>DataTableColumn.razor</p>
  
  <pre><code>@implements IDisposable
  
  &lt;th&gt;
      &lt;span&gt;@Name&lt;/span&gt;
  &lt;/th&gt;
  
  @code
  {
      [Parameter]
      public string Name { get; set; }
  
      [Parameter]
      public string Field { get; set; }
  
      [Parameter]
      public Type Type { get; set; }
  
      [Parameter]
      public RenderFragment&lt;dynamic&gt; Template { get; set; }
  
      [CascadingParameter]
      DataTable DataTable { get; set; }
  
      protected override void OnInitialized()
      {
          DataTable.AddColumn(this);
      }
  
      public void Dispose()
      {
          DataTable.RemoveColumn(this);
      }
  
  }
  
  </code></pre>
  
  <p>I temporarily added <code>OnInitialized()</code> and <code>OnAfterRender()</code> methods to see what order the components are being created in and when the events are raised and the following was the order.</p>
  
  <pre><code>DataTable.OnInitialized
  DataTableColumn.OnInitialized
  DataTable.OnInitialized
  DataTableColumn.OnInitialized
  DataTable.OnAfterRender
  DataTableColumn.OnAfterRender
  </code></pre>
  
  <p>I was thinking maybe I could use some other event/method to make it render later, but, the rendering code/foreach loops are in the main body of the Razor component, not in a call back method.</p>
  
  <p>Before trying this, I had a simpler grid component with a HeaderTemplate and an ItemTemplate. However, I need something more complex, because I want to build in sort functionality where you can click on the column heading and have the grid component do the sort for you.</p>