Zeilen-Duplizierer mit AutoComplete Felder nutzen

Ich bekam heute in einem Kommentar die Frage gestellt, ob mein Zeilen-Duplizierer aus dem Beitrag: Einfacher Zeilen-Duplizierer mit Prototype auch auf Feldern funktioniert, die die Scriptaculous Ajax.AutoCompleter Funktion verwenden.

Ich nutze den Duplizierer selbst in einem Formular, das solche Felder enthält. Meine erste Fassung entsprach dabei dem Einzeiler aus dem vorherigen Beitrag. Das Problem dabei ist allerdings, dass die AutoCompleter Funktion beim Erzeugen mit dem Felder verknüpft wird, auf das sie angewendet werden soll. Wenn man nun dieses Feld kopiert wird nicht eine neue Instanz des Ajax.AutoCompleter erzeugt, sondern die Referenz auf das alte Feld mit kopiert. Ein Eintrag im neu erzeugten Feld aktiviert also die AutoCompleter Funktion des vorherigen Feldes.

Um dieses Problem zu lösen hatte ich die erweiterte Funktion mit den beiden Callback-Funktionen erstellt. Damit ist es möglich die Referenz zum vorherigen Feld zu lösen und eine neue Instanz des Ajax.AutoCompleter nach dem Erzeugen der Kopie zu erstellen. Hier aber zuerst noch einmal die Funktion selbst. Ich habe mittlerweile auch eine Option zum Fokussieren des ersten Feldes in der Kopie hinzugefügt:

function addRow(selector, options){
	var lastRow = $$(selector).last();
	var newRow = lastRow.cloneNode(true);
	if (options.reset) {
		newRow.select('select', 'textarea', 'input').each(function(elm){
			elm.clear().checked = '';
		});
	}
	if(options.beforeInsert) options.beforeInsert(newRow);
	lastRow.insert({'after': newRow});
	if(options.afterInsert) options.afterInsert(newRow);
	
	if (options.focusFirst) {
		newRow.select('input', 'select', 'textarea').first().focus();
	}
}

Um die Ajax.Autocompleter Funktion nutzen zu können benötigt jedes Feld eine eindeutige ID. Nehmen wir also an, wir haben folgendes erstes Feld für unser Formular (hier ein Beispiel aus dem Artikel: Google Maps Suggest – Adress-Autovervollständigung mit Scriptaculous):

<form id="addressform" action="address_save.php" class="niceform">
	<fieldset>
		<legend>Choose an address</legend>
		<dl>
			<dt><label for="address">Address:</label></dt>
			<dd><input type="text" id="address" name="address[]" style="width: 300px;" /></dd>
		</dl>
	</fieldset>
	<fieldset>
		<input type="submit" value="Send!" />
	</fieldset>
</form>			
<div id="adresse_choices" class="autocomplete"></div>
<script type="text/javascript">
	new Ajax.Autocompleter('address', 'adresse_choices', 'get_addresses.php');
</script>

Wichtig sind hierbei natürlich die eckigen Klammern am Ende des Name Attributs, damit auch alle kopierten Werte mitübertragen werden. Damit wir einer Kopie dieses Feldes eine neue Instanz hinzufügen können, müssen wir beim Kopieren auch die ID verändern. Dazu schreiben wir uns eine kleine Callback-Funktion, die genau das erledigt:

function activateAutocomplete(newRow){
	var addressInput = $A(newRow.select('input')).first();
	addressInput.id += 1;
	addressInput.stopObserving();
	new Ajax.Autocompleter(addressInput.id, 'adresse_choices', 'get_addresses.php');
}

In der 2. Zeile selektieren wir das erste Element innerhalb der neuen Zeile (wir nehmen hier also an, dass es sich dabei um das AutoCompleter Feld handelt). Diesem fügen wir in der 3. Zeile zusätzlich zur ID eine 1 hinzu. Es entstehen also Felder nach dem Muster "address1", "address11" usw. Das reicht aus um die Felder genau zu referenzieren.
In der 4. Zeile kommt die schon angesprochene Funktion zum Einsatz, die das Autocomplete für das Feld deaktiviert. Zu guter Letzt erzeugen wir eine neue Instanz des Ajax.Autocompleter. Wir können dabei als zweiten Parameter dasselbe DIV Element für die Vorschläge verwenden, da es ja nicht möglich ist, gleichzeitig in zwei Felder etwas einzutippen. Das DIV wird automatisch durch Scriptaculous unter das Feld positioniert, in dem es verwendet wird.

Damit das ganze nun auch dupliziert werden kann, fügen wir noch einen entsprechenden Link ein. Dieser muss dann die Callback-Funktion als zweiten Parameter in der Funktion erhalten. Ich verwende hier ein SPAN Element mit einem onclick Event. Es geht aber auch mit einem gewöhnlichen Link:

<span class="add" onclick="addRow('#addressform dl', {reset : true, afterInsert: activateAutocomplete})">Adresse hinzufügen</span>

Es wird durch den Selektor "#addressform dl" die gesamte Definition inklusive des Label kopiert. Ihr könnt aber auch eine andere HTML Struktur wie z.B. eine unsortierte Liste verwenden.

Das war auch schon alles. Das Beispiel könnt ihr euch hier ansehen und auch den Quellcode dazu runterladen:

Beispiel Download

Ich hoffe, dass euch die erweiterte Anwendung gefallen hat und vielleicht auf neue Ideen gebracht hat. Wenn ihr eine andere Anwendung kennt, bei der Ihr nicht weiterkommt würde ich mich wie immer über einen Kommentar sehr freuen.

Veröffentlicht von

Bernhard ist fest angestellter Webentwickler, entwickelt in seiner Freizeit Plugin, schreibt in seinem Blog über WordPress und andere Themen, treibt sich gerne bei den WP Meetups in Berlin und Potsdam herum und läuft nach Feierabend den ein oder anderen Halbmarathon.

5 Kommentare » Schreibe einen Kommentar

    • Das funktioniert sehr ähnlich zum Hinzufügen der Zeile. Man wählt die letzte Zeile aus und fügt darauf die remove() Funktion aus. Der Quellcode bezogen auf mein Beipspiel könnte dann wie folgt aussehen:

      <span class="remove" onclick="$$('#addressform dl').last().remove()">Adresse entfernen</span>
      

      Hierbei ist dann aber darauf zu achten, dass die Zeile wirklich komplett gelöscht wird. Daher geht auch der Inhalt verloren und ist nicht durch ein erneutes Hinzufügen wiederherzustellen.

      Ich hoffe, dass das der Code war, den du benötigst.

    • super, hat mir sehr geholfen!! Nur ein kleines Problem

      Ajax.Autocompleter is not a constructor

      ist die Fehlermeldung, obwohl alles geht in allen Browsern.
      Die 2. Kleinigkeit, wenn ich z.B. 4 Zeilen habe und es poste bleibt nach dem posten nur eine Zeile anstatt 4 Zeilen stehen.

      • Das mit dem Fehler sehe ich mir morgen mal an. Den zweiten Fehler habe ich aber nicht ganz verstanden. Könntest du mir nochmal genau erklären, wo das Problem auftritt und was das gewünschte Ergebnis wäre. Eventuell könnten mir auch ein paar Screenshots dazu weiterhelfen.

Schreibe einen Kommentar

Pflichtfelder sind mit * markiert.