Grid with in-line filter

This examples, differs from, the Grid with query windows example only in the filter form being in-line in the web page instead of opening with a query button. Please, refer to that example for more explanations on how to set-up queries.

This example explains just how to put in-line query windows. Basically, instead of using query TagHelpers in a grid toolbar, we add a query-inline TagHelper in the pace where we would like the query form to appear.

Since in this case information can't be inherited from the grid (since it is not an ancestor TagHelper), we must pass the property containing the QueryDescriprion object in the asp-for TagHelper attribute, and the collection where to apply the query to its collection-for attribute. Query window specific toolbars may be placed inside the TagHelper itself, while the RowType definition must be imported by the grid.

Grid may export all its row definitions, by assigning them a name with the rows-cache-key grid attribute. While, the query-inline TagHelper may import them by accepting the same name in its row-collection-name attribute.

 
name package type unit price discontinued supplier
Alice Mutton 20 - 1 kg tins 39.00 Pavlova, Ltd.
Aniseed Syrup 12 - 550 ml bottles 10.00 Exotic Liquids
Boston Crab Meat 24 - 4 oz tins 18.40 New England Seafood Cannery
Camembert Pierrot 15 - 300 g rounds 34.00 Gai pâturage
Carnarvon Tigers 16 kg pkg. 62.50 Pavlova, Ltd.
@model LiveExamples.Viemodels.FoodListViewModel
@{
    ViewData["Title"] = "Grid with in-line filter";
    if (Model.Query.AttachedTo == null)
    {
        Model.Query.AttachEndpoint(Url.Action("IndexInLine""Food"));
    }
}
...
...
...
 
<grid asp-for="Products.Data"
      type="Immediate"
      all-properties="true"
      mvc-controller="typeof(LiveExamples.Controllers.GridsController)"
      row-id="readonly-query"
      operations="user => Functionalities.ReadOnly | Functionalities.GroupDetail"
      query-for="Query"
      sorting-clauses="2"
      enable-query="true"
      rows-cache-key="export-for-query"
      query-grouping-type="typeof(FoodViewModelGrouping)"
      class="table table-condensed table-bordered">
    <column asp-for="Products.Data.Element().SupplierId">
        <external-key-remote display-property="Products.Data
                                 .Element().SupplierCompanyName"
                             items-value-property="Value"
                             items-display-property="Display"
                             items-url="@(Url.Action("GetSuppliers",
                           "Food"new { search = "_zzz_" }))"
                             dataset-name="suppliers"
                             url-token="_zzz_"
                             max-results="20" />
    </column>
    <row-type asp-for="Products.Data.SubInfo<FoodViewModelGrouping>().Model"
              from-row="0">
        <column asp-for="Products.Data
                .SubElement<FoodViewModelGrouping>().SupplierIdCount" />
            <column asp-for="Products.Data
                    .SubElement<FoodViewModelGrouping>().PackageCount" />
    </row-type>
    <toolbar zone-name="@LayoutStandardPlaces.Header">
        <pager class="pagination pagination-sm"
               max-pages="4"
               page-size-default="5"
               total-pages="Products.TotalPages" />
        &nbsp;
        <query type="Sorting" />
        <query type="Grouping" />
    </toolbar>
</grid>
 
<query-inline type="Filtering" asp-for="Query"
              collection-for="Products.Data"
              row-collection-name="export-for-query" />
...
...
...
@section Scripts {
<link href="~/lib/awesomplete/awesomplete.css" rel="stylesheet" />
<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/awesomplete/awesomplete.min.js"></script>
<script src="~/lib/mvcct-controls/modules/mvcct.controls.autocomplete.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 FoodViewModel
{
    public int? Id { getset; }
 
    [QueryStringLength(64, MinimumLength = 2) Required, 
        Display(Name ="name")]
    public string ProductName { getset; }
 
    [QueryStringLength(32, MinimumLength = 2), Required,
        Display(Name = "package type")]
    public string Package { getset; }
    [Range(0, 1000), Query,
        Display(Name = "unit price")]
    public decimal UnitPrice { getset; }
    [Display(Name = "discontinued")]
    public bool IsDiscontinued { getset; }
    [Query,
        Display(Name = "supplier")]
    public int SupplierId { getset; }
    [Query,
        Display(Name = "supplier")]
    public string SupplierCompanyName { getset; }
 
}
[RunTimeType]
public class FoodViewModelGroupingFoodViewModel
{
    public int SupplierIdCount { getset; }
    public int PackageCount { getset; }
 
 
}
public class FoodControllerServerCrudController<FoodViewModelFoodViewModelint?>
{
    public FoodController(FoodRepository repository, 
            IStringLocalizerFactory factory, IHttpContextAccessor accessor, 
            IWebQueryProvider queryProvider) :
        base(factory, accessor)
    {
            
        Repository = repository;
        this.queryProvider = queryProvider;
    }
 
    public IWebQueryProvider queryProvider { getprivate set; }
 
    public async Task<IActionResult> IndexInLine()
    {
        var query = queryProvider.Parse<FoodViewModel>();
 
        int pg = (int)query.Page;
        var grouping = query.GetGrouping<FoodViewModelGrouping>();
 
        var model = new FoodListViewModel
        {
            Query = query,
            Products =
                grouping == null ?
                    await Repository.GetPage(
                        query.GetFilterExpression(),
                        query.GetSorting() ??
                            (q => q.OrderBy(m => m.ProductName)),
                        pg, 5)
                    :
                    await Repository.GetPageExtended(
                        query.GetFilterExpression(),
                        query.GetSorting<FoodViewModelGrouping>() ??
                            (q => q.OrderBy(m => m.ProductName)),
                        pg, 5,
                        query.GetGrouping<FoodViewModelGrouping>())
        };
 
        return View(model);
    }
 
    [HttpGet]
    public async Task<ActionResult> GetSuppliers(string search)
    {
        var res = search == null || search.Length < 3 ?
            new List<AutoCompleteItem>() :
            await (Repository as FoodRepository).GetSuppliers(search, 10);
        return Json(res);
    }
}
tfoot .paginationthead .pagination 
    {
        margin0;
        displayinline !important;
    }
public class FoodRepository : DefaultCRUDRepository<ApplicationDbContextFood>
{
    private ApplicationDbContext db;
    public  FoodRepository(ApplicationDbContext db):
        base(db, db.Foods)
    {
        this.db = db;
    }
    public async Task<IEnumerable<AutoCompleteItem>> 
        GetSuppliers(string search, int maxpages)
    {
        return (await DefaultCRUDRepository.Create(db, db.Suppliers)
                .GetPage<AutoCompleteItem>(m => m.Display.StartsWith(search),
                m => m.OrderBy(n => n.Display), 1, maxpages))
                .Data;
    }
    static FoodRepository()
    {
        DefaultCRUDRepository<ApplicationDbContextSupplier>
            .DeclareProjection(m => new AutoCompleteItem
            {
                Display = m.CompanyName,
                Value = m.Id
            });
    }

Fork me on GitHub