• Aplikacje webowe
  • Tablice w formularzach HTML i ASP.NET Core - praktyczny przewodnik

Tablice w formularzach HTML i ASP.NET Core - praktyczny przewodnik

Bruno Krawczyk 11 czerwca 2026
Formularz z polami tekstowymi, opcjami wyboru, przyciskami i polami daty. Można tu stworzyć form array.

Spis treści

Formularz z jednym polem działa przewidywalnie, dopóki użytkownik nie chce dodać kilku produktów, adresów albo uczestników. Wtedy potrzebna jest tablica wartości przesyłana w ramach formularza, poprawne nazwy pól oraz logika, która bezpiecznie odtworzy dane po stronie serwera. Pokażę, jak działa form array, jak budować takie formularze w HTML i Angularze oraz jak obsłużyć je w aplikacji ASP.NET Core.

Tablica pól formularza upraszcza obsługę powtarzalnych danych

  • Jedna nazwa pola może reprezentować wiele przesłanych wartości.
  • Indeksy w nazwach pomagają ASP.NET Core zbudować kolekcję obiektów.
  • FormArray w Angularze sprawdza się przy dynamicznym dodawaniu i usuwaniu wierszy.
  • Walidację trzeba wykonać po obu stronach, bo dane z przeglądarki nie są zaufane.
  • Puste i usunięte elementy wymagają świadomej obsługi, szczególnie przy edycji rekordów.

Co oznacza tablica danych w formularzu

Tablica formularza to sposób przesłania wielu wartości tego samego rodzaju w jednym żądaniu HTTP. Przykładem może być lista produktów w zamówieniu, kilka numerów telefonu albo zestaw odpowiedzi udzielonych przez użytkownika. W praktyce nie chodzi o specjalny element HTML, lecz o konwencję nazewnictwa pól i sposób odczytania ich po stronie serwera.

Najprostszy wariant wygląda tak:



Po wysłaniu formularza serwer otrzyma trzy wartości powiązane z nazwą tags. To wystarcza dla tablic prostych, ale przy bardziej złożonych danych lepiej użyć indeksów:





W tym przypadku każdy element ma własny indeks i kilka właściwości. Taki zapis jest szczególnie wygodny w aplikacjach .NET, ponieważ mechanizm model bindingu może automatycznie zamienić dane formularza na listę obiektów modelu.

Jak zbudować tablicę w zwykłym HTML

HTML nie narzuca jednej składni dla tablic. Najważniejszy jest atrybut name, ponieważ to on staje się kluczem wysyłanym w formularzu. Atrybut id służy głównie do powiązania pola z etykietą, skryptem lub stylami.

Wiele wartości prostego typu

Gdy przesyłasz listę identyfikatorów albo tagów, możesz użyć tej samej nazwy w kilku kontrolkach:

Po stronie serwera otrzymasz kolekcję zaznaczonych wartości. Niezaznaczone checkboxy nie wysyłają nic, dlatego kod powinien poprawnie obsłużyć także pustą tablicę. To drobny szczegół, który często wychodzi dopiero przy formularzu edycji.

Lista obiektów

Jeżeli każdy element ma kilka pól, indeks powinien obejmować cały obiekt. W aplikacji zakupowej może to wyglądać następująco:





Warto zadbać o to, aby indeksy były spójne, szczególnie gdy wiersze można usuwać za pomocą JavaScriptu. W przeciwnym razie serwer może pominąć część danych albo połączyć je w sposób inny, niż zakłada interfejs. Przy prostym formularzu wystarczy ponowne przeliczenie indeksów przed wysłaniem.

Dynamiczne wiersze z FormArray w Angularze

W Angularze odpowiednikiem dynamicznej tablicy pól jest FormArray. Używam go wtedy, gdy liczba elementów nie jest znana z góry albo użytkownik może dodawać i usuwać wiersze bez przeładowania strony.

Przykładowy model formularza może wyglądać tak:

import { FormArray, FormControl, FormGroup, Validators } from '@angular/forms';

orderForm = new FormGroup({
  items: new FormArray([
    this.createItem()
  ])
});

createItem() {
  return new FormGroup({
    productId: new FormControl('', Validators.required),
    quantity: new FormControl(1, [
      Validators.required,
      Validators.min(1)
    ])
  });
}

get items() {
  return this.orderForm.controls.items;
}

addItem() {
  this.items.push(this.createItem());
}

removeItem(index: number) {
  this.items.removeAt(index);
}

W szablonie każdy element tablicy otrzymuje własny indeks:

Najważniejsza zasada brzmi: FormArray przechowuje kontrolki, a nie same wartości. Dzięki temu Angular zna stan, błędy i walidację każdego wiersza. Sam zwykły array JavaScriptu nie daje podobnej kontroli nad formularzem.

W praktyce nie dodawałbym pustych wierszy bez ograniczenia. Ustaliłbym limit, na przykład 50 elementów, a przed wysłaniem sprawdził, czy formularz jest poprawny. Chroni to interfejs przed przypadkowym rozrostem i ogranicza ilość danych przesyłanych do API.

Formularz menu z polami

Jak odebrać kolekcję w ASP.NET Core

Po stronie backendu najlepiej przyjąć model dopasowany do danych biznesowych, a nie odczytywać wszystkie wartości ręcznie z formularza. Przykładowe klasy mogą wyglądać tak:

public sealed class OrderViewModel
{
    public List Items { get; set; } = [];
}

public sealed class OrderLineViewModel
{
    public int ProductId { get; set; }
    public int Quantity { get; set; }
}

Akcja kontrolera może przyjąć model bez dodatkowego mapowania:

[HttpPost]
public IActionResult Create(OrderViewModel model)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    foreach (var item in model.Items)
    {
        // Walidacja biznesowa i zapis
    }

    return RedirectToAction(nameof(Index));
}

Dla pól o nazwach Items[0].ProductId i Items[1].Quantity ASP.NET Core spróbuje utworzyć listę elementów. To właśnie model binding, czyli mechanizm, który łączy dane z żądania z właściwościami obiektu C#.

Przy kolekcjach warto uważać na usuwanie środkowego elementu. Jeżeli formularz wysyła indeksy 0 i 2, pomijając 1, binder może nie odtworzyć listy tak, jak oczekujesz. W formularzach edycji bezpieczniejszym rozwiązaniem jest przesyłanie stabilnych identyfikatorów albo specjalnych pól indeksu.





Takie podejście rozdziela identyfikator wiersza od jego pozycji na ekranie. Ma sens wtedy, gdy użytkownik może sortować elementy, usuwać je w dowolnej kolejności albo edytować rekordy istniejące już w bazie.

Walidacja i bezpieczeństwo danych z tablicy

Walidacja po stronie przeglądarki poprawia wygodę, ale nie zabezpiecza aplikacji. Użytkownik może zmienić żądanie ręcznie, dlatego backend powinien ponownie sprawdzić każdy element kolekcji, jego typ, zakres i uprawnienia.

  • Sprawdź, czy identyfikator produktu istnieje i jest dostępny dla bieżącego użytkownika.
  • Odrzuć ilości równe 0, ujemne albo przekraczające ustalony limit.
  • Ustal maksymalną liczbę elementów, na przykład 50 lub 100.
  • Nie ufaj cenie ani nazwie przesłanej z przeglądarki, jeśli można je pobrać z bazy.
  • Obsłuż pustą kolekcję i niepoprawne indeksy bez błędu aplikacji.

Dobrym zwyczajem jest walidacja na poziomie modelu oraz dodatkowa walidacja biznesowa w serwisie. Atrybut [Range(1, 100)] może sprawdzić zakres ilości, ale nie odpowie na pytanie, czy dany produkt jest jeszcze dostępny. To dwa różne poziomy kontroli.

Przy dużych formularzach znaczenie ma również rozmiar żądania. Dla kilkunastu wierszy zwykły POST jest naturalnym rozwiązaniem, lecz przy setkach rekordów lepiej rozważyć osobny endpoint JSON, stronicowanie albo zapis etapami. Tablica w formularzu nie jest automatycznie dobrym formatem dla każdej skali.

Najczęstsze błędy przy obsłudze tablic pól

Użycie atrybutu id zamiast name

Serwer nie buduje kolekcji na podstawie identyfikatorów DOM. Jeżeli pole ma tylko id="items-0-quantity", ale nie ma poprawnego name, jego wartość może w ogóle nie trafić do danych formularza. Zawsze sprawdzam najpierw nazwy pól w narzędziach deweloperskich.

Wysyłanie indeksów, które nie odpowiadają danym

Po usunięciu wiersza indeksy wizualne i dane mogą się rozjechać. Efekt bywa zdradliwy, bo formularz wygląda poprawnie, ale backend otrzymuje brakujący element albo właściwości przypisane do złego rekordu. Rozwiązaniem jest reindeksacja przed wysłaniem albo użycie stabilnych kluczy.

Brak rozróżnienia między pustym a usuniętym elementem

W formularzu edycji pusty wiersz może oznaczać błąd użytkownika, a usunięty wiersz może oznaczać polecenie usunięcia rekordu z bazy. Nie warto zgadywać tej intencji na podstawie pustych wartości. Lepiej przesłać identyfikator oraz jawny stan, na przykład IsDeleted.

Przeczytaj również: var w JavaScripcie - zakres, hoisting i pułapki starego kodu

Przyjmowanie kolekcji bez limitu

Dynamiczny przycisk „Dodaj” powinien mieć ograniczenie. Bez niego użytkownik może przypadkiem wysłać tysiące pól, a aplikacja będzie musiała je związać, zwalidować i zapisać. Limit po stronie frontendu pomaga, ale ten sam limit trzeba egzekwować na backendzie.

Prosty wybór rozwiązania dla aplikacji webowej

Przypadek Najlepsze rozwiązanie Dlaczego
Kilka zaznaczonych wartości Powtarzalne pola z tym samym name Prosty format i łatwe odczytanie kolekcji
Lista obiektów w MVC Indeksowane nazwy pól Naturalne powiązanie z modelem C#
Dynamiczne dodawanie w Angularze FormArray Kontrola stanu i walidacji każdego wiersza
Duży formularz lub aplikacja SPA JSON przesyłany do API Lepsza kontrola struktury i obsługi błędów

Moja praktyczna reguła jest prosta. Dla klasycznego formularza MVC wybieram silnie typowany model i indeksowane pola. Dla interfejsu Angular używam FormArray z FormGroup wewnątrz. Dopiero gdy liczba danych rośnie albo formularz staje się częścią rozbudowanego procesu, przechodzę na osobny kontrakt JSON.

Od czego zacząć przy wdrażaniu tablicy formularza

Najpierw opisz strukturę jednego elementu, a dopiero później sposób dodawania kolejnych. Jeżeli pojedynczy wiersz ma jasny model, walidację i identyfikator, cała kolekcja pozostaje łatwa do testowania.

Sprawdź trzy scenariusze: pustą listę, jeden element oraz kilka elementów z usuniętym środkowym wierszem. Te przypadki szybko ujawniają problemy z nazwami pól, indeksami i mapowaniem danych.

Dobrze zaprojektowana tablica w formularzu nie wymaga skomplikowanej magii. Potrzebuje spójnego kontraktu między HTML-em, frontendem i backendem, rozsądnych limitów oraz walidacji wykonywanej po stronie serwera. To właśnie te szczegóły decydują, czy dynamiczny formularz będzie wygodnym narzędziem, czy źródłem trudnych do odtworzenia błędów.

FAQ - Najczęstsze pytania

Użyj kilku pól z tym samym atrybutem name, na przykład categories dla checkboxów. Serwer otrzyma kolekcję zaznaczonych wartości, ale niezaznaczone checkboxy nie wyślą nic, dlatego trzeba obsłużyć także pustą tablicę.

Każdy obiekt powinien mieć indeks obejmujący wszystkie jego właściwości, na przykład Items[0].ProductId i Items[0].Quantity. Przy usuwaniu środkowych elementów można użyć spójnych indeksów, reindeksacji albo stabilnych identyfikatorów z polami Items.index.

FormArray sprawdza się, gdy użytkownik może dynamicznie dodawać i usuwać wiersze, a liczba elementów nie jest znana z góry. Przechowuje kontrolki, więc Angular może śledzić stan i walidację każdego wiersza; warto również ustalić limit, na przykład 50 elementów.

Walidacja w przeglądarce poprawia wygodę, ale backend musi ponownie sprawdzić każdy element, jego typ, zakres i uprawnienia. Należy między innymi ograniczyć liczbę elementów, odrzucać ilości równe 0 i ujemne oraz pobierać ceny i nazwy z bazy zamiast ufać wartościom z przeglądarki.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi

formularze
angular
formarray
asp.net core
walidacja
Autor Bruno Krawczyk
Bruno Krawczyk
Mam na imię Bruno i od 8 lat zgłębiam tajniki programowania w ekosystemie .NET, chmury Azure oraz sztucznej inteligencji. Moja przygoda z technologią zaczęła się od ciekawości, jak złożone systemy mogą ułatwiać codzienne życie i rozwiązywać realne problemy. Dziś moją misją jest dzielenie się tą wiedzą, starając się przybliżyć nawet najbardziej skomplikowane zagadnienia w sposób zrozumiały i przystępny dla każdego. W moich artykułach na kursdotnet.pl skupiam się na praktycznych aspektach, analizuję najnowsze trendy i weryfikuję informacje, aby dostarczyć Wam treści, które są nie tylko dokładne i aktualne, ale przede wszystkim użyteczne w Waszej własnej ścieżce rozwoju technologicznego.

Udostępnij artykuł

Napisz komentarz