Przejdź do głównej zawartości

Tworzenie formularza w CloudFlow

Formularze w CloudFlow służą do edycji rekordów. Używają komponentu CFForm i dziedziczą po CFComponentBase<T>.

Szablon formularza

@inherits CFComponentBase<[NAZWA_KLASY]>
<CFForm TItem="[NAZWA_KLASY]" Record="@Record" Expand="[RELACJE]">
<RadzenStack>
<RadzenRow>
<CFTextBox SizeMD="6" @bind-Value="@context.Name" Required="true" LabelText="Nazwa" />
<CFTextBox SizeMD="6" @bind-Value="@context.Code" LabelText="Kod" />
</RadzenRow>
<RadzenRow>
<CFNumeric SizeMD="4" @bind-Value="@context.Price" LabelText="Cena" />
<CFDropDown SizeMD="4" TValue="ProductStatus"
Data="@(Tools.GenerateDropdownList<ProductStatus>())"
@bind-Value="@context.Status"
TextProperty="Text"
ValueProperty="Value" />
</RadzenRow>
</RadzenStack>
</CFForm>

@code {
#nullable enable
}

Struktura CFForm

Komponent CFForm to główny wrapper formularza obsługujący walidację, zapis i anulowanie.

Parametry CFForm

ParametrTypOpis
TItemTypeTyp encji (musi dziedziczyć po CFEntity)
RecordTItem?Rekord do edycji (przekazany z gridu lub strony)
ExpandstringRelacje do załadowania (rozdzielone przecinkami)
SelectstringPola do pobrania
FilterstringFiltr OData
IsModalboolCzy formularz jest w modalu
EnableAIFillboolWłącz wypełnianie AI

Callbacki CFForm

CallbackTypOpis
OnSubmitEventCallbackPo wysłaniu formularza
OnCancelEventCallback<MouseEventArgs>Po anulowaniu
OnLoadEventCallback<TItem>Po załadowaniu rekordu
OnAfterCreateEventCallback<TItem>Po utworzeniu nowego rekordu
OnAfterUpdateEventCallback<TItem>Po aktualizacji rekordu

Komponenty formularza CF

CloudFlow dostarcza zestaw komponentów UI z automatyczną walidacją i lokalizacją.

CFTextBox - Pole tekstowe

<CFTextBox SizeMD="6" 
@bind-Value="@context.Name"
Required="true"
LabelText="Nazwa produktu"
Placeholder="Wpisz nazwę..." />

CFTextArea - Pole wieloliniowe

<CFTextArea SizeMD="12" 
@bind-Value="@context.Description"
LabelText="Opis"
Rows="5" />

CFNumeric - Pole numeryczne

<CFNumeric SizeMD="4" 
@bind-Value="@context.Price"
LabelText="Cena"
Format="C2"
Min="0" />

CFDatePicker - Wybór daty

<CFDatePicker SizeMD="4" 
@bind-Value="@context.StartDate"
Required="true"
LabelText="Data rozpoczęcia" />

CFDropDown - Lista rozwijana

<CFDropDown SizeMD="4" 
TValue="ProductStatus"
Data="@(Tools.GenerateDropdownList<ProductStatus>())"
@bind-Value="@context.Status"
TextProperty="Text"
ValueProperty="Value"
LabelText="Status" />

CFDropDownDataGrid - Dropdown z gridem

Używany do wyboru rekordu z innej encji (relacja FK):

<CFDropDownDataGrid SizeMD="6" 
TValue="Guid?"
@bind-Value="@context.CategoryId"
LabelText="Kategoria" />

CFCheckbox - Pole wyboru

<CFCheckbox @bind-Value="@context.IsActive" 
LabelText="Aktywny" />

CFMask - Pole z maską

<CFMask SizeMD="4" 
@bind-Value="@context.NIP"
LabelText="NIP"
Mask="**********"
CharacterPattern="[0-9]" />

CFRadioButton - Przyciski radio

<CFRadioButton TValue="PaymentMethod"
@bind-Value="@context.PaymentMethod"
Data="@paymentMethods"
TextProperty="Name"
ValueProperty="Value" />

Wspólne parametry komponentów

ParametrTypOpis
@bind-ValueTValueWiązanie wartości
LabelTextstringEtykieta pola
RequiredboolCzy pole jest wymagane
Size / SizeMD / SizeLGintSzerokość kolumny (1-12)
DisabledboolCzy pole jest wyłączone
PlaceholderstringTekst placeholder
Responsywny layout

Używaj Size, SizeMD, SizeLG do tworzenia responsywnych layoutów. Wartości odpowiadają systemowi 12-kolumnowemu.

Zakładki w formularzu

Dla złożonych formularzy używaj zakładek:

<CFForm TItem="Product" Record="@Record" Expand="Category,Variants">
<RadzenStack>
<RadzenRow>
<RadzenTabs RenderMode="Radzen.TabRenderMode.Client"
TabPosition="TabPosition.Left"
class="w-100">
<Tabs>
<RadzenTabsItem Text="Dane podstawowe" Icon="description">
<RadzenStack>
<RadzenRow>
<CFTextBox SizeMD="6" @bind-Value="@context.Name"
Required="true" LabelText="Nazwa" />
<CFTextBox SizeMD="6" @bind-Value="@context.Code"
LabelText="Kod" />
</RadzenRow>
</RadzenStack>
</RadzenTabsItem>

<RadzenTabsItem Text="Ceny" Icon="attach_money">
<RadzenRow>
<CFNumeric SizeMD="4" @bind-Value="@context.Price"
LabelText="Cena netto" />
<CFNumeric SizeMD="4" @bind-Value="@context.VatRate"
LabelText="Stawka VAT %" />
</RadzenRow>
</RadzenTabsItem>

<RadzenTabsItem Text="Warianty" Icon="inventory_2">
@* Embedded grid *@
</RadzenTabsItem>
</Tabs>
</RadzenTabs>
</RadzenRow>
</RadzenStack>
</CFForm>

Embedded DataGrid (relacje)

Dla edycji kolekcji podrzędnych (np. pozycje zamówienia):

<CFDataGridEmbedded TItem="ProductVariant" 
TForm="DummyForm"
@bind-Data="context.Variants"
ParentObject="context">
<ChildContent Context="variants">
<RadzenDataGridColumn TItem="ProductVariant" Property="Name" Title="Nazwa">
<EditTemplate Context="row">
<RadzenTextBox @bind-Value="@row.Name" class="rz-w-100" />
</EditTemplate>
</RadzenDataGridColumn>
<RadzenDataGridColumn TItem="ProductVariant" Property="SKU" Title="SKU">
<EditTemplate Context="row">
<RadzenTextBox @bind-Value="@row.SKU" class="rz-w-100" />
</EditTemplate>
</RadzenDataGridColumn>
<RadzenDataGridColumn TItem="ProductVariant" Property="Price" Title="Cena">
<EditTemplate Context="row">
<RadzenNumeric @bind-Value="@row.Price" class="rz-w-100" />
</EditTemplate>
</RadzenDataGridColumn>
</ChildContent>
</CFDataGridEmbedded>
DummyForm

TForm="DummyForm" używamy gdy nie potrzebujemy osobnego formularza dla encji podrzędnej - edycja inline w gridzie.

Logika w code block

@code {
#nullable enable

// Pola lokalne
private bool isBusy = false;
private IEnumerable<Category> categories = [];

// Inicjalizacja
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
categories = await DatabaseService.GetAsync<Category>();
}

// Metody pomocnicze
private async Task LoadExternalData()
{
isBusy = true;
try
{
// Logika ładowania danych z zewnętrznego źródła
var data = await ExternalApi.FetchDataAsync();
// Aktualizacja rekordu
}
finally
{
isBusy = false;
StateHasChanged();
}
}

// Walidacja niestandardowa
private bool ValidateNIP(string? nip)
{
if (string.IsNullOrEmpty(nip)) return true;
// Logika walidacji NIP
return nip.Length == 10;
}
}

Przykład kompletnego formularza

@inherits CFComponentBase<Product>
<CFForm TItem="Product" Record="@Record" Expand="Category,Variants">
<RadzenStack>
<RadzenRow>
<RadzenTabs RenderMode="Radzen.TabRenderMode.Client"
TabPosition="TabPosition.Left"
class="w-100">
<Tabs>
<RadzenTabsItem Text="Dane podstawowe" Icon="description">
<RadzenStack>
<RadzenRow>
<CFTextBox SizeMD="6" @bind-Value="@context.Name"
Required="true"
LabelText="@ClassLocalizer["Name"]" />
<CFTextBox SizeMD="3" @bind-Value="@context.Code"
LabelText="@ClassLocalizer["Code"]" />
<CFDropDown SizeMD="3"
TValue="ProductStatus"
Data="@(Tools.GenerateDropdownList<ProductStatus>())"
@bind-Value="@context.Status"
TextProperty="Text"
ValueProperty="Value"
LabelText="@ClassLocalizer["Status"]" />
</RadzenRow>
<RadzenRow>
<CFNumeric SizeMD="4" @bind-Value="@context.Price"
LabelText="@ClassLocalizer["Price"]" />
<CFNumeric SizeMD="4" @bind-Value="@context.Quantity"
LabelText="@ClassLocalizer["Quantity"]" />
<CFDropDownDataGrid SizeMD="4"
TValue="Guid?"
@bind-Value="@context.CategoryId"
LabelText="@ClassLocalizer["Category"]" />
</RadzenRow>
<RadzenRow>
<CFTextArea SizeMD="12"
@bind-Value="@context.Description"
LabelText="@ClassLocalizer["Description"]"
Rows="4" />
</RadzenRow>
</RadzenStack>
</RadzenTabsItem>

<RadzenTabsItem Text="Warianty" Icon="inventory_2">
<CFDataGridEmbedded TItem="ProductVariant"
TForm="DummyForm"
@bind-Data="context.Variants"
ParentObject="context">
<ChildContent Context="variants">
<RadzenDataGridColumn TItem="ProductVariant"
Property="Name"
Title="Nazwa">
<EditTemplate Context="row">
<RadzenTextBox @bind-Value="@row.Name"
class="rz-w-100" />
</EditTemplate>
</RadzenDataGridColumn>
<RadzenDataGridColumn TItem="ProductVariant"
Property="SKU"
Title="SKU">
<EditTemplate Context="row">
<RadzenTextBox @bind-Value="@row.SKU"
class="rz-w-100" />
</EditTemplate>
</RadzenDataGridColumn>
</ChildContent>
</CFDataGridEmbedded>
</RadzenTabsItem>
</Tabs>
</RadzenTabs>
</RadzenRow>
</RadzenStack>
</CFForm>

@code {
#nullable enable
}

Następne kroki