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.
Important: All information contained in the external-key-remote
tag helper
on how to render external keys,
starting from version 2.1.0 may be specified also with data annotations,
thus simplifying the markup and increasing reusability of the code
name | package type | unit price | discontinued | supplier |
---|---|---|---|---|
Geitost | 500 g | 2.50 | Norske Meierier | |
Genen Shouyu | 24 - 250 ml bottles | 15.50 | Mayumi's | |
Gnocchi di nonna Alice | 24 - 250 g pkgs. | 38.00 | Pasta Buttini s.r.l. | |
Gorgonzola Telino | 12 - 100 g pkgs | 12.50 | Formaggi Fortini s.r.l. | |
Grandma's Boysenberry Spread | 12 - 8 oz jars | 25.00 | Grandma Kelly's Homestead |
@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" /> <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 { get; set; } [Query, StringLength(64, MinimumLength = 2) Required, Display(Name ="name")] public string ProductName { get; set; } [Query, StringLength(32, MinimumLength = 2), Required, Display(Name = "package type")] public string Package { get; set; } [Range(0, 1000), Query, Display(Name = "unit price")] public decimal UnitPrice { get; set; } [Display(Name = "discontinued")] public bool IsDiscontinued { get; set; } [Query, Display(Name = "supplier")] public int SupplierId { get; set; } [Query, Display(Name = "supplier")] public string SupplierCompanyName { get; set; } } [RunTimeType] public class FoodViewModelGrouping: FoodViewModel { public int SupplierIdCount { get; set; } public int PackageCount { get; set; } }
public class FoodController: ServerCrudController<FoodViewModel, FoodViewModel, int?> { public FoodController(FoodRepository repository, IStringLocalizerFactory factory, IHttpContextAccessor accessor, IWebQueryProvider queryProvider) : base(factory, accessor) { Repository = repository; this.queryProvider = queryProvider; } public IWebQueryProvider queryProvider { get; private 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 .pagination, thead .pagination { margin: 0; display: inline !important; }
public class FoodRepository : DefaultCRUDRepository<ApplicationDbContext, Food> { 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<ApplicationDbContext, Supplier> .DeclareProjection(m => new AutoCompleteItem { Display = m.CompanyName, Value = m.Id }); }