HTML-Kreuztabelle 2: Werte bearbeiten

Lies diesen Artikel und viele weitere mit einem kostenlosen, einwöchigen Testzugang.

Im Beitrag „Kreuztabelle per HTML“ haben wir gezeigt, wie Sie die Daten einer Tabelle in Form einer Kreuztabelle ausgeben können. Das ist natürlich auch per Kreuztabellenabfrage möglich, aber wir haben in diesem Fall das Webbrowser-Steuerelement mit einer entsprechenden HTML-Seite verwendet. Der Hintergrund ist, dass wir so Funktionen zum direkten Bearbeiten der Einträge hinzufügen können – vorausgesetzt, dass die Kreuztabelle nur die Werte einer Kombination anzeigt und nicht etwa Domänenfunktionen wie Summen oder Mittelwerte. Wir wollen also die bereits vorhandene Darstellung noch um Funktionen zum Bearbeiten sowie zum Hinzufügen neuer Spalten oder Zeilen erweitern.

Ausgangspunkt ist die im bereits genannten Beitrag HTML-Kreuztabelle 1: Basics (www.access-im-unternehmen.de/1161) genannte Ansicht aus Bild 1. Hier wollen wir nun folgende Erweiterungen hinzufügen:

Diese Ansicht soll um Bearbeitungsfunktionen erweitert werden.

Bild 1: Diese Ansicht soll um Bearbeitungsfunktionen erweitert werden.

  • Wenn der Benutzer auf einen der Einträge für die Zahlenwerte klickt, soll er den angezeigten Wert bearbeiten können. Das Verlassen der Zelle soll den Wert in der zugrunde liegenden Tabelle speichern.
  • In der oberen Zeile mit den Spaltenköpfen wollen wir rechts ein Plus-Zeichen anzeigen, mit dem der Benutzer eine neue Spalte hinzufügen kann.
  • In der linken Zeile mit den Zeilenköpfen wollen wir unten ebenfalls ein Plus-Zeichen unterbringen, das per Mausklick die Möglichkeit zum Anlegen einer neuen Zeile ermöglicht.
  • Die leeren Zellen wollen wir auf irgendeine Weise anklickbar machen, damit der Benutzer dort auch Werte eintragen kann.
  • Ein Doppelklick auf einen der Werte soll den entsprechenden Datensatz der Tabelle tblMaterialpreise in einem eigenen Formular öffnen.

Tabellenelemente mit ID und der Eigenschaft contentEditable versehen

Um die erste Aufgabe zu erfüllen, müssen wir die unter den Spaltenüberschriften und rechts von den Zeilenüberschriften befindlichen Zellen, die bereits Werte anzeigen, mit eindeutigen Markierungen versehen. Da jeder Datensatz einen Wert der Tabelle tblMaterialpreise repräsentiert, können wir hier mit dem Primärschlüsselwert der entsprechenden Datensätze arbeiten. Dieser landet dann in Form des Attributs id in einem div-Element, welches im td-Element steckt und den eigentlichen Zahlenwert einschließt. Außerdem wollen wir hier die Grundlage für die Bearbeitbarkeit der Elemente legen, indem wir für das gleiche div-Element das Attribut contentEditable auf den Wert true einstellen:

<table>
   <tbody>
     <tr>
       <td class="columnHeader rowHeader"></td>
       <td class="columnHeader">700</td>
       <td class="columnHeader">800</td>
       ...
     </tr>
     <tr>
       <td class="rowHeader">800</td>
       <td>
          <div id="10" contenteditable="true">12</div>
       </td>
       <td>
          <div id="7" contenteditable="true">64</div>
       </td>
       <td>
          <div id="6" contenteditable="true">72</div>
       </td>
       ...
     </tr>
     ...
   </tbody>
</table>

Diese änderungen erreichen wir leicht, indem wir die Prozedur KreuztabelleErstellen, welche das HTML-Dokument aus objDocument mit dem HTML-Inhalt füllt. Allerdings führen wir gleich noch ein paar weitere Schritte durch, welche die Funktion zum Bearbeiten der Inhalte vervollständigen.

änderung der Prozedur KreuztabelleErstellen

Die neue Version der Prozedur KreuztabellenErstellen finden Sie in Listing 1.

Private Sub KreuztabelleErstellen()
     ...
     Dim objDivWrapper As clsDivWrapper
     Dim objDiv As MSHTML.HTMLDivElement
     Set colDivs = New Collection
     ...
     Set rstZeilen = db.OpenRecordset("SELECT DISTINCT Breite FROM tblMaterialpreise ORDER BY Breite", dbOpenDynaset)
     Do While Not rstZeilen.EOF
         Set objRow = objTable.insertRow
         rstSpaltenkoepfe.MoveFirst
         Set objCell = objRow.insertCell
         objCell.innerText = rstZeilen!Breite
         objCell.className = "rowHeader"
         Set rstWerte = db.OpenRecordset("SELECT MaterialpreisID, Preis, Hoehe FROM tblMaterialpreise WHERE Breite = " _
             & rstZeilen!Breite & " ORDER BY Hoehe", dbOpenDynaset)
         Do While Not rstWerte.EOF
             If rstSpaltenkoepfe!Hoehe = rstWerte!Hoehe Then
                 Set objCell = objRow.insertCell
                 Set objDiv = objDocument.createElement("Div")
                 objDiv.id = rstWerte!MaterialpreisID
                 objDiv.contentEditable = "true"
                 objCell.appendChild objDiv
                 objDiv.innerText = rstWerte!Preis
                 Set objDivWrapper = New clsDivWrapper
                 Set objDivWrapper.Div = objDiv
                 colDivs.Add objDivWrapper
                 rstWerte.MoveNext
             Else
                 Set objCell = objRow.insertCell
             End If
             rstSpaltenkoepfe.MoveNext
         Loop
         rstZeilen.MoveNext
     Loop
End Sub

Listing 1: Ausschnitt aus der Prozedur KreuztabelleErstellen

Hier verwenden wir ein Collection-Objekt, das wir im allgemeinen Teil des Klassenmoduls des Formulars deklarieren wollen:

Dim colDivs As Collection

Die Prozedur Kreuztabellen deklariert dann auch ein paar neue Elemente. Das erste ist ein Objekt des Typs clsDivWrapper. Was das ist, erläutern wir gleich weiter unten:

Dim objDivWrapper As clsDivWrapper

Außerdem deklarieren wir eine Variable des Typs HTML-Div-Element namens objDiv:

Dim objDiv As MSHTML.HTMLDivElement

Damit das Collection-Objekt einsatzbereit ist, füllen wir es mit einer neuen Collection:

Set colDivs = New Collection

Die folgenden Abschnitte sind unverändert, daher haben wir sie im Listing nicht abgebildet. Die Magie bereiten wir in der inneren der beiden verschachtelten Do While-Schleifen vor. Hier erstellen wir mit der createElement-Methode von objDocument zunächst ein neues Objekt des Typs HTMLDivElement und referenzieren es mit der Variablen objDiv. Dann stellen wir dessen Attribut id auf den Primärschlüsselwert des aktuellen Datensatzes des Recordsets rstWerte ein sowie das Attribut contentEditable auf den Wert true.

Außerdem hängen wir das frisch erstellte Element mit der appendChild-Methode an das Objekt objDiv an.

Set objDiv = objDocument.createElement("Div")
objDiv.id = rstWerte!MaterialpreisID
objDiv.contentEditable = "true"
objCell.appendChild objDiv

Was dann geschieht, erklärt sich erst später vollständig, wenn wir die Klasse clsDivWrapper erläutern, die einen Großteil der Arbeit erledigt: Wir erstellen ein neues Objekt auf Basis der Klasse clsDivWrapper und weisen dessen Eigenschaft Div das HTMLDivElement-Objekt aus objDiv zu. Danach hängen wir das Objekt objDivWrapper an die Auflistung colDivs an:

objDiv.innerText = rstWerte!Preis
Set objDivWrapper = New clsDivWrapper
Set objDivWrapper.Div = objDiv
colDivs.Add objDivWrapper

Damit kommen wir zum Inhalt und der Funktion der Klasse clsDivWrapper.

In der Prozedur KreuztabelleErstellen haben wir die Vorbereitung erschaffen und zu jedem Div-Element mit einem Wert des Feldes Preis der Tabelle tblMaterialpreise eine Instanz der Klasse clsDivWrapper erstellt und diese in der global deklarierten Variablen colDivs gespeichert. Diese Klasse nun soll die fehlende Aufgabe übernehmen, nämlich das Speichern geänderter Werte in der Kreuztabelle.

Dazu legen Sie ein neues Klassenmodul namens clsDivWrapper an, dem Sie zunächst zwei Deklarationen hinzufügen:

Private WithEvents m_Div As MSHTML.HTMLDivElement
Private strText As String

Die Variable strText soll den aktuell in der Zelle der Kreuztabelle befindlichen Text speichern, die Variable m_Div nimmt den von der Prozedur KreuztabelleErstellen übergebenen Verweis auf das Objekt des Typs HTMLDivElement auf. Die Deklaration haben wir um das Schlüsselwort WithEvents ergänzt, da wir für dieses Objekt ein oder mehrere Ereignisprozeduren implementieren wollen.

Damit wir der Klasse wie in der KreuztabelleErstellen programmiert den Verweis auf objDiv übergeben können, definieren wir eine Property Set-Prozedur, die wie folgt aussieht:

Public Property Set Div(Div As MSHTML.HTMLDivElement)
     Set m_Div = Div
End Property

Nun müssen wir aus den verschiedenen Ereignissen, die für das Element m_Div zur Verfügung stehen, die für uns interessanten herausfinden.

Die Ereignisse finden Sie, wenn Sie im linken Kombinationsfeld des Klassenmoduls den Eintrag m_Div auswählen und dann das rechte Kombinationsfeld öffnen (siehe Bild 2).

Implementieren der Ereignisse für m_Div

Bild 2: Implementieren der Ereignisse für m_Div

Wir entscheiden uns für die folgenden beiden Ereignisse:

  • onkeyup: Dieses Ereignis wird ausgelöst, wenn der Benutzer eine Taste der Tastatur loslässt, also soeben ein Zeichen eingegeben oder gelöscht hat.
  • onfocusout: Dieses Ereignis wird ausgelöst, wenn das div-Element den Fokus verliert.

Das Ereignis onkeyup nutzen wir, um nach der Eingabe eines jeden Zeichens den aktuellen Inhalt des div-Elements, den wir über die Eigenschaft textContent ermitteln, in die Variable strText schreiben:

Private Sub m_Div_onkeyup()
     strText = m_Div.textContent
End Sub

Das Ereignis onfocusout soll nach dem Verlassen des Feldes den aktuellen Inhalt in den entsprechenden Datensatz der Tabelle tblMaterialpreise eintragen.

Den aktuellen Inhalt können wir nun der Variablen strText entnehmen, allerdings müssen wir vor dem ändern des Datensatzes natürlich prüfen, ob der Wert auch tatsächlich geändert wurde. Nur in diesem Fall enthält strText nämlich einen Inhalt – anderenfalls ist es leer:

Private Sub m_Div_onfocusout()
     Dim db As DAO.Database
     Set db = CurrentDb
     If Len(strText) > 0 Then
         db.Execute "UPDATE tblMaterialpreise SET Preis  = " & strText & " WHERE MaterialpreisID = "  & m_Div.id, dbFailOnError
     End If
End Sub

Mit den aktuellen Einstellungen können Sie nun nach dem erneuten öffnen des Formulars die Werte der vorhandenen Datensätze ändern (siehe Bild 3).

änderungen an den Daten werden direkt in die Tabelle geschrieben

Bild 3: änderungen an den Daten werden direkt in die Tabelle geschrieben

Fehlende Datensätze anlegen

Wenn es in einer Zeile/Spalte beispielsweise nur einen Wert gibt wie im Fall der rechten, unteren Zelle mit dem Wert 1450, kann der Benutzer die fehlenden Werte aktuell noch nicht durch einfaches Anklicken der leeren Felder hinzufügen.

Dazu müssten wir bei den leeren Feldern statt eines leeren tr-Elements (<td></td>) auch ein div-Element hinzufügen und dieses beispielsweise mit einem Minus-Zeichen als Symbol für den fehlenden Wert hinterlegen. Dafür passen wir einfach den Else-Teil der If…Then-Bedingung innerhalb der inneren Do While-Schleife wie folgt an:

Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...

Testzugang

eine Woche kostenlosen Zugriff auf diesen und mehr als 1.000 weitere Artikel

diesen und alle anderen Artikel mit dem Jahresabo

Schreibe einen Kommentar