Przejdź do głównej zawartości

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

ParametrTypOpis
TItemTypeTyp encji
TFormTypeTyp formularza do edycji
ExpandstringRelacje do załadowania
SelectstringPola do pobrania
FilterstringDomyślny filtr OData
OrderBystringDomyślne sortowanie
AllowAddboolCzy pokazać przycisk dodawania
ConfirmDeleteboolCzy potwierdzać usunięcie
UseTabsboolOtwórz w zakładce zamiast dialogu
CreateInModalboolTwórz nowe w modalu
DialogOptionsDialogOptionsOpcje dialogu
FormParametersDictionary<string, object>Parametry dla formularza

Callbacki

CallbackTypOpis
OnMenuItemClickedEventCallback<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

<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