Edit-in line server grid with query

Query may be applied also to grids in edit mode. In this case all items edit buttons disappears automatically when the grid is in edit mode. Toolbars buttons that adds new items must not be rendered when the grid is in grouping mode.
This result, can be achieved with the verify-permission TagHelper that shows its content only if the permissions listed in its required-permissions are granted. Since when the grid is in grouping mode all edit permissions are automatically denied to all users, all add buttons will not be shown in grouping mode. The required-permissions TagHelper is usefull also to show/hide toolbars buttons according to user permissions. Buttons on grid items are automatically shown/hidden according to user permissions.

THe required-permissions TagHelper may be used also outside of a grid. However, in this case it can't inherit what permissions are granted from the grid, so in this case it must be passed in its permissions attribute the same function we pass the the grid operations attribute. Moreover, if its content mus be hidden when a grid is in grouping mode, it must be passed the property containing the QueryDescription object in its query-for attribute.

In this case we have not provided a specific ViewModel for grouping, so the grid uses the item ViewModel also when in grouping mode. As a consequence sounting on several properties has been automatically disabled. The controller code siplifies, since there is no need of calling a different overload of the repository GetPage method when the grid is in grouping mode.

This examples, explain just the peculiarities of quesries for editable grids. Please, refer to the Grid with query windows example for more information on how to set-up queries.

16G Tablet 400.00 $
1T High speed 120.00
1T SSD 200.00
3D Glasses 400.00 $
color laser printer 400.00 $
@model LiveExamples.Viemodels.SimpleProductlistViewModel
    ViewData["Title"] = "Edit-in line server grid with query";
    if (Model.Query.AttachedTo == null)
@*antiforgery is compulsory *@
<form asp-antiforgery="true">
    <div asp-validation-summary="All" class="text-danger"></div>
    <grid asp-for="Products.Data"
          operations="user => Functionalities.FullInLine"
          class="table table-condensed table-bordered">
        <toolbar zone-name="@LayoutStandardPlaces.Header">
            <pager class="pagination pagination-sm"
                   total-pages="Products.TotalPages" />
            <query type="Filtering" />
            <query type="Sorting" />
            <query type="Grouping" />
            <verify-permission required-permissions="@(Functionalities.Append)">
                <button type="button" data-operation="add append 0"
                        class="btn btn-sm btn-primary">
                    new product
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
    <script src="~/lib/mvcct-controls/mvcct.controls.min.js"></script>
    <script src="~/lib/mvcct-controls/modules/mvcct.controls.ajax.min.js"></script>
    <script src="~/lib/mvcct-controls/modules/mvcct.controls.serverGrid.min.js"></script>
    <script src="~/lib/mvcct-odata/dest/global/mvcct.odata.min.js"></script>
    <script src="~/lib/mvcct-controls/modules/mvcct.controls.query.min.js"></script>
public class SimpleProductViewModel 
    public int? Id { getset; }
    [ColumnLayout(DetailWidthsAsString = "100 50")]
    [Display(Name = "Name", Order = 400)]
    public string Name { getset; }
    [ColumnLayout(DetailWidthsAsString = "60 30")]
    [Display(Name = "Price", Order = 300)]
    public decimal Price { getset; }
    [Display(Name = "Cur", Order = 280)]
    [ColumnLayout(WidthsAsString = "10", DetailWidthsAsString = "40 20")]
    public Currency ChosenCurrency { getset; }
    [ColumnLayout(DetailWidthsAsString = "20")]
    [Display(Name = "Av", Order = 230)]
    public bool Available { getset; }
    public override string ToString()
        return Name;
public async Task<IActionResult> AjaxServerQuery()
    var query = queryProvider.Parse<SimpleProductViewModel>();
    int pg = (int)query.Page;
    if (pg < 1) pg = 1;
    var model = new SimpleProductlistViewModel
        Products = await Repository.GetPage(
            query.GetSorting() ??
            (   q => q.OrderBy(m => m.Name)),
            pg, 5, query.GetGrouping()),
    return View(model);
tfoot .paginationthead .pagination
    displayinline !important;
.full-cell {
table .input-validation-error {
    border1px solid red;

Fork me on GitHub