Wszyscy deweloperzy prędzej czy później stają przed problemem stworzenia dynamicznego formularza. Dynamiczny formularz to taki, w którym ilość pół może być zmieniana przez użytkownika. Może to być dowolna ilość plików wysyłanych do serwera, czy zmieniające się pola formularza, w zależności od udzielanych odpowiedzi. Na przykładzie prostego formularza, zajmę się tym tematem.
Na początek tworzę formularz z możliwością wpisania danych jednej osoby, obrazkiem odpowiedzialnym za dodanie kolejnej osoby oraz nagłówek i przycisk wysyłania formularza:
<form action="parser.php" method="post"> <table id="listaOsob"> <thead> <tr> <th>Lp</th> <th>Imię</th> <th>Nazwisko</th> <th>+/-</th> </tr> </thead> <tfoot> <tr> <th colspan="4"> <input type="submit" name="wyslij" value="Wyślij"> </th> </tr> </tfoot> <tbody> <tr> <td>1.</td> <td><input type="text" name="imie[]"></td> <td><input type="text" name="nazwisko[]"></td> <td><img src="http://antczak.org/source/dynamic_forms/11.png" id="dodajOsobe" alt="dodaj"></td> </tr> </tbody> </table> </form>
Ideą jest, aby kliknięcie obrazka powodowało dodanie do części tabelki <tbody> kolejnego wiersza, czyli tagu <tr> wraz z treścią. Potrzebujemy do tego szablonu tagu <tr>, czyli pól formularza do których będziemy mogli wpisać dane kolejnej osoby:
<textarea id="szablon" style="display:none;" cols="1" rows="1"> <tr id="wiersz_{0}"> <td>{0}.</td> <td><input type="text" name="imie[]"></td> <td><input type="text" name="nazwisko[]"></td> <td><img src="http://antczak.org/source/dynamic_forms/12.png" id="usunOsobe_{0}" alt="usun"></td> </tr> </textarea>
Szablon tagu <tr> opakowujemy w tag <textarea>, za pomocą CSS ukrywany <textarea>. Wiersz ten różni się od pierwszego wiersza w tabeli kilkoma rzeczami. Zamiast obrazka mamy
, który będzie usuwał dany wiersz. Uwagę zwracają także tokeny {0}. Są to miejsca, w które później nasz mechanizm będzie wstawiał kolejne numery. Numerowanie jest potrzebne, ponieważ każde pole formularza powinno mieć unikatową nazwę.
Przejdźmy do kolejnego kroku. Mamy już formularz, mamy szablon kolejnego wiersza, pozostało to poskładać.
<script type="text/javascript"> $(document).ready(function(){ // tworzymy zmienną i, wykorzystamy ją do identyfikowania wierszy // ustawiamy jej wartość 2, ponieważ jeden wiersz jest już w tabeli var i = 2; // tworzymy zmienną szablonWiersza i wczytujemy do niej szablon z textarea #szablon var szablonWiersza = jQuery.format($("#szablon").val()); // tworzymy funkcję, która dodaje szablon do właściwej tabeli function dodajWiersz() { var ii = i++; // dodajemy wiersz do właściwej tabeli $("#listaOsob tbody").append(szablonWiersza(ii)); // Nowy wiersz jest już widoczny. // Każdy element nowego wiersza posiada swoje id // tr ma id: wiersz_2, ikona minus ma id: usunOsobe_2 // Do nowo dodanej ikony - dodajmy zdarzenie. // W momencie kliknięcia minusa, wiersz zostanie usunięty. $("#usunOsobe_" + ii).click(function(){ $("#wiersz_" + ii).remove(); }); } // do przycisku #dodajOsobe dodajemy zdarzenie, // kliknięcie na przycisk plus wywoła fukcję dodajWiersz $("#dodajOsobe").click(dodajWiersz); }); </script>
Ponieważ tu zaczęły się odrobinę poważniejsze rzeczy, skomentowałem kod linia po linii. Mechanizm działania jest następujący: każde kliknięcie w obrazek powoduje wywołanie funkcji dodajWiersz. Wewnątrz niej dzieje się kilka rzeczy. Funkcja jQuery.format odczytuje zawartość szablonu wiersza. Użycie w postaci
$(szablonWiersza(ii)).appendTo("#listaOsob tbody");
powoduje wygenerowanie wiersza według szablonu oraz podstawienie w miejsce tokena {0} parametru wywołania – w naszym wypadku zmiennej ii, czyli kolejnego numeru. Druga część tej linii powoduje dodanie do tabeli listaOsob, do części tbody wygenerowanego wiersza. Następne linie dodają zdarzenie do obrazka , kliknięcie spowoduje usunięcie danego wiersza.
Ostatnia linia dodaje zdarzenie do obrazka , kliknięcie spowoduje wywołanie opisanego wyżej mechanizmu.
Efekt końcowy wygląda następująco:
Przygotujemy jeszcze skrypt PHP, który odczyta dane wysłane z formularza.
// tworzymy tablice która będzie zawierała wszystkie osoby $osoby = array(); // przepisujemy dane do wspolnej tablicy $osoby['imiona'] = $_POST['imie']; $osoby['nazwiska'] = $_POST['nazwisko']; // wypisujemy tablicę zawierającą wszystkie osoby $wynik = print_r($osoby,true); echo "<pre>$wynik</pre>";
Działający przykład: http://antczak.org/source/dynamic_forms/
Kod źródłoy: https://github.com/pawelantczak/antczak.org/tree/master/dynamic_forms
Wygląda to bardzo ładnie, ja jednak skusił bym się na renumerację pól formularza.
Po stworzeniu np. dwudziestu pól i usunięciu od dziesiątego do osiemnastego, pola wcześniej oznaczone jako dziewiętnaście i dwadzieścia mogły by zamienić się na kolejne istniejące, w tym przypadku jedenaście i dwanaście bo w tym momencie pozostają w formie jak by poprzednie pola nadal istniały.
Witam.
Dzięki za komentarz.
Rzeczywiście. W produkcyjnej aplikacji można by to zmienić.
Zrobiłem tak dlatego, żeby było widać, że usuwamy pola, a nie tylko zmniejszamy ich ilość.
A mi brakuje jeszcze możliwości przesuwania góra dół.
A i jeszcze trochę bezsensu zrobiłeś te nazewnictwo pól input text.
Mógłbyś zrobić tak:
a później w php miałbyś już od razu posegregowane bez potrzeby przerabiania przesłanych danych.
😉 pozdro.
Poprawione, dzięki za sugestię.
No nie do końca poprawione, bo teraz nie masz par imię nazwisko. Tylko masz osobną tablicę imiona i nazwiska, a nam chodzi o to by były pary array { imie, nazwisko } musisz dodać jeszcze jedną tablice tak jak Ci napisałem komentarz wyżej. Chyba że taki sposób Cie zadowala. Bo mnie osobiście nie. W taki sposób jak zastosowanie par można bezpośrednio przesłać jako argument do funkcji w postgresql np przy użyciu typu ANNYARAY i później już łatwo na loopie powrzucać dane do bazy bez problemu. 😉
Kod PHP to tylko przykład. Artykuł jest nt. dynamicznych formularzach, a nie tablic w PHP. Poza tym Twój przykład nie zadziała, brakuje nazwy tablicy. Poprawnie mogłoby to wyglądać tak: name=”osoby[1][imie]” i name=”osoby[1][nazwisko]”.
Taki ficzer ;]
wszystko fajnie działa tylko jak zrobię tak: dodam 10 nowych pól, potem usunę sobie z 4 dodam znów kilka usunę kilka dodam po raz kolejny 🙂 to psuje się numerowanie poszczególnych, taki drobiazg jednak wielu ludzi zwraca uwagę na usability, no a to jest tak trochę anty 😉
Tak, to prawda. Zacytuję siebie z komentarza wyżej: „W produkcyjnej aplikacji można by to zmienić. Zrobiłem tak dlatego, żeby było widać, że usuwamy pola, a nie tylko zmniejszamy ich ilość.”
Witam! Bardzo fajnie zrobiona „lekcja”… gratuluję! Mam tylko pytanie/prośbę… zapewne banalna, ale nie jestem znawcą javascript i nie wiem końca jak zrobić, żeby przemnażać wartości liczbowe z pierwszego pola (tutaj imię) przez wartości liczbowe z drugiego pola (tut. nazwisko), a wynik wyświetlałby się w trzecim (utworzonym przeze mnie) polu (input field). Udało mi się zrobić coś zbliżonego, do zamierzenia na podstawie prostego skryptu, ale działa tylko w jednym wierszu, nie działa natomiast w wierszach dodanych w dalszej kolejności. Będę ogromnie wdzięczny za pomoc. Pozdrawiam.
Zapraszam na http://forum.php.pl/ do odpowiedniego działu, tam uzyskasz odpowiedź.
@Paweł Antczak
Witam ponownie… Przeglądam forum na php.pl, jeszcze nie udało mi sie trafić na odpowiedni temat… jest tego bardzo dużo, ale bardzo dziękuję za odpowiedź. Pozdrawiam.
@Promien
Stwórz własny temat, postaram się coś pomóc.
@Paweł Antczak
Witam ponownie! Temat został założony pod adresem: http://forum.php.pl/index.php?showtopic=143783. Będę wdzięczny za wskazówki. Pozdrawiam.
Mam pytanie jak przesłać te dane na adres e-mail
Poczytaj o funkcji mail na php.net
http://php.net/manual/en/function.mail.php
Ok już sobie poradziłem z wysyłaniem a jak zrobić pole pod tabelą w którym zmieniała by się liczba w zależności od ilości dodanych wierszy np: 200 potem 400 (po dodaniu 2 wiersza) itd
Witam serdecznie! Opieram się trochę na sposobie wykorzystanym przez Ciebie na dodwanie dynamicznych pól. Lecz powiedz mi jak na podstawie Twojego skryptu mogę wykorzystać działania na inputach. Np powielanie w innym poprzedniego, tak jakbym wpisując w imie autotycznie widział to samo w polu naziwsko: teoretycznie w jquery powinno to wyglądać następująco:
$(document).ready(function(){
$(„input[name=imie[]]”).keyup(function () {
var value = $(this).val();
$(„input[name=nazwisko[]]”).val(value);
}).keyup();
});
Niestety ta metoda działa tylko dla pierwszego wiersza!
pytanie czy jest to wykonalne na tablicach i jak się do nich odwołać w jquery jeśli są tak jak w tym przykładzie numerowane przez zmienną i – lecz ona dodaje textarea a nie poszczególnego inputa
Poradziłem sobie z problemem opierając się o id=”nazwa_pola_{0}”, a później wywołując je w funkcji przez zmienna $(„#nazwa_pola_” + ii) gdzie ii występuje w funkcji podczas tworzenia nowego wiersza. Chyba tylko w taki sposób możemy odwołać się do konkretnych pól w tym formularzu. Niestety metoda ta nie działa to dla pierwszego wiersza tylko skopiowanego szablonu.
Może komuś się przyda ta wiadomość. Pozdrawiam!
Witam, też mam pytanie w jaki sposób można odwołać się do poszczególnych indeksów danego pola korzystając z tej metody. Normalnie robi się to za pomocą metody index() jednak tutaj jak w przypadku kolegi powyżej działa ona tylko dla 1 wiersza
A ja mam inny problem. Chciałbym dodać pole radio. Nadałem mu name=”coś_tam_{0}” żeby każdy wiersz miał osobny wybór. Jednak w tym momencie nie wiem jak się do tego odwołać przy zapisie danych np. do sql. Mógłby ktoś pomóc?
Bardzo proszę mi powiedzieć w jaki sposób mogę zapisać dane z formularza do bazy.
Witam.
Mam dla ciebie fajne wyzwanie. Otóż to co stworzyłeś gwarantuje ci tylko i wyłacznie zapisywanie pol. Sprobuj tak zapisana liste potem wczytac ( np po odswiezeniu strony ) zaktualizowac i oprocz tego dodac nowe pola. Glownym problemem bedzie rozwiazanie identyfikatorow. Nie bedziesz mogl juz uzywac rzeczy w stylu name=”imie[]” beda to musialy byc pola z identyfikatorami np imie[1]. Nowo dodane pole w formularzu nie moze uzywac juz tej samej nazwy jako ze po przekazaniu jej do php nada sie jej index. imie[]=”czesiek”, imie[]=wiesiek stanie sie tablica imie(0=>”czesiek” , 1=>”wiesiek”)
Pozdrawiam
@skoczman
// tworzymy tablice która będzie zawierała wszystkie rekordy
$dane = array();
// przepisujemy dane do wspolnej tablicy
$dane_0[’nr_reklamacji’] = $_POST[’nr_reklamacji’];
$dane_0[’data_dostawy’] = $_POST[’data_dostawy’];
$dane_0[’kontrahent’] = $_POST[’kontrahent’];
$dane_1[] = $_POST[’asortyment’];
$dane_1[] = $_POST[’ilosc’];
$dane_1[] = $_POST[’uznana’];
$dane_1[] = $_POST[’nieuznana’];
$dane_1[] = $_POST[’powod’];
$ile = count($dane_1[0])-1;
for ($i = 0; $i <= $ile; $i++)
{
$x = $i;
$wartosci[$i] = "('$dane_0[nr_reklamacji]','$dane_0[data_dostawy]',$dane_0[kontrahent]".','.$dane_1[0][$i].','.$dane_1[1][$i].','.$dane_1[2][$i].','.$dane_1[3][$i].','.$dane_1[4][$i].')';
}
$sql = "INSERT INTO `reklamacje`.`protokoly` ";
// implode keys of $array…
$sql .= " (`".implode("`, `", array_keys($_POST))."`)";
// implode values of $array…
$sql .= " VALUES ".implode(",", $wartosci)." ";
$insert = mysql_query($sql) or die(mysql_error() );
if($insert =='1')
{
header('Location: drukuj.php?nr_reklamacji='.$dane_0['nr_reklamacji'].'&komunikat=ok');
exit;
}
else
{
print'’.$komunikat[11].”;
exit;
}
Cześć,
od pewnego czasu pracuję nad własnym systemem zamówień, wystawiania faktur itp.
Szukałem rozwiązania które jest tu zamieszczone i bardzo za nie dziękuję.
W Java S. jestem bardzo początkujący mimo, że już nie taki młody 🙂
Czy zadane na początku w sekcji linki do zewnętrznych plików są „pewne”?
Czy może się zdarzyć tak, że kiedyś nie zostaną odnalezione itp?
Czy gdzieś można poczytać co z tych plików jest wykorzystywane na poziomie tego kodu?
Trochę się zastanawiałem dlaczego tam jest textarea 😉
Jakby ktoś miał wątpliwości, to można schować to również w divie, ale linijkę:
var szablonWiersza = jQuery.format($(„#szablon”).val());
trzeba zamienić na:
var szablonWiersza = jQuery.format($(„#szablon”).html());
remove() jest zrobione troche bez sensu, wlasnie skorzystalem z pomyslu ale przerabiam 😉
zrobilbym clone() zamiast jakiegos nadmiarowego i chowanego textarea… (pozniej mozesz pojechac jQ i usunac value czy numer itd), odpada dodatkowa biblioteka validate czy jakos tak ktora sam musialem zaciagnac a juz i tak mam ich duzo za duzo.
remove – $(this).parent().parent().remove() usunie „this” wiersz i to jest uniwersalne (przy tym html) bo sie nie babrzesz pozniej z licznikami w szablonie do edycji tylu nazwisk, dodajesz latwo ale pozniej chce miec mozliwosc edycji tego!
Do tego hmm moze i samo bedzie dbac (przegladarka, oby wszystkie!) o odpowiednia numeracje (usuwajac 5-ty i 7-y z 10 elementow mamy „kaszane” w numeracji jak juz wyzej zauwazyli) ale z lenistwa nie chce mi sie tego sprawdzic 😉
Tak wiec cos ala clone, lepszy remove ale to juz do doszlifowania wg potrzeb.
pozdrawiam.
Rozwiązanie b. pomocne ale przykład nie działa. Brak reakcji na kliknięcie zielonego+
http://antczak.org/source/dynamic_forms/
Czemu to nie działa? Ani w przykładzie pokazanym ani w plikach które pobrałem?
Poprawione. Dziękuję i pozdrawiam.
Wykorzystałem ten kod i wszystko działa doskonale. Jestem pod wrażeniem, jest to coś czego szukałem i będę wykorzystywał. Mam tylko mały problem, mianowicie pozycje formularza się tworzą, ale gdy dodałem do formularza listę rozwijana(dane są pobierane z bazy danych z tabeli produkty), to produkty są wyświetlane tylko w pierwszej linii, a potem po dodaniu kolejnych już nie. Nie wiem jak sobie z tym poradzić bardzo proszę o pomoc.
Oto fragment kodu:
{0}.
<?php
$zapytanie = mysql_query ("SELECT nazwa,id_services FROM mw_services ORDER BY nazwa DESC");
echo '’;
while($option = mysql_fetch_assoc($zapytanie)) {
echo ”.$option[’nazwa’].”;
}
echo ”;
?>
Przepraszam poprzednio źle skopiowało kod
Zgłoszenie Niezgodności/Deviation Notice
a
{
color: #0000FF;
text-decoration: underline;
}
a:visited
{
color: #800080;
}
a:active
{
color: #FF0000;
}
a:hover
{
color: #0000FF;
text-decoration: underline;
}
#wb_Form1
{
background-color: #FAFAFA;
border: 0px #000000 solid;
}
#wb_Text1
{
background-color: transparent;
border: 0px #000000 solid;
padding: 0;
text-align:center;
}
#wb_Text1 div
{
text-align: left;
}
#Combobox1
{
border: 2px #A9A9A9 solid;
background-color: #FFFFFF;
color: #000000;
font-family: Arial;
font-size: 13px;
}
#jQueryDatePicker1
{
border: 1px #A9A9A9 solid;
background-color: #FFFFFF;
color :#000000;
font-family: Arial;
font-size: 13px;
text-align: left;
vertical-align: middle;
}
.ui-datepicker
{
font-family: Arial;
font-size: 13px;
z-index: 1003 !important;
display: none;
}
#wb_Text2
{
background-color: #DCDCDC;
border: 1px #000000 solid;
padding: 0;
text-align: left;
}
#wb_Text2 div
{
text-align: left;
}
#wb_Text3
{
background-color: #DCDCDC;
border: 1px #000000 solid;
padding: 0;
text-align: left;
}
#wb_Text3 div
{
text-align: left;
}
#Editbox1
{
border: 2px #A9A9A9 solid;
background-color: #FFFFFF;
color :#000000;
font-family: Arial;
font-size: 13px;
text-align: left;
vertical-align: middle;
}
#wb_Text4
{
background-color: #DCDCDC;
border: 1px #000000 solid;
padding: 0;
text-align: left;
}
#wb_Text4 div
{
text-align: left;
}
#Editbox2
{
border: 2px #A9A9A9 solid;
background-color: #FFFFFF;
color :#000000;
font-family: Arial;
font-size: 13px;
text-align: left;
vertical-align: middle;
}
#wb_Text5
{
background-color: #DCDCDC;
border: 1px #000000 solid;
padding: 0;
text-align: left;
}
#wb_Text5 div
{
text-align: left;
}
#Editbox3
{
border: 2px #A9A9A9 solid;
background-color: #FFFFFF;
color :#000000;
font-family: Arial;
font-size: 13px;
text-align: left;
vertical-align: middle;
}
#wb_Text6
{
background-color: #DCDCDC;
border: 1px #000000 solid;
padding: 0;
text-align: left;
}
#wb_Text6 div
{
text-align: left;
}
#Editbox4
{
border: 2px #A9A9A9 solid;
background-color: #FFFFFF;
color :#000000;
font-family: Arial;
font-size: 13px;
text-align: left;
vertical-align: middle;
}
function ValidateForm1(theForm)
{
var regexp;
if (theForm.Combobox1.selectedIndex < 0)
{
alert("Proszę wybrać/ Please choose");
theForm.Combobox1.focus();
return false;
}
regexp = /^[-+]?\d*\.?\d*$/;
if (!regexp.test(theForm.Editbox3.value))
{
alert("PO number must be 10 signs long");
theForm.Editbox3.focus();
return false;
}
if (theForm.Editbox3.value == "")
{
alert("PO number must be 10 signs long");
theForm.Editbox3.focus();
return false;
}
if (theForm.Editbox3.value.length = && theForm.Editbox3.value <= ))
{
alert("PO number must be 10 signs long");
theForm.Editbox3.focus();
return false;
}
return true;
}
$(document).ready(function()
{
var jQueryDatePicker1Opts =
{
dateFormat: 'mm/dd/yy’,
changeMonth: false,
changeYear: false,
showButtonPanel: false,
showAnim: 'show’
};
$(„#jQueryDatePicker1”).datepicker(jQueryDatePicker1Opts);
$(„#jQueryDatePicker1”).datepicker(„setDate”, „new Date()”);
$(„#jQueryDatePicker1”).datepicker(„option”, $.datepicker.regional[’pl’]);
});
$(document).ready(function(){
// tworzymy zmienną i, wykorzystamy ją do identyfikowania wierszy
// ustawiamy jej wartość 2, ponieważ jeden wiersz jest już w tabeli
var i = 2;
// tworzymy zmienną szablonWiersza i wczytujemy do niej szablon z textarea #szablon
var szablonWiersza = jQuery.format($(„#szablon”).val());
// tworzymy funkcję, która dodaje szablon do właściwej tabeli
function dodajWiersz() {
var ii = i++;
// dodajemy wiersz do właściwej tabeli
$(„#listaOsob tbody”).append(szablonWiersza(ii));
// Nowy wiersz jest już widoczny.
// Każdy element nowego wiersza posiada swoje id
// tr na id: wiersz_2, ikona minus ma id: usunOsobe_2
// Do nowo dodanej ikony – dodajmy zdarzenie.
// W momencie kliknięcia minusa, wiersz zostanie usunięty.
$(„#usunOsobe_” + ii).click(function(){
$(„#wiersz_” + ii).remove();
});
}
// do przycisku #dodajOsobe dodajemy zdarzenie,
// kliknięcie na przycisk plus wywoła fukcję dodajWiersz
$(„#dodajOsobe”).click(dodajWiersz);
});
Rodzaj zamówienia:PO type
Eksperyment/Experimental
Produkcyjne/Production
Nowe Uruchomienie/New Development
Zgłoszenie niezgodnościDeviation Notification
Nazwa dostawcy:Vendor Name
Kod Dostawcy/Nr Kolejny/Rok:Vendor Code/Sequence Number/Year
Zamówienie numer:PO number
Kupiec:Buyer Name
{0}/{0}
<button id="usunOsobe_{0}" alt="usun">
Item
Położenie na rysunkuB/P Location
Ilość sztuk niezgodnychQuantity
+/-
1/{0}
+
Wydrukuj/Print
czesc, pytanie banalne, jak poprawic numeracje po usunięciu wiersza? dziekuje z góry
No ale ten przyklad nie działa z FF
Czy w kodzie jest jakiś błąd ? nie potrafię zmusić go do działania 🙁