Tworzenie strony w CloudFlow
Strony w CloudFlow zawierają grid z listą rekordów i używają komponentu CFDataGrid.
Szablon strony
@page "/[ROUTE]"
@page "/[ROUTE-PL]"
@using CloudFlow.Shared.Components
@attribute [Authorize]
@inherits CFComponentBase<[NAZWA_KLASY]>
<RadzenRow>
<RadzenColumn SizeMD="12">
<RadzenCard class="rz-p-0">
<CFDataGrid TItem="[NAZWA_KLASY]"
TForm="[NAZWA_FORMULARZA]"
Expand="[RELACJE]"
OrderBy="Name asc">
<ChildContent>
<RadzenDataGridColumn TItem="[NAZWA_KLASY]"
Property="Name"
Title="@ClassLocalizer["Name"]" />
<RadzenDataGridColumn TItem="[NAZWA_KLASY]"
Property="Code"
Title="@ClassLocalizer["Code"]" />
</ChildContent>
</CFDataGrid>
</RadzenCard>
</RadzenColumn>
</RadzenRow>
@code {
#nullable enable
}
Routing
Każda strona powinna mieć dwa routy - angielski i polski:
@page "/products"
@page "/produkty"
Konwencja nazewnictwa
- Route EN: lowercase, liczba mnoga, kebab-case dla wielu słów (
/order-items) - Route PL: lowercase, liczba mnoga, kebab-case (
/pozycje-zamowien)
Struktura CFDataGrid
Parametry CFDataGrid
| Parametr | Typ | Opis |
|---|---|---|
TItem | Type | Typ encji |
TForm | Type | Typ formularza do edycji |
Expand | string | Relacje do załadowania |
Select | string | Pola do pobrania |
Filter | string | Domyślny filtr OData |
OrderBy | string | Domyślne sortowanie |
AllowAdd | bool | Czy pokazać przycisk dodawania |
ConfirmDelete | bool | Czy potwierdzać usunięcie |
UseTabs | bool | Otwórz w zakładce zamiast dialogu |
CreateInModal | bool | Twórz nowe w modalu |
DialogOptions | DialogOptions | Opcje dialogu |
FormParameters | Dictionary<string, object> | Parametry dla formularza |
Callbacki
| Callback | Typ | Opis |
|---|---|---|
OnMenuItemClicked | EventCallback<MenuItemClickEventArgs> | Kliknięcie w menu kontekstowe |
Definiowanie kolumn
Kolumna tekstowa
<RadzenDataGridColumn TItem="Product"
Property="Name"
Title="Nazwa produktu"
Width="200px" />
Kolumna z formatowaniem
<RadzenDataGridColumn TItem="Product"
Property="Price"
Title="Cena"
FormatString="{0:C2}" />
Kolumna z relacją
<RadzenDataGridColumn TItem="Product"
Property="Category.Name"
Title="Kategoria" />
Expand wymagany
Aby wyświetlić dane z relacji, musisz dodać ją do parametru Expand:
<CFDataGrid TItem="Product" TForm="ProductForm" Expand="Category">
Kolumna z enumem
<RadzenDataGridColumn TItem="Product" Property="Status" Title="Status">
<Template Context="data">
@(data.Status.GetDisplayName())
</Template>
</RadzenDataGridColumn>
Kolumna z datą
<RadzenDataGridColumn TItem="Product"
Property="CreatedOn"
Title="Data utworzenia"
FormatString="{0:dd.MM.yyyy}" />
Kolumna z boolean
<RadzenDataGridColumn TItem="Product" Property="IsActive" Title="Aktywny">
<Template Context="data">
<RadzenCheckBox Value="@data.IsActive" Disabled="true" />
</Template>
</RadzenDataGridColumn>
Kolumna z ikoną statusu
<RadzenDataGridColumn TItem="Product" Property="Status" Title="Status" Width="100px">
<Template Context="data">
@switch (data.Status)
{
case ProductStatus.Active:
<RadzenBadge BadgeStyle="BadgeStyle.Success" Text="Aktywny" />
break;
case ProductStatus.Inactive:
<RadzenBadge BadgeStyle="BadgeStyle.Warning" Text="Nieaktywny" />
break;
case ProductStatus.Discontinued:
<RadzenBadge BadgeStyle="BadgeStyle.Danger" Text="Wycofany" />
break;
}
</Template>
</RadzenDataGridColumn>
Lokalizacja (ClassLocalizer)
Używaj ClassLocalizer do automatycznego tłumaczenia nazw pól:
<RadzenDataGridColumn TItem="Product"
Property="Name"
Title="@ClassLocalizer["Name"]" />
To automatycznie pobierze tłumaczenie z pliku Product.pl-PL.resx.
Filtrowanie
Domyślny filtr
<CFDataGrid TItem="Product"
TForm="ProductForm"
Filter="Status eq 'Active'">
Dynamiczny filtr
<CFDataGrid TItem="Product"
TForm="ProductForm"
Filter="@currentFilter">
@code {
private string currentFilter = "";
private void ApplyFilter(ProductStatus status)
{
currentFilter = $"Status eq '{status}'";
StateHasChanged();
}
}
Sortowanie
Domyślne sortowanie
<CFDataGrid TItem="Product"
TForm="ProductForm"
OrderBy="Name asc">
Sortowanie wielokolumnowe
<CFDataGrid TItem="Product"
TForm="ProductForm"
OrderBy="Category/Name asc, Name asc">
Akcje niestandardowe
Menu kontekstowe
<CFDataGrid TItem="Product"
TForm="ProductForm"
OnMenuItemClicked="HandleMenuClick">
<ChildContent>
@* kolumny *@
</ChildContent>
</CFDataGrid>
@code {
private async Task HandleMenuClick(MenuItemClickEventArgs args)
{
var product = (Product)args.Data;
switch (args.Value?.ToString())
{
case "duplicate":
await DuplicateProduct(product);
break;
case "export":
await ExportProduct(product);
break;
case "archive":
await ArchiveProduct(product);
break;
}
}
private async Task DuplicateProduct(Product product)
{
// Logika duplikacji
var newProduct = new Product
{
Name = product.Name + " (kopia)",
Code = product.Code + "-COPY",
Price = product.Price
};
await DatabaseService.CreateAsync(newProduct);
NotificationService.Notify(NotificationSeverity.Success, "Sukces", "Produkt zduplikowany");
}
}
Przekazywanie parametrów do formularza
<CFDataGrid TItem="OrderItem"
TForm="OrderItemForm"
FormParameters="@formParams">
@code {
private Dictionary<string, object> formParams => new()
{
{ "OrderId", CurrentOrderId },
{ "DefaultQuantity", 1 }
};
private Guid CurrentOrderId { get; set; }
}
Przykład kompletnej strony
@page "/products"
@page "/produkty"
@using CloudFlow.Shared.Components
@attribute [Authorize]
@inherits CFComponentBase<Product>
<RadzenRow>
<RadzenColumn SizeMD="12">
<RadzenCard class="rz-p-0">
<CFDataGrid TItem="Product"
TForm="ProductForm"
Expand="Category"
OrderBy="Name asc"
OnMenuItemClicked="HandleMenuClick">
<ChildContent>
<RadzenDataGridColumn TItem="Product"
Property="Code"
Title="@ClassLocalizer["Code"]"
Width="100px" />
<RadzenDataGridColumn TItem="Product"
Property="Name"
Title="@ClassLocalizer["Name"]" />
<RadzenDataGridColumn TItem="Product"
Property="Category.Name"
Title="@ClassLocalizer["Category"]" />
<RadzenDataGridColumn TItem="Product"
Property="Price"
Title="@ClassLocalizer["Price"]"
FormatString="{0:C2}"
Width="120px" />
<RadzenDataGridColumn TItem="Product"
Property="Quantity"
Title="@ClassLocalizer["Quantity"]"
Width="100px" />
<RadzenDataGridColumn TItem="Product"
Property="Status"
Title="@ClassLocalizer["Status"]"
Width="120px">
<Template Context="data">
@switch (data.Status)
{
case ProductStatus.Active:
<RadzenBadge BadgeStyle="BadgeStyle.Success"
Text="Aktywny" />
break;
case ProductStatus.Inactive:
<RadzenBadge BadgeStyle="BadgeStyle.Warning"
Text="Nieaktywny" />
break;
case ProductStatus.Discontinued:
<RadzenBadge BadgeStyle="BadgeStyle.Danger"
Text="Wycofany" />
break;
}
</Template>
</RadzenDataGridColumn>
</ChildContent>
</CFDataGrid>
</RadzenCard>
</RadzenColumn>
</RadzenRow>
@code {
#nullable enable
private async Task HandleMenuClick(MenuItemClickEventArgs args)
{
var product = (Product)args.Data;
switch (args.Value?.ToString())
{
case "duplicate":
await DuplicateProduct(product);
break;
}
}
private async Task DuplicateProduct(Product product)
{
var newProduct = new Product
{
Name = product.Name + " (kopia)",
Code = product.Code + "-COPY",
Price = product.Price,
CategoryId = product.CategoryId
};
await DatabaseService.CreateAsync(newProduct);
NotificationService.Notify(NotificationSeverity.Success, "Sukces", "Produkt zduplikowany");
}
}
Następne kroki
- Komponenty CF - Szczegółowa dokumentacja wszystkich komponentów
- Tworzenie modułów - Powrót do przeglądu tworzenia modułów