Webentwicklung mit script.aculo.us
Kommentare

loadProducts()
Wie Sie bereits wissen, werden die Produktdaten nachträglich in den Client geladen: mit AJAX und JSON. Da Script.aculo.us die für ihre AJAX-Klassen bekannte Prototype-Bibliothek mitbringt,

loadProducts()

Wie Sie bereits wissen, werden die Produktdaten nachträglich in den Client geladen: mit AJAX und JSON. Da Script.aculo.us die für ihre AJAX-Klassen bekannte Prototype-Bibliothek mitbringt, liegt nichts näher, als auf eben diese zurückzugreifen.

function loadProducts() {
  new Ajax.Request(
    "php/getProducts.php", 
    {
      method: 'get', 
      onComplete: function(req) {
        data = req.responseText.parseJSON();
        initShop();
      },
      onError: function() {
        alert("Bei der Datenübertragung ist leider ein Fehler aufgetreten!");
      }
    }
  )
}

Eine neue Instanz von Ajax.Request sorgt für asynchronen Datenfluss zwischen dem Server, der seine Daten durch das PHP-Skript getProducts.php preisgibt, und dem Client, der die erhaltenen Daten in einer anonymen Ereignisbehandlungsmethode für onComplete erhält oder im Fehlerfall onError auslöst. Auch bei der Angabe von Optionen für die Ajax.Request-Instanz kommen Object Literals zum Einsatz. OnComplete übergibt der dazugehörigen Funktion das für die AJAX-Transaktion verwendete XMLHttpRequest-Objekt. Über dessen Eigenschaft responseText gelangt man an das angelieferte Datenmaterial in Form eines Strings. Mit parseJSON(), einer Methode, die der Datei json.js entstammt und das String-Objekt erweitert, wird dieser String in ein JavaScript-Objekt umgewandelt. Ein Blick auf die mögliche Struktur, die getProducts.php ausspuckt, lässt erkennen, welche Beschaffenheit das globale Objekt data im Erfolgsfall und nach Anwendung von parseJSON() haben wird.

{
  "products": [
    {
      "title":"Roter Apfel",
      "price":0.50,
      "id":"apples1"
    },
    {
      "title":"Grüner Apfel",
      "price":0.52,
      "id":"apples2"
    }
  ]
}

Das durch parseJSON() erzeugte Objekt (data) hat zunächst nur eine Eigenschaft: products. Diese Eigenschaft wiederum ist ein Array, das aus zwei Elementen besteht, wobei jedes Element für sich wiederum ein Objekt mit den Eigenschaften title, price und id ist.

initShop()

In der Funktion initShop(), die aufgerufen wird, wenn die AJAX-Engine fehlerfrei gearbeitet hat und die Produktdaten vorliegen, wird das globale data-Objekt ergänzt. Jedes Element des products-Arrays soll die Eigenschaft order erhalten, die mit 0 initialisiert wird. Hier werden später die Bestellmengen für ein Produkt vermerkt. Dazu wird jedes Element von products durch den Iterator each angesprochen, der der Prototype-Bibliothek entstammt. Als Argument erhält er eine Funktion, die für jedes Element des Arrays products aufgerufen und der der Schlüssel und Wert des Array-Elements übergeben wird (Listing 3).

Listing 3
--------------------------------------------------------------------
function initShop() {
  
  // Eigenschaft order bei products-Elementen initialisieren
  data.products.each( 
    function(value, index) {
      data.products[index].order=0;
    }
  )  
  
  // Zähler für eindeutige ids
  itemIndex = 0;
  // Zähler für Artikel im Warenkorb
  itemCount = 0;
 
   // Auto-Completion mit Produktbezeichnern füllen
  new Autocompleter.Local(
    'shop_filter_input', 
    'shop_filter_list', 
    data.products.pluck("title"), 
    {fullSearch: true}
  );

  //Produkte anzeigen, ungefiltert
  showProducts("");
}

In initShop() wird zudem die Auto-Completion-Funktionalität von Script.aculo.us gestartet. Hierbei handelt es sich um die Local-Variante des Autocompleters, da die Daten für die Vorschläge bereits im Client vorliegen. Script.aculo.us hält aber auch eine Variante bereit, die sich die Daten per AJAX eigenständig vom Server holt.

Abb. 2: Autocompleter.Local in Aktion

Als Argumente erwartet Autocompleter.Local (Abb. 2) die id eines Eingabefelds, dessen Funktionalität ergänzt werden soll, ein div-Element, in dem die Auswahlliste möglicher Worte platziert werden soll, die Liste aller möglichen Begriffe in Form eines Arrays und zusätzliche Optionen als Object Literal. Im vorliegenden Fall wird die Option fullSearch auf true gesetzt. Dadurch wird veranlasst, dass der Wert von shop_filter_input nicht nur am Anfang der möglichen Begriffe gesucht wird, sondern auch mittendrin. Bei shop_filter_list handelt es sich um ein ganz normales div-Element, das bei der Benutzung des Autocompleters von eben diesem mit Inhalt gefüllt wird. Dieses Element muss bereits im HTML-Dokument vorhanden sein. Es wird automatisch durch den Autocompleter unsichtbar gemacht und bei Aktivierung direkt unter das Eingabefeld, hier shop_filter_input, platziert. Innerhalb dieses div-Elements werden die Vorschläge in einem ul-Element mit einer entsprechenden Anzahl an li-Elementen untergebracht. Script.aculo.us erzeugt ul- und li-Elemente selbst. Mit CSS können Sie das Erscheinungsbild der Autocompleter-Liste verändern, zum Beispiel folgendermaßen:

#shop_filter_list {
 background-color: #D1FAD1;
}

#shop_filter_list ul {
  list-style: none;
  padding: 2px;
  margin: 0;
}

#shop_filter_list li.selected {
 background: #CCFF33;
 color: #000;
}
showProducts()

Die Funktion showProducts() sorgt dafür, dass nun endlich auch was im Regal erscheint. Als Argument erwartet die Funktion den Filter, der beim Einräumen ins Regal beachtet werden soll. Diese Funktion wird nicht nur zu Beginn, mit leerem Filter, aufgerufen, sondern auch immer dann, wenn der Benutzer einen neuen Filter eingegeben hat. Daher ist die erste Maßnahme der Funktion das komplette Leeren von shop_rack.

Listing 4
------------------------------------------------------------------
function showProducts(filter) {
  
  //Regal räumen
  html_shop_rack.innerHTML = "";
  
  //Produkte filtern
  var filterRegExp = new RegExp(filter);
  var filteredProducts = data.products.findAll(
    function(product) {
      return product.title.match(filterRegExp);
    }
  );  
   
  //Ziehbare img-Elemente erzeugen 
  filteredProducts.each( 
    function(product) {
      itemIndex++;
      html_shop_rack.appendChild(createRackElement(product));
      new Draggable("shop_product_"+itemIndex,{revert: true});
    }  
  );   
}

// Ein einzelnes Produkt (img-Element) für's Regal erzeugen
function createRackElement(product) {
  var rackElement = document.createElement("img");
  rackElement.className="shop_item";
  rackElement.setAttribute("id", "shop_product_"+itemIndex);
  rackElement.setAttribute("src", "products/"+product.id+".jpg");
  rackElement.setAttribute("product", product.id);
  rackElement.setAttribute("title", product.title + " | " + product.price.toFixed(2) + " €");
  rackElement.setAttribute("alt", product.title);
  return rackElement;
}

Auf Basis des übergebenen Filters wird ein regulärer Ausdruck erzeugt, gegen den die Eigenschaft title der einzelnen Elemente von data.products geprüft werden. Der Iterator findAll, den Prototype dem Array zugeteilt hat, gibt das Array filteredProducts zurück, dessen Elemente dem Filter entsprechen. Anschließend wird filteredProducts komplett durchlaufen, wodurch jedes Element Daten für ein Produkt liefert, das im Regal platziert wird. Die Funktion createRackElement erzeugt ein img-Element und stattet es mit den nötigen Attributen aus. So wird zum Beispiel die Eigenschaft id eines Produkts, die wie eine Art Artikel- oder Bestellnummer fungiert, für das src-Attribut herangezogen. Der Dateiname bildet sich dabei automatisch aus Produkt-ID und der Endung .jpg. Die Produkt-ID wird zudem noch in einem eigenen Attribut product des img-Elements vermerkt. Dies entspricht nicht den XHTML-Grundregeln, ein solches Attribut ist dort nicht vorgesehen. Allerdings macht auch Script.aculo.us selbst intensiven Gebrauch von der Erweiterung einzelner XHTML-Elemente durch eigene Attribute. Das Attribut product wird später dazu verwendet, das in den Warenkorb gelegte Produkt an seiner hausinternen Produktkennung zu identifizieren. Das XHTML-konforme Attribut id des img-Elements wird mithilfe der fortlaufenden Nummer itemIndex gebildet, die nach jeder Erzeugung eines Produkts erhöht wird. So ist gewährleistet, dass jedes dynamisch erzeugte Produkt eine auch wirklich einzigartige id trägt und eindeutig angesprochen werden kann. Außerdem erfolgt eine Zuweisung der Klasse shop_item. Erinnern Sie sich? Das war das Kriterium, an dem das Dropp- able shop_cart ein zulässiges Draggable erkennt. Um aus einem img-Element, das ein Produkt darstellt, ein Draggable zu machen, erzeugen wir einfach eine neue Instanz der gleichnamigen Klasse. Ihr übergeben wir die id des Elements, das ziehbar werden soll und fügen als zweites Argument noch Optionen hinzu. In diesem Fall handelt es sich um die Eigenschaft revert, die, wenn sie auf true gesetzt ist, ein Zurückschnellen des Elements auf die Originalposition nach dem Platzieren des Draggables zur Folge hat.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -