Razor to silnik widoków w ASP.NET Core, który pozwala mieszać kod C# z HTML-em w jednym pliku .cshtml. W trzeciej lekcji kursu ASP.NET Core zajmiemy się tym, jak faktycznie przepychać dane z kontrolera do widoku, jak te widoki silnie typować modelami oraz jak obsłużyć pętle i warunki bez zbędnego rozsypywania logiki po całym projekcie. W tym wpisie znajdziesz wszystko, co pokazuję na filmie — od @model przez ViewBag po TempData.
Kod z lekcji leży na GitHubie: asp-net-core-podstawy/tree/Lekcja-3.
Modele — podstawa każdego widoku
Modele w ASP.NET Core MVC trzymamy w folderze Models. W poprzedniej lekcji zaprojektowaliśmy prostą klasę Product, teraz rozbudujemy ją o kilka właściwości i dodamy drugą klasę — Category.
namespace WebApp.Models;
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
Teraz w kontrolerze ProductController deklarujemy nową akcję, która zwróci pojedynczy produkt do widoku:
public IActionResult Product()
{
var product = new Product
{
Id = 1,
Name = "BMW M5",
Description = "To jest super samochód",
Category = "samochód"
};
return View(product);
}
Jak kompilator nie widzi klasy
Product, użyj skrótuCtrl + .— IntelliSense sam podpowie import odpowiedniegousing. To najszybsza droga do czystych referencji.
Do kontrolera przekazujemy obiekt przez View(product) i tyle — reszta dzieje się w widoku.
Silne typowanie widoku, czyli @model z dużej litery
Klikasz prawym na akcji, wybierasz „Add View”, zaznaczasz „Empty” (bez scaffoldingu) i dostajesz czysty Product.cshtml. Pierwsza linijka widoku to deklaracja, jakim modelem chcesz typować ten plik:
@model WebApp.Models.Product
<h1>@Model.Name</h1>
<p>Kategoria: @Model.Category</p>
<p>ID: @Model.Id</p>
<p>@Model.Description</p>
Zwróć uwagę na różnicę między @model (małe „m” — deklaracja typu) i @Model (duże „M” — odwołanie do konkretnej instancji). To klasyczny tripwire dla początkujących. Po pełnej ścieżce Namespace.Models.Product Razor wie, czym jest Model, i daje pełne podpowiadanie właściwości.
Jak odpalisz aplikację i wejdziesz pod /Product/Product, zobaczysz dane z kontrolera wyrenderowane w HTML. Na tym właśnie polega cały myk — kontroler przygotowuje dane, a widok je tylko wyświetla.
Kolekcje, pętle i warunki w Razor
Pojedynczy produkt to nuda — realnie zawsze ląduje się z listą. Dorzućmy akcję List, która zwróci List<Product>:
public IActionResult List()
{
var products = new List<Product>
{
new Product { Id = 1, Name = "BMW M5", Category = "samochód" },
new Product { Id = 2, Name = "Audi RS6", Category = "samochód" },
new Product { Id = 3, Name = "Monstera", Category = "rośliny" },
new Product { Id = 4, Name = "Pepsi", Category = "napoje" }
};
return View(products);
}
Widok List.cshtml typujemy jako IEnumerable<Product> (albo List<Product>) i lecimy z pętlą:
@model IEnumerable<WebApp.Models.Product>
<h1>Lista produktów</h1>
<ul>
@foreach (var product in Model)
{
<li>@product.Category - @product.Name (ID: @product.Id)</li>
}
</ul>
Składnia @ otwiera blok Razora. Wewnątrz @foreach Razor rozumie HTML i C# naprzemiennie — wystarczy, że zagnieżdżasz tagi, a silnik sam wie, co renderować, a co wykonać. Dla pojedynczych wartości używasz @product.Name, dla bloków — klamer { ... }.
Chcesz pokazać tylko samochody? Wrzuć if:
<h2>Tylko samochody</h2>
<ul>
@foreach (var product in Model)
{
@if (product.Category == "samochód")
{
<li>@product.Name</li>
}
}
</ul>
A jakbyś chciał odwrotnie — wszystko, co nie jest samochodem — wystarczy !=:
@if (product.Category != "samochód")
{
<li>@product.Name</li>
}
Jak piszesz kod C# bez żadnego taga HTML w środku, nie musisz otwierać kolejnego
@. Razor zgaduje kontekst po tagach. To mały szczegół, ale oszczędza kilkanaście sekund przy każdym widoku.
Menu w layoutcie, czyli _Layout.cshtml
Wpisywanie URL-i ręcznie w pasku przeglądarki szybko się nudzi, więc dorzucimy linki do menu. W ASP.NET Core layout siedzi w Views/Shared/_Layout.cshtml. Zamiast klasycznego <a href="..."> masz tam pomocniki tagów (tag helpers):
<li class="nav-item">
<a class="nav-link text-dark"
asp-area=""
asp-controller="Product"
asp-action="List">Lista produktów</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark"
asp-area=""
asp-controller="Product"
asp-action="Product">Pojedynczy produkt</a>
</li>
Tag helpery (asp-controller, asp-action) są czytelne i Visual Studio podświetla je na zielono, dzięki czemu od razu widzisz, że to nie zwykły HTML. asp-area zostawiasz puste, dopóki nie dzielisz aplikacji na Areas — to temat na później. Linki renderują się do pełnych ścieżek bez hardkodowania w menu.
ViewBag, ViewData, TempData — trzy sposoby na dodatkowe dane
Nie zawsze chcesz przerzucać cały model. Czasem wystarczy pojedyncza informacja — np. tekst powitania, komunikat po zapisie formularza, flaga dla UI. ASP.NET Core daje trzy mechanizmy.
ViewBag — dynamiczny worek, najwygodniejszy w użyciu:
public IActionResult Info()
{
ViewBag.Greeting = "Cześć z kontrolera";
return View();
}
<p>@ViewBag.Greeting</p>
ViewData — to samo, tylko w formie słownika po stringach:
ViewData["Greeting"] = "Cześć z ViewData";
<p>@ViewData["Greeting"]</p>
TempData — też słownik, ale z kluczowym twistem: przeżywa jeden dodatkowy request:
TempData["Greeting"] = "Cześć z TempData";
<p>@TempData["Greeting"]</p>
Różnica w sumie sprowadza się do jednego:
ViewBagiViewDatażyją tylko w bieżącym cyklu request → response.TempDatajest utrzymywany w sesji przez jeszcze jeden kolejny request — dlatego nadaje się do flash messages po redirectach (np. „Zapisano pomyślnie” po przekierowaniu z akcji POST).
Przy formularzach zobaczysz to w praktyce — ale z grubsza: jak robisz RedirectToAction, to ViewBag po drodze zginie, a TempData przetrwa. Do tematu wrócimy w kolejnej lekcji.
Co dalej?
Razor przestaje być magią, jak tylko załapiesz trzy rzeczy: @model silnie typuje widok, @Model to konkretna instancja, a @foreach i @if dają ci pełną kontrolę nad renderowaniem. Do tego ViewBag/ViewData/TempData, gdy nie chcesz ciągnąć całego modelu dla jednej zmiennej — i masz wszystko, co potrzebne, żeby generować sensowny HTML z danych kontrolera.
W następnym wpisie bierzemy się za formularze — jak je walidować, co to są ModelState i atrybuty walidacyjne oraz czym różnią się [HttpGet] i [HttpPost]. Wtedy też pokażę konkretny case, w którym TempData ratuje dane przed utratą po redirectcie. Do zobaczenia w lekcji czwartej.



