<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Der Interaktionsdesigner - PHP, jQuery und CSS &#187; jQuery</title>
	<atom:link href="http://www.interaktionsdesigner.de/category/jquery/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.interaktionsdesigner.de</link>
	<description>Webentwicklung mit TYPO3, jQuery, CakePHP und Spaß an neuen Projekten</description>
	<lastBuildDate>Wed, 07 Dec 2011 14:26:54 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title>Schönes Javascript mit Callbacks</title>
		<link>http://www.interaktionsdesigner.de/2011/12/07/schones-javascript-mit-callbacks/</link>
		<comments>http://www.interaktionsdesigner.de/2011/12/07/schones-javascript-mit-callbacks/#comments</comments>
		<pubDate>Wed, 07 Dec 2011 14:26:29 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[Entwicklung]]></category>
		<category><![CDATA[Tutorial]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=826</guid>
		<description><![CDATA[Callbacks sind jedem jQuery Anweder bekannt. Von bis zu bindet der Webentwickler jede Menge Logik an festgelegte Events. Meiner Meinung nach eine angenehme Art zu programmieren und gut nachvollziehbar, da der Code gut lesbar bleibt, sofern man nicht übertreibt. Wenn man selbst Applikationen programmiert die auf Javascript basieren und später noch diverse Erweiterungen ermöglichen sollen, [...]]]></description>
			<content:encoded><![CDATA[<p>Callbacks sind jedem jQuery Anweder bekannt. Von <code>onClick</code> bis zu <code>onResize</code> bindet der Webentwickler jede Menge Logik an festgelegte Events. Meiner Meinung nach eine angenehme Art zu programmieren und gut nachvollziehbar, da der Code gut lesbar bleibt, sofern man nicht übertreibt.</p>
<p>Wenn man selbst Applikationen programmiert die auf Javascript basieren und später noch diverse Erweiterungen ermöglichen sollen, dann macht es durchaus Sinn eigene Callbacks anzubieten. Sei es in einer jQuery Extension oder einer eigenen Applikation.</p>
<p><span id="more-826"></span></p>
<h2>Die Anforderung</h2>
<p>Zur Erfüllung meiner aktuellen Anforderung habe ich eine Javascript Klasse erstellt die mit dem Operator <code>new</code> erstellt werden kann:</p>
<pre>var myObject = new FunkyObject();</pre>
<p>Perfekt für die erste Anforderung, aber sobald weitere Entwickler involviert werden oder man Teile der Applikation sogar als OpenSource Software veröffentlicht, wäre es ja schön wenn nicht jeder im eigenen Code rum macht sondern ordentlich in seinem Script bleibt:</p>
<pre>var myObject = new FunkyObject({
&nbsp;&nbsp;onLoad: function() {
&nbsp;&nbsp;&nbsp;&nbsp;//do crazy custom things
&nbsp;&nbsp;}
});</pre>
<p>Sieht ordentlich aus und ist ohne Kommentare selbsterklärend: Beim laden werden verrückte Sachen getan. Aber was tut das FunkyObject um die Callbacks aufzurufen?</p>
<h2>Eine Javascript Klasse</h2>
<p>Genau genommen ist eine Javascript Klasse nichts weiter als eine Funktion die sich selbst zurück gibt und über den <code>new</code> Operator erstellt wird. Meine Grundlage sieht so aus:</p>
<pre>var FunkyObject = function(args) {
&nbsp;&nbsp;var defaults = {};
&nbsp;&nbsp;this.args = $.extend(defaults, args);
&nbsp;&nbsp;this.goCrazy();
&nbsp;&nbsp;return this;
};</pre>
<p>Dieser Code setzt jQuery voraus, das die Funktion <code>$.extend</code> mitbringt. Damit wird das übergebene <code>args</code> Objekt durch die in <code>defaults</code> angegebenen Werte aufgefüllt, sofern sie nicht vorhanden sind. Man kann also für das <code>onClick</code> Event ein Standardverhalten hinterlegen:</p>
<pre>var FunkyObject = function(args) {
&nbsp;&nbsp;var defaults = {
&nbsp;&nbsp;onLoad: function() {
&nbsp;&nbsp;&nbsp;&nbsp;//default behavior
&nbsp;&nbsp;};
&nbsp;&nbsp;this.args = $.extend(defaults, args);
&nbsp;&nbsp;this.goCrazy();
&nbsp;&nbsp;return this;
};</pre>
<p>Die Funktion <code>goCrazy()</code> wird ausgeführt sobald die Klasse initialisiert wird. Um dem nächsten Entwickler die Möglichkeit zu geben auch diese Funktion zu überschreiben, werden alle Grundlagen in der <code>prototype</code> Eigenschaft der Klasse definiert:</p>
<pre>var FunkyObject = function(args) { /* ... */ };
FunkyObject.prototype.goCrazy = function() {
&nbsp;&nbsp;// do the init stuff
};</pre>
<p>Mit diesem Konstrukt könnte ein Entwickler im eigenen Script jene Klasse überschreiben ohne in den eigentlichen Quelltext eingreifen zu müssen.</p>
<pre>myObject.goCrazy = function() {
&nbsp;&nbsp;// custom constructor
}</pre>
<p>Das als kleine Anmerkung am Rande, das erklärte Ziel ist der komfortable Aufruf des <code>onLoad</code> Events. Natürlich könnte man über <code>this.args.onClick()</code> einfach die entsprechende Funktion aufrufen, aber sieht das schön aus? Ist das variabel?</p>
<p>Die Antwort lautet nein. </p>
<h2>Schön und variabel</h2>
<p>Viel cooler ist eine total schöne und variable <code>trigger()</code> Funktion zu schreiben, die in den Parametern nach einem Callback sucht und diesen ggf. ausführt.</p>
<pre>FunkyObject.prototype.trigger = function(action) {
&nbsp;&nbsp;var callback = 'on'+this.ucfirst(action);
&nbsp;&nbsp;if(typeof this.args[callback] == 'function') {
&nbsp;&nbsp;&nbsp;&nbsp;this.args[callback]();
&nbsp;&nbsp;};
}</pre>
<p>In der <code>goCrazy</code> Funktion sieht der Aufruf dann so aus:</p>
<pre>var FunkyObject = function(args) { /* ... */ };
FunkyObject.prototype.goCrazy = function() {
&nbsp;&nbsp;this.trigger('load');
};</pre>
<p>Die <code>trigger</code> Funktion baut sich den Eventnamen aus einem <code>on</code> und dem Parameter <code>action</code> zusammen und prüft anschließend ob eine Funktion mit diesem Namen vorhanden ist. Ist dies der Fall wird sie ausgeführt.</p>
<p>Leider gibt es keine Javascript Funktion mit dem Namen <code>ucfirst</code>, aber über die Prototype Eigenschaft kann man das String Objekt entsprechend aufrüsten:</p>
<pre>String.prototype.ucfirst = function() {
&nbsp;&nbsp;return this.substr(0,1).toUpperCase() + this.substr(1,this.length);
};</pre>
<p>Mit diesen zwei Funktionen baut man sich sehr schnell eine Grundlage um variable Scripte zu schreiben die komplett erweitert werden können.</p>
<h2>Parameter für den Callback</h2>
<p>Oft muss der Callback über einen gewissen Zustand informiert werden. Für das benutzte Beispiel könnte es die Information sein ob das Laden erfolgreich war. Bei der Definition des Callbacks muss ein Parameter akzeptiert werden:</p>
<pre>var myObject = new FunkyObject({
&nbsp;&nbsp;onClick: function(success) {
&nbsp;&nbsp;&nbsp;&nbsp;alert(success ? 'hat geklappt!' : 'fehler');
&nbsp;&nbsp;}
});</pre>
<p>Die <code>goCrazy</code> Funktion muss den Status an die <code>trigger</code> Funktion weitergeben:</p>
<pre>FunkyObject.prototype.goCrazy = function() {
&nbsp;&nbsp;success = true;
&nbsp;&nbsp;this.trigger('load', success);
};</pre>
<p>Wir gehen mal von einem grundlegenden Erfolg aus. Das wird es auch sofern die <code>trigger</code> Funktion beigebracht bekommt was sie mit zusätzlichen Parametern anfangen soll:</p>
<pre>FunkyObject.prototype.trigger = function(action) {
&nbsp;&nbsp;var callback = 'on'+this.ucfirst(action);
&nbsp;&nbsp;if(typeof this.args[callback] == 'function') {
&nbsp;&nbsp;&nbsp;&nbsp;this.args[callback](arguments.shift());
&nbsp;&nbsp;};
}</pre>
<p>Viele Änderungen sind es nicht. In der vordefinierten Variable <code>arguments</code> sind sämtliche Parameter vom Aufruf der Funktion gespeichert. Die Array Funktion <code>shift</code> entfernt das erste Element aus dem Array und gibt den Rest zurück. So wird nicht der Callbackname sinnloser Weise an den Callback gegeben sondern alle andere Parameter. So kann man auch mehr als ein Wert an den Callback schicken:</p>
<pre>FunkyObject.prototype.goCrazy = function() {
&nbsp;&nbsp;success = true;
&nbsp;&nbsp;this.trigger('load', success, 'ready', {hello: 'world'});
};</pre>
<h2>Fazit</h2>
<p>Ich mag Callbacks. Wenn man ein gutes Grundgerüst hat kann man auf alle Events individuell reagieren. Sollte es ein Callback nicht geben den man wirklich dringend und sofort benötigt kann man mit dem Überschreiben der <code>prototype</code> Funktion kurzfristig neue Callbacks hinzufügen und später in die Core Anwendung übernehmen.</p>
<p>Wichtig ist aber darauf zu achten keine unendlichen Scripte in den Callbacks unterzubringen. Spätestens nach 20 Zeilen sollte man sich überlegen den Code in einzelne Häppchen aufzuteilen.</p>
<p>Und in eigener Sache noch die Warnung vor gedankenlosem Copy and Paste: Durch Unterschrift auf dem NDA musste ich alle Funktionen umbenennen. Also nachdenken und gute Funktionsnamen benutzen.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2011/12/07/schones-javascript-mit-callbacks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jQuery Deferred verstehen und bessere Scripte schreiben</title>
		<link>http://www.interaktionsdesigner.de/2011/03/23/jquery-deferred-verstehen-und-bessere-scripte-schreiben/</link>
		<comments>http://www.interaktionsdesigner.de/2011/03/23/jquery-deferred-verstehen-und-bessere-scripte-schreiben/#comments</comments>
		<pubDate>Wed, 23 Mar 2011 10:01:44 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=795</guid>
		<description><![CDATA[jQuery 1.5 ist schon eine Weile draußen und hat ein neues tolles Konzept mitgebracht das ich unbedingt vorstellen möchte, denn damit schreibt man bessere Scripte, die übersichtlicher sind und zuverlässiger funktionieren. Es heißt: Deferred und ist eigentlich sehr leicht zu verstehen. Mit jQuery.Deferred lassen sich Objekte erzeugen, die beim erreichen eins Zustandes bescheid geben, damit [...]]]></description>
			<content:encoded><![CDATA[<p><strong>jQuery 1.5</strong> ist schon eine Weile draußen und hat ein neues tolles Konzept mitgebracht das ich unbedingt vorstellen möchte, denn damit schreibt man bessere Scripte, die übersichtlicher sind und zuverlässiger funktionieren. Es heißt: <strong>Deferred</strong> und ist eigentlich sehr leicht zu verstehen.</p>
<p>Mit <strong>jQuery.Deferred</strong> lassen sich Objekte erzeugen, die beim erreichen eins Zustandes bescheid geben, damit andere Objekte darauf reagieren können. Klingt verworren ist aber ganz einfach, zum Beispiel sollen die Bilder angezeigt werden wenn sie fertig geladen sind.</p>
<p><span id="more-795"></span></p>
<h2>Ohne $.Deferred</h2>
<p>Vor jQuery 1.5 musste man sich mit Callbacks oder <code>window.setTimeout()</code> helfen um auf Ereignisse zu reagieren.</p>
<pre>$('#elem').fadeIn(function() {
&nbsp;&nbsp;// do something when #elem is visible
});</pre>
<p>Bei einem Element ist das leicht, aber wenn es sich um <strong>mehrere Elemente</strong> handelt, oder man nicht genau sagen kann wann ein Element einen Status erreicht hat, dann wirds kritisch.</p>
<h2>Mit $.Deferred</h2>
<p>Die großartigen Funktionen heißen <code>$.when()</code> und <code>$.then()</code>. In meinem letzten Projekt musste ich warten bis mehrere Bilder geladen wurden. So sieht der Aufruf aus:</p>
<pre>$.when(loadImg('#img1')).then(function() {
&nbsp;&nbsp;//do stuff, the image is loaded
});</pre>
<p>Zugegeben, das kann auch leichter haben mit <code>$('#img1').load(function() { /* action */ })</code>, richtig schön wird es auch bei mehreren Bildern:</p>
<pre>$.when(
&nbsp;&nbsp;loadImg('#img1'),
&nbsp;&nbsp;loadImg('#img2'),
&nbsp;&nbsp;loadImg('#img3'))
.then(function() {
&nbsp;&nbsp;//do stuff, all images are loaded
});</pre>
<p>Die Funktion <code>$.then()</code> wird erst ausgeführt wenn alle Funktionen in <code>$.when()</code> ihr okay gegeben haben. Genial oder?</p>
<p>Die Funktion <code>loadImg()</code> muss natürlich entsprechend vorbereitet werden, damit sie okay sagen kann.</p>
<pre>function loadImg(selector) {
&nbsp;&nbsp;var dfd = $.Deferred();
&nbsp;&nbsp;$(selector).load(function() { dfd.resolve(); });
&nbsp;&nbsp;return dfd.promise();
}</pre>
<p>In der Variable <code>dfd</code> wird ein neues <strong>Deferred Objekt</strong> erzeugt. Die Rückgabe der Funktion muss von diesem Objekt die Funktion <code>promise()</code> ausgeben.</p>
<p>Und wenn die Funktion ihr erklärtes Ziel erreicht hat, wird die Funktion <code>resolve()</code> aufgerufen. Damit ist der Funktion <code>$.when()</code> klar das sie ggf. weitermachen kann.</p>
<h2>Fazit</h2>
<p>jQuery Magic vom feinsten! Das war ein sehr kleiner Einstieg in dieses großartige Konzept, <strong>Deferred</strong> kann noch einiges mehr und die <a title="Deferred in jQuery" href="http://api.jquery.com/category/deferred-object/" target="_blank">offizielle jQuery Dokumentation</a> verrät was.</p>
<p>Ich wünsche viel Spaß beim programmieren und coole neue Scripte.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2011/03/23/jquery-deferred-verstehen-und-bessere-scripte-schreiben/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Die eine Zeile zum jQuery Tab</title>
		<link>http://www.interaktionsdesigner.de/2011/01/17/jquery-super-cool/</link>
		<comments>http://www.interaktionsdesigner.de/2011/01/17/jquery-super-cool/#comments</comments>
		<pubDate>Mon, 17 Jan 2011 07:30:54 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=760</guid>
		<description><![CDATA[jQuery ist ein großartiges Javascript Framework. Mit einer einzigen Zeile kann man ein komplettes Tab Widget bauen. In meinem aktuellen Projekt kann der Benutzer unter einer großen Auswahl Elementen wählen. Jedes Element gehört in eine Kategorie. Also wäre es doch nett oben eine Kategorie anzuklicken und anschließend nur die Einträge dieser Kategorie zu sehen. Nichts [...]]]></description>
			<content:encoded><![CDATA[<p><strong>jQuery</strong> ist ein großartiges <strong>Javascript Framework</strong>. Mit einer einzigen Zeile kann man ein komplettes <strong>Tab Widget</strong> bauen.</p>
<p>In meinem aktuellen Projekt kann der Benutzer unter einer großen Auswahl Elementen wählen. Jedes Element gehört in eine Kategorie. Also wäre es doch nett oben eine Kategorie anzuklicken und anschließend nur die Einträge dieser Kategorie zu sehen.</p>
<p>Nichts leichter als das! Es gibt natürlich großartige und weniger gute Plugins zu dem Thema, aber es ist <strong>in einer Zeile selbst geschrieben</strong>. Macht Spaß und verdeutlicht die <strong>Verkettung in jQuery</strong>.</p>
<p><span id="more-760"></span></p>
<h2>Das HTML Gerüst</h2>
<p>Damit fängt es immer an. So sieht es leicht gekürzt aus:</p>
<pre>&lt;div class="categories"&gt;
&nbsp;&nbsp;&lt;ul&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;li data-id="1"&gt;Kategorie 1&lt;/li&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;li data-id="2"&gt;Kategorie 2&lt;/li&gt;
&nbsp;&nbsp;&lt;/ul&gt;
&nbsp;&nbsp;&lt;div id="category_1" class="category"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;!-- jede Menge Inhalt --&gt;
&nbsp;&nbsp;&lt;/div&gt;
&nbsp;&nbsp;&lt;div id="category_2" class="category"&gt;
&nbsp;&nbsp;&nbsp;&nbsp;&lt;!-- noch  mehr Inhalt in Kategorie 2 --&gt;
&nbsp;&nbsp;&lt;/div&gt;
&lt;/div&gt;</pre>
<p>Es gibt also einen umfassenden Container, darin befindet sich eine unsortierte Liste als Menü und darunter die einzelnen Kategorien. Die Listenpunkte sind mit dem HTML5 Attribut <code>data-id</code> ausgestattet um klar zu machen, welchen Punkt sie einblenden.</p>
<p>jQuery arbeitet auf grandiose Art und Weise damit zusammen, dazu gleich mehr.</p>
<h2>Eine Zeile jQuery</h2>
<p>Hier kommt sie:</p>
<pre>$('.categories&gt;ul li').live('click', function() { $(this).closest('.categories').find('.category').hide().filter('#category'+$(this).data('id')).show().end().end().end().siblings('li').removeClass('active').end().addClass('active');}).filter(':first').click();</pre>
<p>Auch wenn ich große Töne gespuckt habe von wegen eine einzige Zeile, würde ich doch empfehlen ein paar Zeilenumbrüche einzubauen. In Javascript müssen die Zeilen nicht mit einem Simikolon beendet werden, deshalb kann man diese Zeile auch übersichtlicher notieren:</p>
<pre>$('.categories&gt;ul li')
&nbsp;&nbsp;&nbsp;&nbsp;.live('click', function() {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$(this)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.closest('.categories')
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.find('.category')
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.hide()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.filter('#category'+$(this).data('id'))
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.show()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.end()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.end()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.end()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.siblings('li')
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.removeClass('active')
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.end()
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.addClass('active');
&nbsp;&nbsp;&nbsp;&nbsp;})
&nbsp;&nbsp;&nbsp;&nbsp;.filter(':first')
&nbsp;&nbsp;&nbsp;&nbsp;.click();</pre>
<h2>Einzelerklärung</h2>
<p>Jetzt gehts los! jQuery basiert immer auf einem <strong>Elementstack</strong>. Das bedeutet man wählt per CSS Selektor eine beliebige Anzahl Elemente aus und wendet darauf beliebig viele Befehle an. In diesem Beispiel werden alle Listenelemente ausgewählt die sich in einer Liste befindet, welche wiederum ein direktes Kindelement eines Elements mit der Klasse <code>.categories</code> ist.</p>
<pre>$('.categories&gt;ul li')</pre>
<p>Würde man das <code>&gt;</code> Zeichen weglassen, würden sich die folgenden Anweisungen auf alle Listen im Element <code>.categories</code> beziehen, was natürlich nicht gewünscht ist.</p>
<p>Jedem Listenelement wird der Eventhandler <code>click</code> zugewiesen:</p>
<pre>.live('click', function() {</pre>
<p>Die großartige Funktion <code>live(event, callback)</code> arbeitet im Gegensatz zu <code>click(callback)</code> auch mit Elementen die nachträglich dem Dom hinzugefügt wurden.</p>
<p>Innerhalb eines Callbacks bezieht sich <code>$(this)</code> auf das spezifische Element welches den Funktionsaufruf ausgelöst hat. Sprich: das angeklickte Listenelement. Das dient als Ausgangspunkt um das umfassende Element <code>.categories</code> in den Elementstack zu laden:</p>
<pre>$(this).closest('.categories')</pre>
<p>Vom Ausgangspunkt <code>$(this)</code> wird das nächstgelegene Element ausgewählt. Sollten sich mehrere Elemente mit der Klasse <code>.categories</code> auf der Seite befinden können wir so sicherstellen, dass das richtige Element behandelt wird.</p>
<p>Aus dem richtigen Element werden alle Elemente der Klasse <code>.category</code> gesucht und ausgeblendet:</p>
<pre>.find('.category').hide()</pre>
<p>Mit <code>find(selector)</code> wird innerhalb der Objekte im Elementstack gesucht. Im Gegensatz zu der Funktion <code>filter(selector)</code> welche die Elemente im Stack durchgeht und nur jene zurück liefert auf die der Selector zutrifft.</p>
<p>Die großartige Methode <code>data(key, value)</code> greift automatisch auf das <code>data</code> Attribut aus dem HTML zu und stellt es zur Verfügung.</p>
<p>Mit diesem Wissen müsste der nächste Schritt klar sein:</p>
<pre>.filter('#category'+$(this).data('id')).show()</pre>
<p>Aus allen ausgewählten und versteckten <code>.category</code> Elementen wird das mit der ID des angeklickten Listenelements ausgewählt und angezeigt.</p>
<p>Mit <code>end()</code> setzt man einen veränderten Elementstack wieder zurück. Wir haben jetzt drei mal <code>end()</code>, für <code>filter()</code>, <code>find()</code> und <code>closest()</code>. Also befinden wir uns wieder beim angeklickten Listenelement.</p>
<pre>.siblings('li')</pre>
<p>Damit werden alle Nachbarn des Listenelements ausgewählt, also alle Einträge in der Liste. Egal welches, keines darf mehr die Klasse <code>active</code> besitzen:</p>
<pre>.removeClass('active').end()</pre>
<p>Im gleichen Zug wird auch die Veränderung durch <code>siblings(selector)</code> zurück gesetzt. Fehlt nur noch das hinzufügen der Klasse zum angeklickten Element:</p>
<pre>.addClass('active');</pre>
<p>Das war es innerhalb des Callbacks. Beim laden der Seite werden alle Kategorien angezeigt, das ist nicht Ziel des Tab Widgets, aber auch leicht zu lösen. Zur Erinnerung: Es wurden alle Listenelemente ausgewählt und jedem Element ein Clickhandler zugewiesen.</p>
<pre>$('.categories&gt;ul li').live('click', function() { /* lange zeile */ })</pre>
<p>Auch die <code>live()</code> Funktion gibt den Elementstack zurück, deshalb brauchen wir nur noch zwei grandiose Funktionen anzuhängen:</p>
<pre>.filter(':first').click();</pre>
<p>In jQuerysprache heißt das: Nimm das erste Element aus deinem Stack und klicke es an. Was dann passiert haben wir gerade ausführlich besprochen. Alle Kategorien werden ausgeblendet, die erste wieder ein und das erste Listenelement mit der Klasse <code>active</code> gekennzeichnet.</p>
<p><strong>Fertig!</strong> Das war ein Tab Widget in mehr oder weniger einer Zeile.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2011/01/17/jquery-super-cool/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Mein jQuery Plugin: movingLabels</title>
		<link>http://www.interaktionsdesigner.de/2010/09/17/mein-jquery-plugin-movinglabels/</link>
		<comments>http://www.interaktionsdesigner.de/2010/09/17/mein-jquery-plugin-movinglabels/#comments</comments>
		<pubDate>Fri, 17 Sep 2010 17:15:38 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=712</guid>
		<description><![CDATA[Ich mag Projekte bei denen jQuery Plugins rausspringen. Aktuell haben wir ein super Projekt welches in Cake realisiert wird und natürlich jQuery im Frontend benutzt. Wäre nicht unendliche Arbeit damit verbunden, würde noch viel mehr für den Blog rum kommen. Eins gibt es heute: movingLabels! Der Name sagt alles, mit diesem Plugin kann man nach [...]]]></description>
			<content:encoded><![CDATA[<p>Ich mag Projekte bei denen<strong> jQuery Plugins</strong> rausspringen. Aktuell haben wir ein super Projekt welches in <strong>Cake</strong> realisiert wird und natürlich jQuery im Frontend benutzt. Wäre nicht unendliche Arbeit damit verbunden, würde noch viel mehr für den Blog rum kommen.</p>
<p>Eins gibt es heute: <strong>movingLabels</strong>! Der Name sagt alles, mit diesem Plugin kann man nach belieben seine Labeltags rumwirbeln lassen, sobald das zugehörige Formularfeld den Focus erhält. Am besten direkt die <a title="movingLabels Demo" href="http://www.interaktionsdesigner.de/stuff/movinglabels/" target="_blank">Demo</a> ansehen, <a href="http://downloads.apeunit.com/jquery.movinglabels.js">downloaden</a> und den erklärenden Artikel lesen.</p>
<p><span id="more-712"></span></p>
<h2>Vorweg</h2>
<p>Ich weiß es gibt schon genau dieses Plugin Namens <a href="http://www.csskarma.com/blog/sliding-labels-plugin/" target="_blank">slidingLabels</a>, aber das ist mir in der Anwedung und in den Möglichkeiten nicht ausgereift genug. Wir brauchen doch in jedem Projekt etwas neues! Jedenfalls fürs Auge. Deshalb hier meine eigene Umsetzung.</p>
<h2>HTML und CSS</h2>
<p>Vor dem Einsatz des Plugins muss man sein Formular entsprechend vorbereiten. Das vom <strong>Cake HtmlHelper</strong> generierte HTML passt schon perfekt und sieht in etwa so aus:</p>
<pre>&lt;div&gt;
&nbsp;&nbsp;&lt;label&gt;Name&lt;/label&gt;
&nbsp;&nbsp;&lt;input type="text" name="name" /&gt;
&lt;/div&gt;</pre>
<p>Vor dem Einsatz muss das Label schon über dem Inputfeld liegen. Deshalb ist der umschließende DIV Container notwendig. Am schnellsten geht es mit folgendem CSS:</p>
<pre>form div {
&nbsp;&nbsp;position:relative;
&nbsp;&nbsp;margin:5px 0;
}
form div label {
&nbsp;&nbsp;position:absolute;
&nbsp;&nbsp;top:3px;
&nbsp;&nbsp;left:8px;
}</pre>
<p>Damit ist die <strong>Grundlage</strong> fertig.</p>
<h2>JavaScript</h2>
<p>Das Plugin <a href="http://downloads.apeunit.com/jquery.movinglabels.js">herunterladen</a>, jQuery einbinden und das <strong>Plugin movingLabels</strong>. Das Plugin sucht in dem übergebenen Element standardmäßig nach DIV Containern und in diesen nach <strong>Label-</strong> und <strong>Inputtags</strong> vom Typ Text und Passwort. So sieht der Aufruf aus:</p>
<pre>jQuery(function($) {
&nbsp;&nbsp;$('form').movingLabels();
});</pre>
<p><strong>Das wars!</strong> Beim Fokus von einem Formularfeld wird das Label sanft ausgeblendet.</p>
<h2>Anpassungen</h2>
<p>Natürlich kann man alle Einstellungen und Animationen ändern. Als Parameter übergibt man dafür ein Objekt mit angepassten Eigenschaften.</p>
<pre>jQuery(function($) {
&nbsp;&nbsp;$('form').movingLabels({custom: 'settings'});
});</pre>
<p>Folgende Optionen sind implementiert:</p>
<pre>label: 'label'</pre>
<p>ein HTML Selector der das Element spezifiziert, welches sich bewegt<br />
&nbsp;</p>
<pre>input: ':text, :password'</pre>
<p>das Eingabefeld für welches die Labels bewegt werden sollen. Ebenfalls ein HTML Selector<br />
&nbsp;</p>
<pre>div: 'div'</pre>
<p>das umfassende Element. Rate mal? Richtig, ein HTML Selector<br />
&nbsp;</p>
<pre>animation.hide: {opacity: 0, marginLeft: 20}</pre>
<p>das Animationsobjekt welches ausgeführt wird, wenn der Focus gesetzt wurde<br />
&nbsp;</p>
<pre>animation.show: {opacity: 1, marginLeft: 0}</pre>
<p>verliert das Feld den Focus, wird das Label in diesen Zustand versetzt<br />
&nbsp;</p>
<pre>animation.start: {marginLeft: '-20px'}</pre>
<p>nachdem die Animation zum verstecken gelaufen ist, wird dieses CSS Objekt zugewiesen. <strong>Achtung:</strong> wenn man das nicht braucht muss es als leeres Objekt übergeben werden, sonst gibts Ärger von jQuery.<br />
&nbsp;</p>
<pre>animation.speed: 150</pre>
<p>die Geschwindigkeit mit der das ganze ablaufen soll. Entweder in Millisekunden oder in Worten ('slow', 'fast')</p>
<h2>Fertig!</h2>
<p>Ich muss sagen, ich bin bisher recht Stolz auf dieses kleine Plugin. Jetzt kann man sich eine <a title="movingLabels Demo" href="http://www.interaktionsdesigner.de/stuff/movinglabels/" target="_blank">Demo</a> ansehen oder es <a href="http://downloads.apeunit.com/jquery.movinglabels.js">herunterladen</a>. Kommentare sind geöffnet und ich bin gespannt.</p>
<p>Wer komplizierte oder ausgefallene Projekte umsetzt und Hilfe sucht, kann sich gerne an die coolste Agentur der Welt wenden: <a title="Ape Unit GmbH Berlin" href="http://www.apeunit.com/webentwicklung" target="_blank">Ape Unit</a>!!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2010/09/17/mein-jquery-plugin-movinglabels/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>TYPO3 Klickvergrößern durch eine jQuery Lightbox ersetzen</title>
		<link>http://www.interaktionsdesigner.de/2009/12/04/typo3-klickvergrosern-durch-eine-jquery-lightbox-ersetzen/</link>
		<comments>http://www.interaktionsdesigner.de/2009/12/04/typo3-klickvergrosern-durch-eine-jquery-lightbox-ersetzen/#comments</comments>
		<pubDate>Fri, 04 Dec 2009 13:07:44 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[TYPO3]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=629</guid>
		<description><![CDATA[Wenn jQuery auf einer Seite eingebunden ist (z.B. durch die coole Extension Kiwi Slider) dann beißt sich das Javascriptframework mit anderen wie MooTools oder Prototype. Insgesamt sollte man bevorzugt ein Framework einsetzen, wenn man unbedingt beide braucht, dann lohnt sich die Suche nach dem NoConflict Mode von jQuery. Wenn aber das unbedingt nur von einer [...]]]></description>
			<content:encoded><![CDATA[<p>Wenn jQuery auf einer Seite eingebunden ist (z.B. durch die coole Extension <a title="Kiwi Slider" href="http://www.interaktionsdesigner.de/2008/09/09/typo3-extension-kiwi-slider-2/">Kiwi Slider</a>) dann beißt sich das Javascriptframework mit anderen wie MooTools oder Prototype. Insgesamt sollte man bevorzugt ein Framework einsetzen, wenn man unbedingt beide braucht, dann lohnt sich die Suche nach dem <a title="jQuery noConflict" href="http://docs.jquery.com/Core/jQuery.noConflict" target="_blank">NoConflict Mode</a> von jQuery.</p>
<p>Wenn aber das <em>unbedingt</em> nur von einer Lightbox kommt, dann sollte man sich kurz hinsetzen, einen geeigneten Klon aussuchen (<a title="Lightboxclone Matrix" href="http://planetozh.com/projects/lightbox-clones/" target="_blank">hier ist die große Lightbox Matrix</a>) und die <strong>Klickvergrößerung von TYPO3</strong> damit ersetzen. Das ist schnell, effektiv und in diesem Beitrag erklärt.<span id="more-629"></span></p>
<h2>Vorbereitungen</h2>
<p>Für mein aktuelles Projekt habe ich mich für den Lightbox Clone <a title="TopUp" href="http://gettopup.com/" target="_blank">TopUp</a> entschieden. Sieht sehr vielversprechend aus. Also welcher Klone auch immer: <strong>Daten herunterladen</strong> und <strong>ins Projekt einbinden</strong>. Dazu gehören mind. jQuery und das entsprechende Plugin, in vielen Fällen auch noch eine CSS Datei. Firebug verrät im <em>Script</em> Reiter ob es funktioniert hat.</p>
<p>TopUp benötigt keine CSS Datei, dafür aber <strong>einen Bilder- und einen Players-Ordner</strong> in dem SWF Dateien gespeichert sind um verschiedene Medien wiederzugeben. Per JavaScript können die Pfade angepasst werden:</p>
<pre>TopUp.host = 'http://www.kiwi-service.de/';
TopUp.images_path = 'fileadmin/template/img/topup/';
TopUp.players_path = 'fileadmin/template/flash/';</pre>
<h2>JavaScript einbinden</h2>
<p>Eine Besonderheit bringt TopUp mit: Es lädt dynamisch die benötigten JavaScriptdateien nach. Dafür braucht er den <strong>kompletten javascripts/jquery Ordner</strong>. Dieser muss im gleichen Ordner liegen wie <strong>top_up-min.js</strong>.</p>
<pre>fileadmin/template/js/top_up-min.js
fileadmin/template/js/jquery/...</pre>
<h2>Test</h2>
<p>Alle benötigten Dateien sind eingebunden und in Firebug sichtbar. Um zu testen ob es funktioniert, füge ich ein Inhaltselement vom Typ HTML ein und schreibe mein eigenes Bild rein. Testbilder sind im <em>Images</em> Ordner vorhanden.</p>
<pre>&lt;a href="fileadmin/template/img/topup/photos/1.jpg" class="top_up"&gt;
&nbsp;&nbsp;&lt;img src="fileadmin/template/img/topup/thumbnails/1.jpg"&gt;
&lt;/a&gt;</pre>
<p>Funktioniert, wunderbar! Wenn nicht, dann die eigene Seite mit den <a title="TopUp Beispiele" href="http://gettopup.com/examples" target="_blank">Beispielen</a> vergleichen.</p>
<h2>Typoscript</h2>
<p>Nun muss man <strong>TYPO3</strong> noch das hässliche JavaScript PopUp austreiben. Hier mein Konfiguration für <strong>Version 4.3</strong></p>
<pre>temp.imageLinkWrap = 1
temp.imageLinkWrap {
enable = 1
typolink {
parameter.cObject = IMG_RESOURCE
parameter.cObject.file.import.data = TSFE:lastImageInfo|origFile
parameter.cObject.file.maxW = {$styles.content.imgtext.maxW}
parameter.override.listNum.stdWrap.data = register : IMAGE_NUM_CURRENT
title.field = imagecaption // title
title.split.token.char = 10
title.if.isTrue.field = imagecaption // header
title.split.token.char = 10
title.split.returnKey.data = register : IMAGE_NUM_CURRENT
parameter.cObject = IMG_RESOURCE
parameter.cObject.file.import.data = TSFE:lastImageInfo|origFile
ATagParams = target="_blank"
}
}
tt_content.image.20.1.imageLinkWrap &gt;
tt_content.image.20.1.imageLinkWrap &lt; temp.imageLinkWrap </pre>
<p>Als erstes wird das temporäre Objekt <strong>temp.imageLinkWrap</strong> erzeugt und konfiguriert. Interessant ist die letzte Angabe <strong>ATagParams</strong> - darüber gibt man jedem Link die Klasse <em>top_up</em> mit, damit das Plugin sich um die Bilder kümmert.</p>
<p>Anschließend wird die Standardvergrößerung in <strong>tt_content.image.20.1.imageLinkWrap</strong> überschrieben. <strong>Cache leeren</strong> und Seite neuladen, fertig!</p>
<h2>TopUp Spezialitäten</h2>
<p>Dieses TopUp ist wirklich ein schönes Teil. Mit der Funktion <strong>addPresets()</strong> lassen sich für Links in DOM Bereichen Voreinstellungen treffen. Zum Beispiel alle Bilder die über <strong>ein Inhaltselement</strong> eingestellt sind, zu einer Gruppe zusammenfassen:</p>
<pre>TopUp.addPresets({
".csc-textpic-imagewrap a": {
group: "images"
}
});</pre>
<p>Das bedeutet alle TopUp-Links im Div <strong>.csc-textpic-imagewrap</strong> werden der Gruppe <em>images</em> zugewiesen. Im Overlay gibts dann Vor- und Zurückpfeile.</p>
<h2>Fazit</h2>
<p>Die Initialisierung und Einstellungen einer Lightbox variieren natürlich. Es gibt aber ein paar schöne und elegante Tools deren Einsatz sich lohnen. Vorallem im Zusammenspiel mit der brand neuen TYPO3 Version 4.3 - ein großartiger Schritt mit schönen Verbesserungen. Unbedingt ansehen. Und TopUp einbinden!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/12/04/typo3-klickvergrosern-durch-eine-jquery-lightbox-ersetzen/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Ajax in Webapplikationen mit CakePHP und jQuery</title>
		<link>http://www.interaktionsdesigner.de/2009/11/21/ajax-in-webapplikationen-mit-cakephp-und-jquery/</link>
		<comments>http://www.interaktionsdesigner.de/2009/11/21/ajax-in-webapplikationen-mit-cakephp-und-jquery/#comments</comments>
		<pubDate>Sat, 21 Nov 2009 14:38:09 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=618</guid>
		<description><![CDATA[Jeder Webentwickler weiß, dass kein Benutzer mehr auf das Neuladen einer Seite warten will. Vorallem nicht, wenn es in Webapplikationen darum geht Elemente hinzuzufügen, zu bearbeiten oder zu entfernen. Zum Beispiel in einem Tool welches umfangreiche Exportmöglichkeiten besitzt und gerade von einer jungen, dynamischen Agentur entwickelt wird. In dem Programm legt man ein Exportscript an [...]]]></description>
			<content:encoded><![CDATA[<p>Jeder Webentwickler weiß, dass kein Benutzer mehr auf das Neuladen einer Seite warten will. Vorallem nicht, wenn es in Webapplikationen darum geht Elemente hinzuzufügen, zu bearbeiten oder zu entfernen.</p>
<p>Zum Beispiel in einem Tool welches umfangreiche Exportmöglichkeiten besitzt und gerade von einer <a title="Meine junge dynamische Agentur!" href="http://www.kiwi-service.de" target="_blank">jungen, dynamischen Agentur</a> entwickelt wird. In dem Programm legt man ein Exportscript an und kann diesem verschiedene Exportdateien zuweisen die beim durchlaufen dynamisch erstellt werden.</p>
<p>Diese einfache 1:n Beziehung ist mit Cake schnell gebacken, mit ein paar Zeilen erweitert und läuft. Allerdings verteilt auf mehreren Seiten. Zum Glück gibt uns Cake einfache Möglichkeiten an die Hand um das zu verhindern.</p>
<p>In diesem Artikel will ich aufschreiben wie man praktisch jedes Cake Projekt zusammen mit dem Lieblings-Javascriptframework jQuery in eine coole Web 2.0 Anwendung verwandeln kann.</p>
<p><span id="more-618"></span></p>
<h2>Grundlagen</h2>
<p>Ich gehe mal von einer laufenden Cakeanwendung aus. Da gibt es die Tabelle Export und die Tabelle Exportfiles. Die beiden sind über eine 1:n Beziehung verknüpft, die Anwendung wurde gebacken und man verfügt über die normalen CRUD Funktionen.</p>
<p><em>Wenn Interesse an einem Tutorial dazu besteht, wäre ein kurzer Hinweis in den Kommentaren oder per Mail nett.</em></p>
<h2>Der AppController</h2>
<p>Den schönsten Teil bringt Cake schon mit: den <strong>RequestHandler</strong>. Mit dessen Hilfe kann man auf Ajaxanfragen reagieren. Als erstes muss er in der Datei <strong>app/app_controller.php</strong> eingebunden werden:</p>
<pre>$components = array('RequestHandler');</pre>
<p>Anschließend stehen in jedem Controller über <strong>$this-&gt;RequestHandler</strong> umfangreiche Möglichkeiten zur Verfügung. Besonders toll wirds im Callback <strong>beforRender()</strong>. Dieser wird im AppController definiert und von Cake automatisch aufgerufen, wenn die Logik im entsprechenden Controller abgearbeitet wurde.</p>
<p>So sieht die Killerfunktion aus:</p>
<pre>function beforeRender() {
&nbsp;&nbsp;if($this-&gt;RequestHandler-&gt;isAjax()) {
&nbsp;&nbsp;   Configure::write('debug', 0);
&nbsp;&nbsp;   if($this-&gt;RequestHandler-&gt;prefers() == 'json') {
&nbsp;&nbsp;      die(json_encode($this-&gt;viewVars));
&nbsp;&nbsp;   }
&nbsp;&nbsp;   else {
&nbsp;&nbsp;      $this-&gt;layout = 'ajax';
&nbsp;&nbsp;   }
&nbsp;&nbsp;}
}</pre>
<p>Was hier passiert ist so gut wie selbsterklärend: Wenn es sich um eine asynchrone Anfrage handelt <strong>if($this-&gt;RequestHandler-&gt;isAjax())</strong>, dann wird als erstes jegliche Debugausgabe verboten. Das muss man sich unbedingt merken, sonst besteht die Gefahr auszuflippen weil <strong>debug()</strong> "aufeinmal" nichts mehr ausgibt. Auf der anderen Seite kann man aber nicht mit einem JSON String in Javascript weiterarbeiten, wenn dem ein Datenbanklog <span style="text-decoration: line-through;">am Hintern</span> an der schließenden Klammer klebt. <em>Also nicht vergessen!</em></p>
<p>Nach dem Cake weiß, das es eine Ajaxanfrage bearbeitet, wird geprüft, welche Rückantwort erwartet wird <strong>$this-&gt;RequestHandler-&gt;prefers()</strong>. Wenn diese JSON ist, werden alle <strong>viewVars</strong>, dass sind alle Variablen die über <strong>$this-&gt;set() </strong>im Controller gesetzt wurden, als JSON Objekt zurück gegeben. Damit stehen dem Javascript alle Informationen zur Verfügung die sonst im View verarbeitet werden. Toll oder?</p>
<p>Dieses <strong>die(json_encode()) </strong>finde ich noch nicht ganz so elegant wie der ganze Rest von Cake, tut aber seine Arbeit. Vielleicht hat jemand einen schöneren Ansatz?!</p>
<p>Wenn die Anfrage kein JSON erwartet, wird nicht das Standardlayout genutzt, sondern auf das Ajax umgeschaltet. Das befindet sich in der Datei <strong>app/views/layouts/ajax.ctp</strong> und besteht nur aus einer Zeile:</p>
<pre>&lt;?php echo $content_for_layout; ?&gt;</pre>
<h2>Standardlayout</h2>
<p>Um Probleme beim ändern der BaseURL zu vermeiden habe ich mir angewöhnt im Standardlayout das Tag <strong>base</strong> zu verwenden um mit jQuery schnell auf die URL zugreifen zu können. In der Datei <strong>app/views/layouts/default.ctp</strong> muss dafür folgende Zeile hinzugefügt werden:</p>
<pre>&lt;base href="http://&lt;?=$_SERVER['HTTP_HOST']?&gt;&lt;?=$this-&gt;base?&gt;/" host="http://&lt;?=$_SERVER['HTTP_HOST']?&gt;" /&gt;</pre>
<p>Ich weiß, das Attribut <strong>host</strong> verhindert eine komplette Validierung, allerdings brauche ich in einigen Fällen nur den Host und in anderen die komplette BaseURL, da zum Beispiel die erstellen Formulare mit <strong>$form-&gt;create(...) </strong>schon die BaseURL enthalten.</p>
<p>Das wird man gleich sehen.</p>
<h2>jQuery Action</h2>
<p>Da sind jetzt in wenigen Zeilen mächtige Grundlagen gelegt worden, die man mit jQuery an seiner Seite ausnutzen möchte. Im Formular zur Erstellung eines neuen Exports habe ich ein Button "Neue Exportdatei", die bei Klick in den Container <strong>div#new</strong> das Formular lädt um eine neue Datei zu speichern. Nichts leichter als das:</p>
<pre>$ajax({
&nbsp;&nbsp;url: $('base').attr('href')+'exportfiles/add/export:'+$('#ExportEditForm').attr('rel'),
&nbsp;&nbsp;dataType: 'html',
&nbsp;&nbsp;success: function(form) {
&nbsp;&nbsp;    $('#new').html(form);
&nbsp;&nbsp;}
};</pre>
<p>Als benannten Parameter wird die ID des aktuellen Exports übergeben. Im Controller steht diese Information im Array <strong>$this-&gt;params['named']</strong> zur Verfügung. Von da wird sie an den View übergeben und hier wird, sofern gesetzt ein verstecktes Formularfeld erzeugt, anstatt der gebackenen Liste der vorhandenen Exports.</p>
<p>Mit der Verwendung des Ajax-Layouts und der Angabe <strong>dataType: 'html'</strong> steht in der Variable <strong>form</strong> der Success Funktion genau das Formular zur Verfügung, welches im View definiert wurde.</p>
<h2>JSON benutzen</h2>
<p>Wenn das Formular ausgefüllt wurde, muss man das Abschicken abfangen. Leider unterstützt die <strong>Live</strong> Funktion noch nicht den Event <strong>submit</strong>, deshalb muss man sich mit einem Klick auf den Submitbutton helfen.</p>
<pre>$('#new .submit input').live('click', function() { ... });</pre>
<p>Das wirklick spannende ist natürlich auch hier die Ajaxanfrage und das Geheimnis liegt im <strong>dataType: 'json'</strong>.</p>
<pre>$.ajax({
&nbsp;&nbsp;url: $('base').attr('host')+$form.attr('action'),
&nbsp;&nbsp;data: $form.serialize(),
&nbsp;&nbsp;type: 'POST',
&nbsp;&nbsp;dataType: 'json',
&nbsp;&nbsp;success: function(content) {
&nbsp;&nbsp;  if(content.saved) {
&nbsp;&nbsp;      $form.closest('fieldset').prev().find('ul')
&nbsp;&nbsp;          .find('li.empty').remove().end()
&nbsp;&nbsp;          .append('&lt;li&gt;Neuer Eintrag &lt;b&gt;'+$form.find('input[type=text]:first').val()+'&lt;/b&gt; angelegt.&lt;/li&gt;');
&nbsp;&nbsp;  }
&nbsp;&nbsp;  else {
&nbsp;&nbsp;      alert("Leider ist ein Fehler beim speichern aufgetreten.");
&nbsp;&nbsp;  }
&nbsp;&nbsp;}
});</pre>
<p>In der Variable <strong>$form</strong> ist das jQuery Objekt gespeichert, welches das Formular widerspiegelt, dass gerade abgeschickt wurde. Da man immer wieder darauf zugreifen muss, macht es Sinn das in eine Variable zu legen um Zeit bei der Abarbeitung zu sparen.</p>
<p>In der Variable <strong>content</strong> stehen jetzt dank <em>AppController</em> und <em>RequestHandler</em> alle View-Variablen zur Verfügung. Über das Auslesen des Attributs <strong>Action</strong> des Formulars muss man sich nicht mal mehr Gedanken machen wo das Formular überhaupt abgearbeitet wird. Wichtig ist nur das in der entsprechenden Funktion ein Status gesetzt wird:</p>
<pre>$this-&gt;set('saved', true);</pre>
<h2>Fazit</h2>
<p>Nach genau dem gleichen Prinzip funktioniert auch das Löschen oder jeder andere Anwendungsfall der einem einfällt. Ich bin im Moment mal wieder begeistert von diesen beiden tollen Frameworks und den schier unerschöpflichen Möglichkeiten.</p>
<p>Demnächst gibt es dann hoffentlich mal die Vorstellung von einem fertigen Tool. Bis dahin freue ich mich auf Fragen und Verbesserungsvorschläge. Vielen Dank fürs lesen!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/11/21/ajax-in-webapplikationen-mit-cakephp-und-jquery/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Select Boxes dynamisch verändern</title>
		<link>http://www.interaktionsdesigner.de/2009/09/03/select-boxes-dynamisch-verandern/</link>
		<comments>http://www.interaktionsdesigner.de/2009/09/03/select-boxes-dynamisch-verandern/#comments</comments>
		<pubDate>Thu, 03 Sep 2009 08:00:00 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=591</guid>
		<description><![CDATA[Die praktische Auswahl von Daten über eine Selectbox ist immer wieder ein Thema. Vorallem bei den ganzen neuen Beta-Ajax-Web-Applications kommen sie einem immer wieder unter. Das Problem mit den Dingern, ist die dynamische Veränderung. Ein einfaches $("select").find("option:first").remove(); funktioniert zwar zum entfernen, aber das hinzufügen von Options ist schon schwieriger (-böser Blick zum Internet Explorer-). Und [...]]]></description>
			<content:encoded><![CDATA[<p>Die praktische Auswahl von Daten über eine Selectbox ist immer wieder ein Thema. Vorallem bei den ganzen neuen <strong>Beta-Ajax-Web-Applications</strong> kommen sie einem immer wieder unter.</p>
<p><strong>Das Problem </strong>mit den Dingern, ist die dynamische Veränderung. Ein einfaches <em>$("select").find("option:first").remove();</em> funktioniert zwar zum entfernen, aber das hinzufügen von Options ist schon schwieriger (<strong>-böser Blick zum Internet Explorer-</strong>). Und wenn die Selectbox sich dann an einer weiteren orientieren soll, dann wirds haarig.</p>
<p><strong>Die Lösung</strong> ist das Klonen und verstecken der "originalen" Box. Aus dem Klon kann man dann die überflüssigen Auswahlmöglichkeiten entfernen.</p>
<p>Und jetzt folgt der Artikel wie man das effizient anstellen könnte. Natürlich Hand in Hand mit <strong>jQuery</strong>.</p>
<p><span id="more-591"></span></p>
<h2>Das Problem</h2>
<p>Folgende Anforderung bestand im aktuellen Projekt: In der ersten Selectbox wird die Frequenz gewählt (monatlich, vierteljährlich usw.). Die zweite Box bietet eine Auswahl an Monaten zur Angabe der Dauer.</p>
<pre>&lt;select name="frequency" class="frequency"&gt;
&nbsp;&nbsp;&lt;option value="1"&gt;monatlich&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="3"&gt;vierteljährlich&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="6"&gt;halbjährlich&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="12"&gt;jährlich&lt;/option&gt;
&lt;/select&gt;</pre>
<pre>&lt;select name="months" class="months"&gt;
&nbsp;&nbsp;&lt;option value="0"&gt;0&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="1"&gt;1&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="2"&gt;2&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="3"&gt;3&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="4"&gt;4&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="5"&gt;5&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="6"&gt;6&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="7"&gt;7&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="8"&gt;8&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="9"&gt;9&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="10"&gt;10&lt;/option&gt;
&nbsp;&nbsp;&lt;option value="11"&gt;11&lt;/option&gt;
&lt;/select&gt;</pre>
<h2>Theoretisch</h2>
<p>Sehr einfach zu erklären: Der Anwender wählt in der ersten Auswahl "<em>halbjährlich</em>" - darauf hin stehen in der Monatsauswahl nur noch "<em>0</em>" und "<em>6</em>" zur Verfügung. Welcher Monat angezeigt wird, kann ganz einfach mit der <strong>Modulo-Operation</strong> berechnet werden. <a title="Pauls Blog über die Modulo Operation" href="http://www.interaktionsdesigner.de/2009/01/27/modulo-verstehen-und-benutzen/" target="_blank">Darüber hatte ich mich auch schon im Blog ausgelassen.</a></p>
<p><span>Der Klon wird mit der Klasse <em>active</em> gekennzeichnet und muss vor der nächsten Veränderung entfernt werden.<br />
</span></p>
<h2>Praktisch</h2>
<p>Jetzt wirds nett. jQuery ist natürlich im Projekt eingebunden. Sobald das Dokument geladen wurde, muss ein <strong>Eventhandler</strong> auf die Frequenzauswahl gelegt werden.</p>
<pre>jQuery(function($) {
&nbsp;&nbsp;$("select.frequency").onChange(function() {
&nbsp;&nbsp;//hier gehts weiter!!
&nbsp;&nbsp;});
});</pre>
<p><span>Diese Form ist besser als das überall gesehene <em>$(document).ready(function() {})</em>, denn die <em>$</em> Funktion muss nicht mehr zwangsläufig von jQuery besetzt sein, sondern wird dem eigenen Code als Parameter übergeben.</span></p>
<p><span>Als nächstes beschreibe ich alle Schritte zum Ändern der Selectbox <em>$("select.months")</em> einzelnd. Anschließend gibts nochmal die komplette Funktion unkommentiert im Ganzen.</span></p>
<p><span><br />
</span></p>
<pre>new_frequency = $(this).val();
$form = $(this).parents("form:first");</pre>
<p>Die Auswahl wird in <em>new_frequency</em> zwischengespeichert und das umfassende Formular in der Variable <strong>$form</strong> abgelegt, um später schneller darauf zugreifen zu können.</p>
<p>Die Variable <em>$form</em> beginnt mit einem Dollarzeichen. Damit kennzeichne ich Variablen, die ein jQuery-Objekt enthalten. So ist es leicher, diese im weiteren Verlauf zu erkennen. Dieses Vorgehensweise kann ich <strong>wärmstens empfehlen</strong>.</p>
<pre>if($("select.months.active", $form).length &gt; 0) {
&nbsp;&nbsp;$("select.months", $form).filter(".active").remove().end().show();
}</pre>
<p>Mit der Anweisung <em>$("select.months.active", $form).length</em> werden alle Selectboxen mit der Klasse <em>active</em> gesucht. Sind diese vorhanden, werden <strong>beide Selectboxen</strong> (die ggf. versteckte und der Klon) selektiert.</p>
<p>Mit der Funktion <em>filter(".active")</em> wird davon die aktive Selectbox ausgewählt und entfernt. Das <em>end() </em>setzt die Auswahl von <em>filter()</em> zurück und<em> show() </em>zeigt die übrig gebliebene Selectbox an.</p>
<p><strong>jQuery Power</strong> in einer einzigen Zeile! Aber es wird noch besser...</p>
<p>Die Selectbox mit der Klasse <em>.months</em> muss geklont werden, verändert, angezeigt und die ursprüngliche Box versteckt werden. Zum Glück braucht man dazu auch nicht mehr als <strong>eine Zeile</strong>.</p>
<pre>$("select.months", $form).clone().hide().insertBefore("select.months").end().addClass("active").find("option").filter(function(i) { return i%new_frequency == 0 ? false : true; }).remove();</pre>
<p>Sieht komplizierter aus als es ist. Man kann es praktisch Stück für Stück lesen:</p>
<ol>
<li><strong>$("select.months", $form)</strong> sucht die Selectbox im aktuellen Formular mit der Klasse <em>months</em>.</li>
<li><strong>clone()</strong> kopiert die aktuelle Auswahl und speichert sie <em>irgendwo</em> zwischen. Die Auswahl besteht <strong>nicht</strong> aus dem Klon, sondern der ursprünglichen Auswahl.</li>
<li><strong>hide() -</strong> deshalb kann die Auswahl, also die <strong>erste</strong> Selectbox, auch einfach versteckt werden. Das passiert nach dem klonen, damit der Klon nicht extra wieder eingeblendet werden muss.</li>
<li><strong>insertBefore("select.months")</strong> fügt den Klon vor der Selectbox ein.</li>
<li><strong>end()</strong> setzt die Auswahl zurück. Damit besteht die Auswahl aus dem Klon und nicht mehr aus der ursprünglichen Selectbox.</li>
<li><strong>addClass("active")</strong><em> </em>fügt die Klasse hinzu damit der Klon später erkannt werden kann.</li>
<li><strong>find("option")</strong> sucht alle Optiontags innerhalb der geklonten Selectbox.</li>
<li><strong>filter(function(i) { return i%new_frequency == 0 ? false : true; })</strong><br />
Ein großartiges Stück jQuery präsentiert hier die ganze Power der <em>filter()</em> Funktion. Dieser kann man nicht nur einen <strong>Tagnamen</strong>, eine <strong>ID</strong> oder eine <strong>Klasse</strong> übergeben, sondern auch eine <strong>anonyme Funktion</strong>! Anhand dieser wird jedes enthaltene Element überprüft. Gibt die Funktion <em>true</em> zurück, bleibt das Element in der Auswahl; ansonsten fliegt es raus.<br />
Diese Funktion besteht nur aus einer simplen Berechnung und schmeißt alle Monate raus, welche zur gespeicherten Frequenz passen.<br />
Schön, oder?</li>
<li><strong>remove()</strong> entfernt dann die übrig gebliebenen Auswahlmöglichkeiten.</li>
</ol>
<p><strong>Fertig!</strong> Eine einzige Zeile klont und bearbeitet die Auswahlmöglichkeit. Und das sooft der Benutzer möchte. So sieht die Funktion im ganzen aus:</p>
<pre>$(".frequency").change(function() {
&nbsp;&nbsp;new_frequency = $(this).val();
&nbsp;&nbsp;$form = $(this).parents("form:first");
&nbsp;&nbsp;
&nbsp;&nbsp;if($("select.months.active", $form).length &gt; 0) {
&nbsp;&nbsp;  $("select.months", $form).filter(".active").remove().end().show();
&nbsp;&nbsp;}
&nbsp;&nbsp;
&nbsp;&nbsp;$("select.months", $form)
&nbsp;&nbsp;    .clone().hide().insertBefore(".plan:visible select.months").end().addClass("active")
&nbsp;&nbsp;    .find("option").filter(function(i) { return i%new_frequency == 0 ? false : true; }).remove();
});</pre>
<h2>Fazit</h2>
<p>Ich mag jQuery. Kennst du eine besser Lösung? Mal abgesehen von Plugins die das ganze mit einem unüberschaubaren Overhead erledigen?</p>
<p>Ansonten viel Spaß mit <em>abhängigen</em> Selectboxen.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/09/03/select-boxes-dynamisch-verandern/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>CakePHP HABTM mit automatischer Vervollständigung</title>
		<link>http://www.interaktionsdesigner.de/2009/08/10/cakephp-habtm-mit-automatischer-vervollstandigung/</link>
		<comments>http://www.interaktionsdesigner.de/2009/08/10/cakephp-habtm-mit-automatischer-vervollstandigung/#comments</comments>
		<pubDate>Mon, 10 Aug 2009 09:38:33 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=578</guid>
		<description><![CDATA[Wenn man für ein beliebiges Model Posts speichern und eingeben kann, vielleicht weil man dieser Anleitung gefolgt ist, dann ist das schon ein schöner Fortschritt. Allerdings passieren immer wieder Tippfehler, und ähnlich klingende Begriffe finden sich in der Tagcloud. Da hilft nur eins: während der Eingabe dem Benutzer direkt passende Tags vorschlagen. Eine automatische Vervollständigung [...]]]></description>
			<content:encoded><![CDATA[<p>Wenn man für ein beliebiges Model Posts speichern und eingeben kann, vielleicht weil man <a title="Pauls Blog erklärt HABTM" href="http://www.interaktionsdesigner.de/2009/04/17/mit-cakephp-in-30-minuten-tags-zu-einer-tabelle-hinzufugen/#comment-375" target="_blank">dieser Anleitung</a> gefolgt ist, dann ist das schon ein schöner Fortschritt. Allerdings passieren immer wieder Tippfehler, und ähnlich klingende Begriffe finden sich in der Tagcloud.</p>
<p>Da hilft nur eins: während der Eingabe dem Benutzer <strong>direkt passende Tags vorschlagen</strong>. Eine automatische Vervollständigung wie beim Mailen in den  großen sozialen Netzwerken oder beim suchen bei Google.</p>
<p>Zum Glück haben sehr schlaue Leute schon diverse Plugins erstellt, welche einen JSON String oder eine simple Textliste verarbeiten können. Zum Beispiel <a title="jQuery Autobox2" href="http://www.bigredswitch.com/blog/2008/12/autobox2/" target="_blank">Autobox2</a>. Gleich mal die <a title="jQuery Autobox2 Demo" href="http://www.bigredswitch.com/blog/wp-content/uploads/2008/12/autobox.html" target="_blank">Demo</a> angucken, runterladen und dann ins eigene Projekt integrieren.</p>
<p><strong>So funktionierts...</strong><span id="more-578"></span></p>
<h2>Vorbereitung</h2>
<p>Bestimmt ist <strong>jQuery</strong> schon im gesamten Projekt verfügbar, wenn nicht, muss es unter <em>views/layouts/default.ctp</em> mit Hilfe des <a title="Der Javascript Helper von CakePHP" href="http://book.cakephp.org/view/207/Javascript" target="_blank">Javascript Helpers</a> eingebunden werden:</p>
<pre>echo $javascript-&gt;link('jquery.min');
echo $javascript-&gt;link('jquery.action');
echo $scripts_for_layout;</pre>
<p>Der Javascript Helper sucht die Dateien <strong>automatisch</strong> im Ordner <em>webroot/js/</em> und bindet sie ein, sofern sie vorhanden sind. In der Datei <em>jquery.action.js</em> befindet sich mein eigenes Script in dem das Verhalten der Seite gesteuert wird.</p>
<p>Sehr wichtig für den nächsten Schritt ist die Ausgabe der Variable <em>$scripts_for_layout</em>. Diese kann von jedem View aus befüllt werden. Das brauchen wir im View der<strong> Edit/Add Methode</strong> vom Model:</p>
<pre>$javascript-&gt;link(array(
&nbsp;&nbsp;'jquery.templating',
&nbsp;&nbsp;'jquery.ui.autobox.ext.js',
&nbsp;&nbsp;'jquery.ui.autobox.js'
), false);</pre>
<p>Damit werden die Javascript Dateien <strong>nicht überall</strong> eingebunden, sondern nur auf den Seiten auf denen sie auch benötigt werden.</p>
<p>Nach dem gleichen Prinzip, aber mit dem <a title="HTML Helper von CakePHP" href="http://book.cakephp.org/view/205/HTML" target="_blank">HTML Helper</a> wird die Autobox CSS Datei eingebunden.</p>
<pre>$html-&gt;css('jquery.ui.autobox.css', null, array(), false);</pre>
<p>Und da wir gerade dabei sind, bietet es sich an diese Datei zu öffnen und in der <strong>Zeile 66</strong> den Pfad zum <em>close.gif</em> anzugeben. Wenn sich alles in den korrekten Ordnern befindet ist es einfach:</p>
<pre>background: url('../img/close.gif');</pre>
<p>Aussehen wird es gut, aber im Moment weiß das Script nicht wo es sich befindet. Damit die <strong>Baseurl</strong> auch in Javascript zur Verfügung steht, habe ich mir angewöhnt im HTML Gerüst das Basetag zu erweitern:</p>
<pre>&lt;base href="http://&lt;?=$_SERVER['HTTP_HOST']?&gt;&lt;?=$this-&gt;base?&gt;/" host="http://&lt;?=$_SERVER['HTTP_HOST']?&gt;" /&gt;</pre>
<p>Mit dieser Zeile im Layout wird später vieles einfacher. Leider validiert es mit dem Attribut host nicht mehr, wer darauf Wert legt, muss sich also eine andere Lösung überlegen.</p>
<h2>jQuery Action</h2>
<p>Jetzt gehts los und das Plugin wird initalisiert. Ich mache das gerne in der Datei <em>jquery.action.js</em>. Um es auf die schnelle zu testen, kopiere ich einfach das Beispiel aus der Demo. Gemäß des letzten Posts zu HABTM heißt mein Feld <strong>temp_tags</strong>. Das resultiert in der ID <strong>ModelTempTags</strong>:</p>
<pre>jQuery(function($) {
&nbsp;&nbsp;
&nbsp;&nbsp;var list1 = [{text: 'Curious George'}, {text: 'George of the Jungle'}, {text: 'Felix the Cat'}];
&nbsp;&nbsp;
&nbsp;&nbsp;$('#ModelTempTags').autobox({
&nbsp;&nbsp;&nbsp;&nbsp;list: list1,
&nbsp;&nbsp;&nbsp;&nbsp;match: function(typed) { return this.text.match(new RegExp(typed, "i")); },
&nbsp;&nbsp;&nbsp;&nbsp;insertText: function(obj) { return obj.text },
&nbsp;&nbsp;&nbsp;&nbsp;templateText: "&lt;li&gt;Hey: &lt;%= text %&gt;&lt;/li&gt;"
&nbsp;&nbsp;});
});</pre>
<p>Seite neuladen, und... herrlich! <strong>Funktioniert</strong>. Nur eine Kleinigkeit: Die vorhandenen Tags werden nicht übernommen. Zum Glück hilft eine einzige Zeile um das Problem zubeheben. Innerhalb der Autobox Konfiguration wird die Vorbelegung (<strong>prevals</strong>) angegeben:</p>
<pre>prevals: $('#LinkTempTags').val().split(', ')</pre>
<p>Mit dieser Zeile wird der Inhalt ausgelesen, beim Komma geteilt und der Autobox als Vorbelegung übergeben. Dann kanns jetzt losgehen mit der <strong>asynchronen Datenübertragung</strong>. Das Attribut <strong>list</strong> in der Autobox Konfiguration wird ausgetauscht mit einer URL:</p>
<pre>ajax: $('base').attr('href')+'tags/index',</pre>
<p>Damit wird im Parameter <strong>val</strong> die Benutzereingabe an den <em>Tags Controller</em> geschickt. Und der muss jetzt noch reagieren.</p>
<h2>Antworten mit CakePHP</h2>
<p>Der <strong>Controller Tags</strong> ist dafür zuständig eine Liste zu genierieren. In der Variable <strong>$this-&gt;params['url']['val']</strong> steht die Benutzereingabe zur Verfügung. Mit der wird eine Datenbankabfrage gestartet:</p>
<pre>$tags = $this-&gt;Tag-&gt;find('all', array('conditions' =&gt; array('title LIKE' =&gt; $this-&gt;params['url']['val'].'%'), 'order' =&gt; 'Tag.title ASC'));</pre>
<p>Mit Hilfe der <strong>RequestHandler Componente</strong> wird ein Ajaxaufruf erkannt. Dafür muss die Componente im <strong>Controller</strong> eingebunden werden.</p>
<pre>class TagsController extends AppController {
&nbsp;&nbsp;var $components = array('RequestHandler');</pre>
<p>In der<strong> Index Funktion</strong> wird bei einem Ajaxaufruf der Renderingprozess gestoppt und purer Text zurück gegeben.</p>
<pre>if($this-&gt;RequestHandler-&gt;isAjax()) {
&nbsp;&nbsp;Configure::write('debug', 0);
&nbsp;&nbsp;$this-&gt;autoRender = false;
&nbsp;&nbsp;return '['.implode(',', Set::format($tags, '{text: "{0}"}', array('{n}.Tag.title'))).']';
}</pre>
<p>Besondere Aufmerksamkeit verdient die letzte Zeile. <em>Implode</em> bekommt ein <em>Array</em>, das aus dem Core Utility <strong>Set</strong> stammt: <a title="CakePHP Set::format()" href="http://book.cakephp.org/view/672/format" target="_blank">Format</a>. Eine großartige Funktion die ein <strong>verschachteltes Array umformatiert</strong>. In diesem Fall zu JSON Objekten, welche dann mit einem Komma verbunden werden.</p>
<p>Das wars! Kaum zu glauben, aber ein Neuladen macht die Einbindung komplett. Und den Webentwickler fröhlich!</p>
<h2>Speichern im Model</h2>
<p>Das Plugin erstellt für jeden Tag ein verstecktes Formularfeld, welches den gleichen Namen hat wie das Ursprungsfeld. Damit die eingegebenen Daten übertragen werden, muss der Name auf ein Array hinweise:</p>
<pre>echo $form-&gt;input('temp_tags', array('name' =&gt; 'data[Model][temp_tags][]');</pre>
<p>Der Trick sind die <strong>beiden eckigen Klammern am Ende</strong>. Damit steht die Eingabe im Model als Array zur Verfügung:</p>
<pre>debug($this-&gt;data['temp_tags']);</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/08/10/cakephp-habtm-mit-automatischer-vervollstandigung/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Nette Erweiterung für jedes Menü mit jQuery</title>
		<link>http://www.interaktionsdesigner.de/2009/07/27/nette-erweiterung-fur-jedes-menu-mit-jquery/</link>
		<comments>http://www.interaktionsdesigner.de/2009/07/27/nette-erweiterung-fur-jedes-menu-mit-jquery/#comments</comments>
		<pubDate>Mon, 27 Jul 2009 12:09:29 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=554</guid>
		<description><![CDATA[Es gibt kaum noch ein Menü, welches den Nutzer nicht mit einem Mouseover-Status über seine aktuelle Position informiert. Soweit so gut. Meistens sieht es so aus: Der Nutzer klickt auf den Menüpunkt, der Mouseover-Status verschwindet, die Seite verschwindet, die Seite baut sich wieder auf und der eben geklickte Menüpunkt ist dauerhaft hervorgehoben. Ganz normal. Aber: [...]]]></description>
			<content:encoded><![CDATA[<p>Es gibt kaum noch ein Menü, welches den Nutzer nicht mit einem <strong>Mouseover-Status</strong> über seine aktuelle Position informiert. Soweit so gut. Meistens sieht es so aus: Der Nutzer klickt auf den Menüpunkt, der Mouseover-Status verschwindet, die Seite verschwindet, die Seite baut sich wieder auf und der eben geklickte Menüpunkt ist dauerhaft hervorgehoben.</p>
<p>Ganz normal. Aber: Mit ein bisschen jQuery lässt sich der Mouseover-Status schon <strong>bei einem Klick festsetzen</strong>. Das sieht gut aus und wirkt fast wie ein dynamisches nachladen der Seite.</p>
<p>Ansehen kann man sich diesen Effekt zum Beispiel hier im Hauptmenü oder auf der Seite von <a title="Isabel von Roon" href="http://www.freies-herz.de" target="_blank">Isabel von Roon</a>. Wie der Effekt eingebaut wird, was <strong>find()</strong>, <strong>filter()</strong> und <strong>siblings()</strong> bedeutet und wie man den aktiven Menüpunkt hervorhebt wird in diesem Artikel besprochen.</p>
<p><span id="more-554"></span></p>
<h2>Die HTML Grundage</h2>
<p>Das oben genannte Beispiel ist zu simpel, deshalb machen wir es etwas komplizierter undzwar mit einem verschachtelten Menü.</p>
<pre>&lt;div id="menu"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#"&gt;Punkt 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="#"&gt;Punkt 2&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#"&gt;Punkt 2.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Punkt 2.2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Punkt 2.3&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="#"&gt;Punkt 3&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="#"&gt;Punkt 3.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Punkt 3.2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Punkt 3.3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Punkt 3.4&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="#"&gt;Punkt 4&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;</pre>
<p>Die Funktionsweise ist klar: Bei einem Klick ins Menü darf sich nur das jeweilige Menü verändern (die Nachbarn). Einfach die Aktiv-Klasse generell zu entfernen und dem angeklickten Menüpunkt zuweisen hätte zur Folge, dass beim Klick ins Submenü der darüber liegende Punkt seine Aktiv-Klasse verlieren würde.</p>
<p><a title="Pauls Blog jQuery Demo Menü" href="http://interaktionsdesigner.de/stuff/mainmenu.php" target="_blank">Demo: Hier ansehen was ich meine</a>.</p>
<h2>Lovley jQuery</h2>
<p>Wer jetzt an ein kompliziertes und umfangreiches Script denkt, der hat noch nicht genug mit jQuery gearbeitet. Denn die Anforderungen benötigen nur <strong>eine einzige Zeile</strong>:</p>
<pre>jQuery(function($) {
&nbsp;&nbsp;$('#menu a').click(function() {
&nbsp;&nbsp;  $(this).blur().parents('li').siblings('li:has(.active)').find('a').removeClass('active').end().end().end().addClass('active');
&nbsp;&nbsp;  return false;
&nbsp;&nbsp;});
});</pre>
<p>Was hier passiert ist wird jetzt Stück für Stück durchleuchtet:</p>
<p><strong>jQuery(function($) {})</strong><br />
Diese Funktion wird aufgerufen, sobald das DOM fertig geladen wurde. Als Parameter wird das jQuery-Framework übergeben und in der Variable <strong>$</strong> gespeichert. Mit dieser Schreibweise hat man weniger Stress, wenn ein Kollege auf die Idee kommt ein zweites Javascript Framework einzubinden (<em>Grrr...</em>)</p>
<p><strong>$('#menu a').click(function() {})</strong><br />
Mit dieser Anweisung werden alle Links im Element mit der ID <em>Menü</em> selektiert und jeder einzelne bekommt einen Eventhandler zugewiesen: <em>click</em>.</p>
<p>In der Funktion ist der angeklickte Link in der Variable <em>this</em> gespeichert.</p>
<p><strong>$(this)</strong><br />
Mit diesem Konstrukt wird der angeklickte Menüpunkt in ein jQuery-Objekt verwandelt, und liegt als einziges auf dem Elementstack.</p>
<p><strong>.blur()</strong><br />
entfernt den gepunkteten Rahmen um den Link. Eine alternative Möglichkeit wäre <strong>outline:none</strong> im CSS anzugeben. Je nach belieben.</p>
<p><strong>.parents('li')</strong><br />
Jetzt wird es spannend! Diese Funktion wählt alle Elternelemente aus. Mit der Einschränkung, dass sie vom Typ <em>li</em> sind.</p>
<p><strong>.siblings('li:has(.active)')</strong><br />
Siblings steht für Nachbarn. Diese Funktion findet nur die benachbarten Elemente. Klickt man also auf ein Untermenü, befinden sich nach <strong>siblings()</strong> alle LI Elemente <em>dieser</em> Liste im Stack.</p>
<p>Mit der Übergabe eines Selectors kann man die Nachbarn auch gleich einschränken. <strong>li:has('.active')</strong> steht für alle LI Elemente welche ein Objekt der Klasse<em> .active</em> beinhalten.</p>
<p>Alternativ könnte man auch die Funktion <strong>filter()</strong> benutzen. Mit <strong>siblings('li')</strong> werden alle benachbarten LI Elemente ausgewählt. Anschließend mit <strong>filter(':has('.active')')</strong> auf das enthalten sein eines aktiven Links geprüft. Und genau hier liegt der Unterschied zwischen filter() und find():</p>
<p><strong>Filter()</strong> <em>filtert</em> die Elemente im Stack. <strong>Find()</strong> <em>sucht</em> innerhalb der Elemente im Stack. Logisch, oder?</p>
<p>Mit <strong>siblings('li:has(.active)') </strong>sparen wir uns eine Anweisung und im Stack befinden sich alle benachbarten Elemente, die einen aktiven Link beinhalten. Weiter gehts.</p>
<p><strong>.find('a')</strong><br />
Innerhalb des LI Elements wird jetzt nach dem aktiven Link gesucht und dieser ausgewählt.</p>
<p><strong>.removeClass('active')</strong><br />
Entfernt die Klasse von dem Link. (Bitte daran denken keinen Punkt vor der Klasse anzugeben!)</p>
<p><strong>.end()</strong><br />
Mit der Funktion <strong>end()</strong> wird eine Veränderung im Elementstack rückgängig gemacht. Dabei bezieht sich das <strong>end()</strong> immer auf die als <em>letztes getätigten Veränderungen</em>.</p>
<p>Das erste <strong>end()</strong> macht also das <strong>find('a')</strong> rückgängig, im Stack befinden sich jetzt wieder die LI Elemente mit einem (ehemals) aktiven Link.</p>
<p><strong>.end()</strong><br />
Als nächstes werden die Nachbarn aus dem Stack geworfen. (<em>siblings</em>)</p>
<p><strong>.end()</strong><br />
Und jetzt noch die Elternelemente raus und wieder zurück zum Kind: <strong>$(this)</strong>. Wir befinden uns jetzt wieder ganz am Anfang der Auswahl und es bleibt nur noch eine Funktion:</p>
<p><strong>.addClass('active')</strong><br />
Diese Funktion weißt dem angeklickten Link die passende Klasse zu, damit beim nächsten Klick das ganze Spiel wieder von vorne beginnen kann.</p>
<h2>Das wars!</h2>
<p>Im Beispiel habe ich noch ein <strong>return false;</strong> hinzugefügt. Diese Anweisung verhindert, dass der Browser sein Standardverhalten abspielt (bei Links die angeklickte Seite aufrufen). In einer wirklichen Umgebung darf das natürlich nicht vorkommen.</p>
<p>Ich hoffe ihr konntet etwas aus diesem Artikel mitnehmen. Weitere jQuery <strong>Fragen</strong> und <strong>Beispiellösungen</strong> stelle ich gerne auf Anfrage vor. Nur rein in die Kommentare.</p>
<p>Frohes verketten!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/07/27/nette-erweiterung-fur-jedes-menu-mit-jquery/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Viele Elemente dynamisch aktualisieren</title>
		<link>http://www.interaktionsdesigner.de/2009/07/24/viele-elemente-dynamisch-aktualisieren/</link>
		<comments>http://www.interaktionsdesigner.de/2009/07/24/viele-elemente-dynamisch-aktualisieren/#comments</comments>
		<pubDate>Fri, 24 Jul 2009 18:10:57 +0000</pubDate>
		<dc:creator>Paul</dc:creator>
				<category><![CDATA[Entwicklung]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.interaktionsdesigner.de/?p=555</guid>
		<description><![CDATA[Ein aktuelles Projekt interagiert auf sehr starke Art und Weise mit dem Benutzer. Das bedeutet neben einer anpassbaren Oberfläche wird der Benutzer ständig über den Status diverser Objekte auf dem laufenden gehalten. Dabei können sich verschiedene Objekte untereinander beeinflussen. jQuery bietet ja ein tolles Ajaxkonstrukt an mit dem es kein Problem ist Daten vom Server [...]]]></description>
			<content:encoded><![CDATA[<p>Ein aktuelles Projekt interagiert auf sehr starke Art und Weise mit dem Benutzer. Das bedeutet neben einer anpassbaren Oberfläche wird der Benutzer <strong>ständig über den Status</strong> diverser Objekte auf dem laufenden gehalten. Dabei können sich verschiedene Objekte untereinander beeinflussen.</p>
<p>jQuery bietet ja ein tolles Ajaxkonstrukt an mit dem es kein Problem ist Daten vom Server zu holen. Aber auf die Dauer wird es doch etwas nervig immer wieder zu tippen.</p>
<p>Und weil <strong>"objektorientiertes Javascript"</strong> und selber entwickeln so viel <strong>Spaß</strong> macht, habe ich mir eine alternative Lösung einfallen lassen: Im Projekt werden an geeigneter Stelle <em>Elemente</em> erzeugt, die alle benötigten Informationen enthalten.</p>
<p>Diese werden in einem <em>Updater</em> registriert, welcher sich um die regelmäßige Abfrage kümmert. Das war die Theorie und jetzt kommt die <strong>Praxis</strong>.</p>
<p><span id="more-555"></span></p>
<h2>Das Element</h2>
<p>Ein Element besteht aus <strong>einer URL</strong> auf dem Server, <strong>dem Typ</strong> der zurückerwartenden Daten und <strong>einer Funktion</strong> die aufgerufen wird, wenn die Aktion erfolg hatte. Im Quelltext sieht das so aus:</p>
<pre>var Paul = new element('count/paul', 'json', function(j) {
&nbsp;&nbsp;alert("Paul hat "+j.count+" Einträge.");
});</pre>
<p>Diese Elemente können überall im Projekt erzeugt werden. Alternativen zu <em>json</em> sind alle Formen die jQuerys Ajaxfunktion erkennt: <em>Html</em>, <em>Text</em> und <em>Jsonp</em>.</p>
<h2>Der Updater</h2>
<p>Ganz am Anfang (oder am Ende, je nach Geschmack) wird der Updater initalisiert:</p>
<pre>var Updater = new updater();</pre>
<p>Der Updater akzeptiert zwei Einstellungen:</p>
<pre>Updater.intervall = 1000; // Zeit bis zur nächsten Abfrage
Updater.baseurl = "http://kleiner.test.com/"; // String der vor jede URL geschrieben wird</pre>
<p>Und hat eine handvoll Funktionen für den dynamischen Umgang mit dem Objekt:</p>
<pre>Updater.start(); // Startet den Vorgang sofern Elemente vorhanden sind
Updater.stop(); // Stoppt die Aktualisierungen
Updater.isRunning(); // Prüft ob sich der Updater in Aktion befindet
Updater.log(); // Schreibt alle gespeicherten Elementen in die Konsole
Updater.register(Element); // Fügt ein neues Element hinzu
Updater.remove(Element); // Entfernt ein Element</pre>
<h2>Die Verwendung</h2>
<p>Dem Updater werden <strong>beliebig viele Elemente</strong>, auch zur Laufzeit, übergeben:</p>
<pre>Updater.register(Paul);
Updater.start();</pre>
<p>Das wars! Die Funktion im übergebenen Element wird jetzt, im erfolgsfall, alle 1000 Millisekunden aufgerufen.</p>
<h2>Die technische Seite vom Element</h2>
<p>So sieht das Element aus:</p>
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><span style="color:#3f7bc6;">function</span> <span style="color:#4080ff;">element</span>(url, dataType, successFunction) {

  <span style="color:#e27600;">//einstellungen
</span>  <span style="color:#3f7bc6;">this</span>.url = url;
  <span style="color:#3f7bc6;">this</span>.data = {};
  <span style="color:#3f7bc6;">this</span>.type = <span style="color:#df2800;">'get'</span>
  <span style="color:#3f7bc6;">this</span>.dataType = dataType || <span style="color:#df2800;">'html'</span>;

  <span style="color:#e27600;">//callback um die daten zu bekommen
</span>  <span style="color:#3f7bc6;">this</span>.getData = <span style="color:#3f7bc6;">function</span>() {
    <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">this</span>.data;
  }

  <span style="color:#e27600;">//callback um an die URL zu kommen
</span>  <span style="color:#e27600;">//erster parameter ist die im Updater gespeicherte baseurl
</span>  <span style="color:#3f7bc6;">this</span>.getUrl = <span style="color:#3f7bc6;">function</span>(BASEURL) {
    <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">this</span>.url;
  }

  <span style="color:#e27600;">//callback wenn eine antwort vorliegt
</span>  <span style="color:#e27600;">//erster Parameter ist die Antwort vom Server, zweiter der Updater an sich
</span>  <span style="color:#3f7bc6;">this</span>.success = successFunction || <span style="color:#3f7bc6;">function</span>(html, Updater) {
    console.<span style="color:#4080ff;">log</span>(<span style="color:#df2800;">'Anfrage #'</span>+Updater.counter);
    console.<span style="color:#4080ff;">log</span>(html);
  }

  <span style="color:#e27600;">//callback wenn ein fehler aufgetreten ist
</span>  <span style="color:#e27600;">//erster parameter ist der Updater an sich um dieses Element daraus zu entfernen
</span>  <span style="color:#3f7bc6;">this</span>.error = <span style="color:#3f7bc6;">function</span>(Updater) {
    <span style="color:#e27600;">//bei einem fehler entfernt sich das objekt selbstständig aus dem updater
</span>    Updater.<span style="color:#4080ff;">remove</span>(<span style="color:#3f7bc6;">this</span>);
  }

}</pre>
<p>Zwei Funktionen sind wichtig: <strong>getData()</strong> wird aufgerufen, bevor die Anfrage an den Server gesendet wird. Der Rückgabewert der Funktion wird an den Server geschickt.</p>
<p>Damit ist es also möglich, auf <strong>Veränderungen im Frontend</strong> zu reagieren. Zum Beispiel:</p>
<pre>Paul.getData = function() {
&nbsp;&nbsp;return {relation: $('body').attr('rel'), project: $('#project_selector').val()};
}</pre>
<p>Schön, oder? Natürlich kann man sich noch viel einfallen lassen. Etwa mit der URL, welche mit Hilfe der Funktion <strong>getUrl()</strong> aus dem Element geholt wird. Es soll ja Fälle geben (<em>CakePHP</em>), bei denen es leichter ist Daten per URL zu übergeben:</p>
<pre>Paul.getUrl = function(BASEURL) {
&nbsp;&nbsp;return BASEURL+this.url+'/'+$('#team .paul').attr('rel');
}</pre>
<p>Die Funktionen <strong>success(return, Updater)</strong> und <strong>error(Updater)</strong> erklären sich von selbst und haben als Parameter immer das umfassende Element dabei. Deshalb kann sich das Objekt in einem Fehlerfall auch selbstständig entfernen. Es könnte auch das Intervall verändern, ein neues Objekt hinuzfügen, oder was auch immer!</p>
<h2>Die technische Seite vom Updater</h2>
<p>Als erstes der gesamte Block Quelltext:</p>
<pre style="text-align:left;color:#000000; background-color:#ffffff; border:solid black 1px; padding:0.5em 1em 0.5em 1em; overflow:auto;font-size:small; font-family:monospace; "><span style="color:#3f7bc6;">function</span> <span style="color:#4080ff;">updater</span>() {

  <span style="color:#e27600;">//einstellungen
</span>  <span style="color:#3f7bc6;">this</span>.intervall = <span style="color:#fe2d00;">1000</span>;
  <span style="color:#3f7bc6;">this</span>.elements = <span style="color:#3f7bc6;">new</span> <span style="color:#4080ff;">Array</span>();
  <span style="color:#3f7bc6;">this</span>.active_element = <span style="color:#fe2d00;">0</span>;
  <span style="color:#3f7bc6;">this</span>.baseurl = BASEURL;

  <span style="color:#3f7bc6;">this</span>.running = <span style="color:#3f7bc6;">false</span>;
  <span style="color:#3f7bc6;">this</span>.counter = <span style="color:#fe2d00;">0</span>;
  <span style="color:#3f7bc6;">this</span>.timeout = <span style="color:#df2800;">''</span>;

  <span style="color:#e27600;">//alle elemente anzeigen
</span>  <span style="color:#3f7bc6;">this</span>.log = <span style="color:#3f7bc6;">function</span>() {
    console.<span style="color:#4080ff;">log</span>(<span style="color:#3f7bc6;">this</span>.elements);
  }

  <span style="color:#e27600;">//neues element hinzufügen
</span>  <span style="color:#3f7bc6;">this</span>.register = <span style="color:#3f7bc6;">function</span>(element) {
    <span style="color:#3f7bc6;">this</span>.elements.<span style="color:#4080ff;">push</span>(element);
  };

  <span style="color:#e27600;">//ein element entfernen
</span>  <span style="color:#3f7bc6;">this</span>.remove = <span style="color:#3f7bc6;">function</span>(element) {
    <span style="color:#3f7bc6;">for</span>(i <span style="color:#3f7bc6;">in</span> <span style="color:#3f7bc6;">this</span>.elements) {
      <span style="color:#3f7bc6;">if</span>(<span style="color:#3f7bc6;">this</span>.elements[i] == element) {
        <span style="color:#e27600;">//delete this.elements[i];
</span>        <span style="color:#3f7bc6;">this</span>.elements.<span style="color:#4080ff;">splice</span>(i, <span style="color:#fe2d00;">1</span>);
        <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">true</span>;
      }
    }
    <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">false</span>;
  }

  <span style="color:#e27600;">//elemente durchgehen
</span>  <span style="color:#3f7bc6;">this</span>.process = <span style="color:#3f7bc6;">function</span>(active_element) {
    <span style="color:#3f7bc6;">this</span>.active_element = active_element || <span style="color:#fe2d00;">0</span>;

    <span style="color:#3f7bc6;">if</span>(<span style="color:#3f7bc6;">this</span>.elements[<span style="color:#3f7bc6;">this</span>.active_element]) {
      _this = <span style="color:#3f7bc6;">this</span>;
      _elem = <span style="color:#3f7bc6;">this</span>.elements[<span style="color:#3f7bc6;">this</span>.active_element];

      <span style="color:#e27600;">//ajaxaufruf
</span>      $.<span style="color:#4080ff;">ajax</span>({
        <span style="color:#e27600;">//einstellungen
</span>        url: _elem.<span style="color:#4080ff;">getUrl</span>(_this.baseurl),
        data: _elem.<span style="color:#4080ff;">getData</span>(),
        type: _elem.type,
        dataType: _elem.dataType,

        <span style="color:#e27600;">//hat funktioniert!
</span>        success: <span style="color:#3f7bc6;">function</span>(result) {
          _elem.<span style="color:#4080ff;">success</span>(result, _this);
        },

        <span style="color:#e27600;">//ein fehler
</span>        error: <span style="color:#3f7bc6;">function</span>(error) {
          <span style="color:#e27600;">//das object soll entscheiden wie es weitergeht
</span>          _elem.<span style="color:#4080ff;">error</span>(_this);
        },

        <span style="color:#e27600;">//wie auch immer soll das nächste element aufgerufen werden
</span>        complete: <span style="color:#3f7bc6;">function</span>() {
          _this.<span style="color:#4080ff;">next</span>();
        }
      });     

    }
    <span style="color:#3f7bc6;">else</span> {
      <span style="color:#3f7bc6;">this</span>.active_element = <span style="color:#fe2d00;">0</span>;
      <span style="color:#3f7bc6;">this</span>.<span style="color:#4080ff;">next</span>();
    }
  };

  <span style="color:#e27600;">//ruft process erneut auf, verzögert um intervall mit dem activen element
</span>  <span style="color:#3f7bc6;">this</span>.next = <span style="color:#3f7bc6;">function</span>() {
    <span style="color:#3f7bc6;">if</span>(<span style="color:#3f7bc6;">this</span>.<span style="color:#4080ff;">isRunning</span>()) {
      <span style="color:#e27600;">//nur so zum spaß
</span>      <span style="color:#3f7bc6;">this</span>.counter++;

      <span style="color:#e27600;">//zeit die bis zum nächsten aufruf vergehen soll
</span>      <span style="color:#3f7bc6;">var</span> _intervall = <span style="color:#fe2d00;">0</span>;

      <span style="color:#e27600;">//nächstes element suchen
</span>      <span style="color:#3f7bc6;">this</span>.active_element++;

      <span style="color:#e27600;">//schon am ende angelangt?
</span>      <span style="color:#3f7bc6;">if</span>(<span style="color:#3f7bc6;">this</span>.active_element &gt;= <span style="color:#3f7bc6;">this</span>.elements.length) {
        <span style="color:#3f7bc6;">this</span>.active_element = <span style="color:#fe2d00;">0</span>;

        <span style="color:#e27600;">//längerer intervall bis es wieder los geht
</span>        _intervall = <span style="color:#3f7bc6;">this</span>.intervall;
      }

      <span style="color:#e27600;">//erneut aufrufen
</span>      _this = <span style="color:#3f7bc6;">this</span>;
      _this.timeout = window.<span style="color:#4080ff;">setTimeout</span>(<span style="color:#3f7bc6;">function</span>() {
        _this.<span style="color:#4080ff;">process</span>(_this.active_element);
      }, _intervall);
      <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">true</span>;
    }
    <span style="color:#3f7bc6;">else</span> {
      <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">false</span>;
    }
  }

  <span style="color:#e27600;">//startet den vorgang
</span>  <span style="color:#3f7bc6;">this</span>.start = <span style="color:#3f7bc6;">function</span>() {
    <span style="color:#3f7bc6;">this</span>.active_element = -<span style="color:#fe2d00;">1</span>;
    <span style="color:#3f7bc6;">this</span>.running = <span style="color:#3f7bc6;">true</span>;
    <span style="color:#3f7bc6;">this</span>.counter = <span style="color:#fe2d00;">0</span>;
    <span style="color:#3f7bc6;">this</span>.<span style="color:#4080ff;">next</span>();
  }

  <span style="color:#e27600;">//stoppt das updaten
</span>  <span style="color:#3f7bc6;">this</span>.stop = <span style="color:#3f7bc6;">function</span>() {
    <span style="color:#3f7bc6;">this</span>.running = <span style="color:#3f7bc6;">false</span>;
    window.<span style="color:#4080ff;">clearTimeout</span>(<span style="color:#3f7bc6;">this</span>.timeout);
    <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">this</span>.counter;
  }

  <span style="color:#e27600;">//prüfen ob der Updater läuft
</span>  <span style="color:#3f7bc6;">this</span>.isRunning = <span style="color:#3f7bc6;">function</span>() {
    <span style="color:#3f7bc6;">return</span> <span style="color:#3f7bc6;">this</span>.running;
  }
}</pre>
<p>Nett, oder? Was ist passiert? Die Einstellungen sollten klar sein. In der Variable <strong>timeout</strong> wird der aktuelle Timeout gespeichert. Damit kann man beim Abbrechen alle Verbindungen trennen, anstatt noch eine ausführen zu müssen.</p>
<p><strong>Log()</strong>, <strong>register()</strong> und <strong>remove()</strong> sind an sich uninteressant. Außer eine Erwähnung, dass der Operator <em>delete</em> ein Element zwar entfernt, aber danach nicht das Array neu durchnummeriert. Da liegen dann lauter "<em>undefinied</em>" rum. Deshalb habe ich <strong>splice()</strong> benutzt. Funktioniert super.</p>
<p>Spannend wird es in der Funktion <strong>process()</strong>. Diese wird für jedes Element aufgerufen. Als Parameter erwartet sie den <strong>Key</strong> vom aktiven Element. Wenn ein Element vorhanden ist, wird die Ajaxabfrage ausgeführt.</p>
<p>Innerhalb der Funktion Ajax() bekommt das <em>this</em> eine neue Bedeutung. Deshalb musste ich es in <em>_this</em> zwischenspeichern.</p>
<p>Die Funktion ruft also, je nach umstand, <strong>success()</strong> oder <strong>error()</strong> auf und übergibt die Kontrolle an das Element. In jedemfall wird aber <strong>_this.next()</strong> durch die Funktion <strong>Ajax.complete()</strong> aufgerufen. Diese wird, unabhängig vom Status, nach jeder Ajaxanfrage ausgeführt.</p>
<p>In der Funktion <strong>next()</strong> wird das nächste Objekt gesucht. Sofern der Updater läuft <strong>isRunning() </strong>wird der Counter erhöht und das Intervall auf Null gesetzt. Das passiert, weil alle registrierten Elemente sofort nacheinander aufgerufen werden sollen und erst anschließend die in <em>this.intervall</em> angegebene Zeit gewartet wird.</p>
<p>Wenn alle Elemente durchgearbeitet wurden, wird der <strong>Index auf Null</strong> und der <strong>Intervall auf die eingestellte Zeit</strong> gesetzt.</p>
<p>Anschließend gibt es ein klassisches <strong>window.setTimeout()</strong> um das ganze Spielchen wieder von vorne anzufangen.</p>
<p><strong>Start()</strong>, <strong>stop()</strong> und <strong>isRunning()</strong> erklären sich wieder von selbst, und damit ist die Vorstellung beendet!</p>
<h2>Fazit</h2>
<p>Der große nächste Erweiterungsschritt ist in dem Updater alle Anfragen zu sammeln und<strong> in einem Packet</strong> an den Server zu schicken. Die Antwort kann dann direkt vom Updater auseinander gefriemelt und an die verschiedenen Elemente verteilt werden.</p>
<p>Dazu muss der Server allerdings noch vorbereitet werden, und bisher sind es noch nicht viele Benutzer. Aber ich denke es ist eine <strong>solide Grundlage</strong> die gut erweitert werden kann.</p>
<p>Das war auch die Intention dieses Postings. Hast du einen <strong>Vorschlag</strong>? <strong>Kritik</strong>? <strong>Hinweis</strong>? Freue mich über Kommentare und Inspiration. Danke für die Aufmerksamkeit.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.interaktionsdesigner.de/2009/07/24/viele-elemente-dynamisch-aktualisieren/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

