Opgave: Validation, Search and Filter Items


Dette er den tredie opgave i opgave-serien ItemRazor.
I forrige opgave blev det muligt at oprette nye Items via siden CreateItem. I denne opgave vil vi introducere validering af input-data, søgning og filtrering af Items.
Udgangspunktet er løsningen fra opgave 2 (ItemRazorV2.zip):





Step 1 (Validering - input tag-helper: asp-for - Items.cs)
I sidste opgave benyttede vi input tag-helper: asp-for. asp-for kan hjælpe med at validere, at typen af input-data matcher property'ens type i model-klassen (idet asp-for bl.a. sætter HTML "type" attributtet til den tilsvarende .NET type). Ved at benytte data annotations i model-klassen kan vi angive mere specifikke input typer der kan mappes til (fx [EmailAddress] vil blive mappet til type="email" og [DataType(DataType.Date)] mappes til type="date").

Tilføj følgende data annotations i model klassen Item:


Bemærk: Typen af Id er ændret til int? og Price til double? - det betyder at de også kan være null (nullable). Dette er nødvendigt hvis vi vil udskrive egne ErrorMessage, da vi ellers vil få en system errormessage: "The value is invalid" (pga id ikke må være null)

Overvej:




Step 2 (CreateItem.cshtml )
I pagen CreateItem tilføjes <span>-tags med asp-validation-for helper tag samt class="text-danger" (Bootstrap - gør teksten rød).



Bemærk: asp-for behøver ikke at blive foranstillet @Model - det er implicit (derfor er de fjernet her - var med i sidste opgave).



Step 3 (Afprøv )
Afprøv om valideringen virker - du skulle gerne få noget ala:







Step 4 (IItemService, ItemService)
Næste step er at tilføje en søge funktion. Først tilføjes en metode-signatur for funktionen: NameSearch til IItemService. Funktionen skal have en søge-streng som parameter og returtypen skal være en liste af Items (vi vælger at benytte det mere generelle Interface IEnummerable, i stedet for den specifikke Class List - List implementere IEnummerable) :

IEnumerable<Item> NameSearch(string str);

Dernæst implementeres funktionen NameSearch i Class ItemService. Funktionen skal tage en søge-streng som argument og returnere en liste med alle de Items der har et Name der indeholder søge-strengen:

public IEnumerable<Item> NameSearch(string str)
{
List<Item> nameSearch = new List<Item>();
foreach (Item item in _items)
{
if (item.Name.ToLower().Contains(str.ToLower()))
{
nameSearch.Add(item);
}
}

return nameSearch;
}

Overvej:

 

 

Step 5 (GetAllItems.cshtml.cs)
Ideen er, at det skal være muligt at indtaste en søge-streng på siden GetAllItems og få vist alle de Items der indeholder denne søge-streng i sidens tabel.

Add en property SearchString til Class GetAllItemsModel og tilføj en annotation [BindProperty], så den kan bindes til et input-field på siden:

[BindProperty] public string SearchString { get; set; }

Siden GetAllItems kommer til at indeholde en Search-Button og når der klikkes på knappen, skal service funktionen NameSearch kaldes med SearchString som argument. Listen Items skal assignes til resultatet så det kan blive vist i tabellen. Spørgsmålet er hvordan kobles det sammen?

Vi vælger at benytte en OnPost-metode og kalder den OnPostNameSearch( ). Metoden skal kalde NameSearch på servicen og opdatere Items:

public IActionResult OnPostNameSearch()
{
Items = _itemService.NameSearch(SearchString).ToList();
return Page();
}

Overvej:


 

Step 6 (GetAllItems.cshtml)
Nu mangler vi blot at tilføje et <form>-tag til siden. <form> tagget skal indeholde et <input>-tag med et asp-for der binder til SearchString i model-klassen samt et <input>-tag af typen "submit" med et asp-page-handler der binder til metoden NameSearch i modellen.

<form method="post" class="form-inline">
<input asp-for="SearchString" class="form-control mr-1" placeholder="Enter search term"/>
<input type="submit" asp-page-handler="NameSearch" value="Search" class="btn btn-primary"/>
</form>

Vigtigt! Bemærk: Da <form>-tagget har method="post", skal navnet på handler metoden i model-klassen hedde: OnPostHandlerMetodeNavn (her OnPostNameSearch( ) - så kaldes metoden automatisk ved klik (on submit) på "knappen").




Step 7 (Afprøv)

Afprøv at programmet virker og at der kan søges efter Items objekter via søgefeldtet ala:

 

 

 




Step 8 (IItemService, ItemService)
Næste step er at tilføje en filter funktion der kan filtrere Items efter en mindste pris og/eller en maks pris. I gen starter vi med IItemsService og ItemService.

Tilføj en metode-signatur for funktionen: PriceFilter til IItemService: IEnumerable<Item> PriceFilter(int maxPrice, int minPrice = 0);

Implementer metoden i ItemService ala:

public IEnumerable<Item> PriceFilter(int maxPrice, int minPrice = 0)
{
List<Item> filterList = new List<Item>();
foreach (Item item in _items)
{
if ((minPrice == 0 && item.Price <= maxPrice) || (maxPrice == 0 && item.Price >= minPrice) || (item.Price >= minPrice && item.Price <= maxPrice))
{
filterList.Add(item);
}
}

return filterList;
}

Overvej:

 

Step 9 (GetAllItems.cshtml.cs)
Ideen er, at det skal være muligt at indtaste en min og en maks pris på siden GetAllItems og få vist alle de Items der har en pris der ligger inden for grænserne i sidens tabel.

a) Add to properties MinPrice og MaxPrice til Class GetAllItemsModel og tilføj annotation [BindProperty], så de kan bindes til input-fields på siden.

Siden GetAllItems kommer til at indeholde en Filter-Button og når der klikkes på knappen, skal service funktionen PriceFilter kaldes med MinPrice og MaxPrice som argumenter. Listen Items skal assignes til resultatet så det kan blive vist i tabellen.

b) Add metoden OnPostPriceFilter til klassen (ps. minder om OnPostNameSearch)

 

Step 10 (GetAllItems.cshtml)
Nu mangler vi blot at tilføje et <form>-tag til siden. <form> tagget skal indeholde <input>-tags med et asp-for der binder til MinPrice og MaxPrice i model-klassen samt et <input>-tag af typen "submit" med et asp-page-handler der binder til metoden PriceFilter i modellen.

<form method="post" class="form-inline">
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Min</span>
</div>
<input asp-for="MinPrice" class="form-control mr-1"/>

<div class="input-group-prepend">
<span class="input-group-text">Max</span>
</div>
<input asp-for="MaxPrice" class="form-control mr-1"/>
<input type="submit" asp-page-handler="PriceFilter" value="Filter" class="btn btn-primary" />
</div>
</form>

Overvej:

 

Step 11 (Afprøv)
Afprøv at programmet virker og at der kan filtreres efter min og maks priser på Items objekter:


God fornøjelse!
Henrik Høltzer