Webseiten und ZubehörProgrammierung UmsetzungPflege Backups BetreuungJoomla-Spezialist

JoomlaSchnipsel

In einem Forum stolperte ich über die Frage, wie man dynamisch die Reihenzahl ermitteln kann, die von einem Benutzer in einem Formularfeld vom Typ repeatable angelegt wurde. So, dass ein anderes Feld des Formulars an diese Information rankommt, nachdem das Repeatable-Feld bearbeitet und geschlossen wurde. Da musste ich mich schrittweise hinarbeiten und habe zwei Varianten gefunden.

15.09.2015: Das Repeatable-Feld wird in Joomla 3.6.0, als deprectaed (veraltet) gekennzeichnet und dann wohl auch irgendwann aus dem Core entfernt werden. Es hat sich als schwer handhabbar für Programmierer herausgestellt, was ich bestätigen kann. Die Entwicklung eines neuen Formular-Feldes als Ersatz kann man hier einsehen: https://github.com/joomla/joomla-cms/pull/7829. Das neue Feld namens Subform steht ab 3.6.0 im Core zur Verfügung.

Ich bin mir sicher, das geht noch viel effizienter als folgend beschrieben. Ein erster Ansatz.

Wenn man ein Feld Repeatable in sein Formular per XML-Datei einbindet, kommen zwei Dateien in's Spiel.

/libraries/joomla/form/fields/repeatable.php, in dem das Formularfeld generiert wird.

Darin findet sich u.a. diese Zeile, um die JavaScript-Datei /media/system/js/repeatable.js zu laden.

JHtml::_('script', 'system/repeatable.js', true, true);

Im selben Ordner (zum Glück) die nicht minifizierte Version repeatable-uncompressed.js der Datei.

Darin, vorbildlich, im Kopfkommentar eine Übersicht der JS-JQuery-Events, die das Repeatable-Feld verwendet.

 * Events:
 *   $('input.form-field-repeatable')
 *   .on('weready', function(e){
 *    // fires when JRepeatable initialized
 *   })
 *   .on('prepare-template', function(e, template){
 *    // fires when row template initialized
 *   })
 *   .on('prepare-modal', function(e, modal){
 *    // fires when modal container initialized
 *   })
 *   .on('row-add', function(e, row){
 *    // fires when new row added
 *   })
 *   .on('row-remove', function(e, row){
 *    // fires before row removing
 *   })
 *   .on('value-update', function(e, value){
 *    // fires before when value in hidden input was updated
 *   });

Aus Faulheitsgründen setze ich das Formfeldbeispiel (Überschrift Example XML Definition) der Seite https://docs.joomla.org/Repeatable_form_field_type in eine Core-Datei ein, natürlich auf einer Joomla-Testseite, die ich kaputt machen darf.

/administrator/components/com_content/models/forms/article.xml

Und zwar innerhalb des  attribs-fields und dort iinerhalb des basic-fieldsets, damit ich das Feld automatisch/sofort in der Artikelbearbeitung im Tabulator Optionen zu sehen bekomme und die Werte auch gleich mit dem Beitrag mitgespeichert werden, ohne dass ich das alles selbst aufsetzen muss.

<fields name="attribs" label="COM_CONTENT_ATTRIBS_FIELDSET_LABEL">
 <fieldset name="basic" label="COM_CONTENT_ATTRIBS_FIELDSET_LABEL">

  <!--EINFÜGEN START-->
  <field name="list_templates" type="Repeatable" icon="list"
   description="PLG_TINY_FIELD_TEMPLATE_FIELD_ELEMENTS_DESC"
   label="PLG_TINY_FIELD_TEMPLATE_FIELD_ELEMENTS_LABEL"
   default="{'template':['Layout','Simple snippet'],'location':['layout1.html','snippet1.html'],'description':['HTMLLayout','Simple HTML snippet']}"
   onchange="alert('hallo');">
   <fields name="params">
   <fieldset hidden="true" name="list_templates_modal" repeat="true">
    <field name="template" type="text" size="30"
     label="PLG_TINY_FIELD_TEMPLATE_FIELD_NAME_LABEL" />
    <field name="location" type="filelist" size="30"
     label="PLG_TINY_FIELD_TEMPLATE_FIELD_LOCATION_LABEL"
     description="PLG_TINY_FIELD_TEMPLATE_LOCATION_DESC"
     directory="media/editors/tinymce/templates"
     exclude="index.html"
     hide_default="true"
     hide_none="true" />
    <field name="description" type="textarea" size="30"
     label="PLG_TINY_FIELD_TEMPLATE_FIELD_DESCRIPTION_LABEL"/>
   </fieldset>
   </fields>
  </field>
  <!--EINFÜGEN ENDE-->

Wenn ich jetzt einen Beitrag im Backend öffne, habe ich mein neues modales Repeatable-Feld. Ein schneller Test zeigt, dass mein in Zeile 9 hinzugefügtes onchange="alert('hallo');" nichts macht. Ich sehe keinen JavaScript-Alert beim Ändern. Halt mal probiert.

javascript alert hallo
javascript alert hallo

Das Repeatable-Feld feuert also keine Onchange-Events, wie viele andere Formularfelder das tun.

Repeatable-form-field-type.jpg
Repeatable-form-field-type.jpg
Repeatable-form-field-type-2.jpg
Repeatable-form-field-type-2.jpg

Mit den obigen Events, die ich in der repeatable.js gefunden hatte, setze ich mir ein eigenes JavaScript zusammen, dass auf Formularfeld-Änderungen (hoffentlich) reagiert. Ich setze es am Ende der Datei /administrator/components/com_content/views/article/tmpl/edit.php ein, damit es zusammen mit dem Beitrags-Formular geladen wird.

<script type="text/javascript">
(function($){
 $('input.form-field-repeatable')
 
 .on('weready', function(e){
  alert('// fires when JRepeatable initialized');
 })
 
 .on('prepare-template', function(e, template){
  alert('// fires when row template initialized');
 })
 
 .on('prepare-modal', function(e, modal){
  alert('// fires when modal container initialized');
 })
 
 .on('row-add', function(e, row){
  alert('// fires when new row added');
 })
 
 .on('row-remove', function(e, row){
  alert('// fires before row removing');
 })
 
 .on('value-update', function(e, value){
  alert('// fires before when value in hidden input was updated');
 });

})(jQuery);
</script>

Danach lege ich los und spiele mit dem Formularfeld in der Beitragsbearbeitung rum, lege Reihen an, lösche sie, fülle Felder etc. pp. um zu sehen, wann welches Alert erscheint, wann welches on-Dingsbums-Event relevant ist.

Es zeigt sich, dass das Event value-update das ist, das gefeuert wird, wenn ich das Formularfeld nach gemachten Änderungen speichere und schließe. Da setze ich an und kürze das Script erst mal ob der nervigen Alerts.

<script type="text/javascript">
(function($){
 $('input.form-field-repeatable')
  
 .on('value-update', function(e, value){
  alert('// fires before when value in hidden input was updated');
 });
 
})(jQuery);
</script>

Der Rest war Forschen (teils im HTML-Quelltext, teils im Code der o.g. /libraries/joomla/form/fields/repeatable.php) bis ich das erste provisorische Script hatte, das mir die Reihenzahl beim Speichern des Repeatable-Feldes von außen abrufbar ablegt.

<script type="text/javascript">
(function($){
 $('input.form-field-repeatable')

 .on('value-update', function(e, value){
  
  var hauptContainer = $(this).attr("data-container");
  
  alert(hauptContainer); // String #jform_attribs_list_templates_container
 
  var $dataContainer = $(hauptContainer);
  
  alert($dataContainer); // Container-Object, Container-Element
  
  var einzelneReihe = $(this).attr("data-repeatable-element");
  
  alert(einzelneReihe); // String table tbody tr
  
  var $reihen = $dataContainer.find(einzelneReihe);
  
  alert($reihen); // Alle-Reihen-Object, Array
  
  var reihenAnzahl = $reihen.length;
  
  alert(reihenAnzahl); // String/Number
  
  $(this).attr("meinereihenanzahl", reihenAnzahl);
  
  alert($(this).attr("meinereihenanzahl")); // String/Number
  
 });

})(jQuery);
</script>

Inspiziere ich das Formfeld anschließend bspw. mit Firebug, sehe ich, dass tatsächlich das <INPUT> ein neues Attribut meinereihenanzahl in Zeile 16 bekommen hat.

<input type="hidden"
 data-input="#jform_attribs_list_templates"
 data-maximum="999"
 data-bt-modal-save-data="button.save-modal-data"
 data-bt-modal-close="button.close-modal"
 data-bt-modal-open="#jform_attribs_list_templates_button"
 data-bt-remove="a.remove"
 data-bt-add="a.add"
 data-repeatable-element="table tbody tr"
 data-modal-element="#jform_attribs_list_templates_modal"
 data-container="#jform_attribs_list_templates_container"
 class="form-field-repeatable"
 value="{&quot;template&quot;:[&quot;Layout&quot;,&quot;sdas&quot;,&quot;qweqwe&quot;],&quot;location&quot;:[&quot;layout1.html&quot;,&quot;snippet1.html&quot;,&quot;layout1.html&quot;],&quot;description&quot;:[&quot;HTMLLayout&quot;,&quot;qwewqeqw&quot;,&quot;fdgfrethjgk,jh&quot;]}"
 id="jform_attribs_list_templates"
 name="jform[attribs][list_templates]"
 meinereihenanzahl="3"
/>

An diesen Zeilenzahlwert komme ich dann bspw. aus weiterem JavaScript-Code wie eben üblich in Jquery via id des Repeatable-<INPUT> (Zeile 14).

$("#jform_attribs_list_templates").attr("meinereihenanzahl");

Nichts hindert mich daran, die Reihenzahl auch irgendwoanders zu hinterlegen anstatt obiges Attribut anzulegen, bspw. in einem extra hidden-Formularfeld. Oder weiteren Code hinzuzufügen, der stattdessen andere Formularfelder gleich passend dynamisch manipuliert.

Und, wenn man den HTML-Quelltext oben ansieht, stellt man fest, dass im value-Attribut (Zeile 13) des Repeatable-Felds ebenfalls alle Reihen als JSON-String vorliegen. Könnte man auf die Idee kommen, den ganzen Kokolores oben zu ignorieren und die Reihenzahl so zu ermitteln. (Dran denken, dass JSON-Strings gelegentlich bereinigt werden müssen je nach Eingabe durch den User.)

var reihenValues = JSON.parse($("#jform_attribs_list_templates").val());
var reihenAnzahl = reihenValues[Object.keys(reihenValues)[0]].length;

Hängt einfach davon ab, zu welchem Zeitpunkt ich die aktuelle Reihenzahl benötige. Zweitere Variante hat den Vorteil, dass man es in eine function() packen kann, die bspw. beim Onchange irgendeines anderen Formularelements ausgeführt wird.

Oder man kombiniert, da ja auch die .on('value-update', function(e, value){ , wie man sieht, das value-Attribut mitüberreicht bekommt (nehme ich mal an).

Oder man setzt das Attribut schon beim Initialisieren des Repeatable-Feldes. Gibt ja genug weitere Events.

Wie's beliebt...

JavaScript Formular Joomla XML Erweiterung