Eigentlich geht es nicht direkt nur um Waschmittel, sondern um den Einsatz aller Putzutensilien und dem dazugehörigen Putzschrank.

Als ich letztes Jahr anfing CakePHP zu nutzen, gab es da schon einen Helper für Ajax eigentlich insbesondere für Prototype. Leider hatte ich immer das Problem, dass der nie genau das tun wollte, was ich gerade vorhatte. Zum Beispiel gab es damals noch keine saubere Umsetzung ein Formular per Ajax abzuschicken, aber genauso ohne Javascript zu funktionieren. Heute findet man auch im trac etwas zu ‘degradable ajax’.

Das ändert aber nichts daran, dass die Javascript Generierung nach wie vor nicht alle Möglichkeiten bietet und das HTML mit Script-Blöcken und hart codierten Eventhandlern durchsetzt. Was wenn ich eine Applikation bauen möchte, in der CSS und Javascript ‘degradable’ sein sollen, sehr unschön ist. Dieses Problem zu lösen würde mir ein Maximum an Usability und gleichzeitiger Accessibility bieten.

Davon abgesehen ist die Pflege des Javascript Codes schwierig, wenn die Funktionen über viele Views hinweg verstreut in der Gegend herum liegen. Da wird es auch schwer sein Javascript test-getrieben zu entwickeln. Ich war zwar froh mit CakePHP ein Framework gefunden zu haben, mit dessen Konventionen ich sehr gut leben und meinen PHP Code besser strukturieren kann, aber das Javascript ist und bleibt chaotisch.

Was also, wenn ich mein Javascript komplett von meinen Templates trennen möchte? Wieso ist mein Javascript nicht auch nach MVC getrennt? Warum übertrage ich nicht einfach die Ideen aus Rails und CakePHP auf mein Javascript?

Diese Fragen schwirrten lange in meinem Kopf herum. Mittlerweile habe ich mir ein rudimentäres Javascript Framework geschrieben, der größte Dank geht dabei an jQuery! Mit jQuery kann ich mein Javascript komplett vom HTML trennen ohne für jedes Element Ids vergeben zu müssen. Die Notation ist ähnlich wie bei Prototype über $(), jedoch kann man anstelle der Id auch eine CSS ähnliche Notation verwenden (das hat uns ja auch schon behaviour gezeigt). jQuery ist mit 17kb recht kompakt, performant und bietet neben Ajax, Effekten und Eventhandling die Möglichkeit recht schicke Plugins zu schreiben.

Derzeit habe ich also bei einer Cake Applikation kein einziges ‘onclick, onsubmit usw.’ und ausser den im Head eingebundenen Javascript Dateien keinen weiteren Skript-Block in meinen Views. Die Initialisierung des Javascripts läuft, nach dem Laden des DOMs über jQuery’s document.ready(). Das Startup Skript lädt die Routen für die Applikation, initialisiert über einen Dispatcher den jeweiligen Controller und startet eine Methode, genauso geht es ja auch in Cake zu. In der Controller Methode werden dann die Events dieser Seite registriert und die für die Intialisierung der Seite notwendigen Model bzw. View Methoden direkt aufgerufen.

Die Konzepte Model, View, Controller in Javascript zu übertragen ist recht einfach. Zuerst einmal wird ein Request durch ein Event ausgelöst. D.h. der Controller startet die für das Event registrierte Funktion und je nachdem ob es sich z.b. um ein Ajax Request oder Business Logik handelt wird eine Model Funktion aufgerufen. View Methoden bestehen aus Effekten z.b. das Ein/Ausblenden von Elementen, CSS Modifikationen und dem Rendern von neuen Elementen.

Die Kommunikation zwischen Cake und Javascript läuft über JSON und es können von Cake gerenderte View-Elemente oder auch Objekte/Arrays übergeben werden.

Die Ordnerstruktur ist ähnlich aufgebaut, wie in Cake d.h. während der Entwicklung ist das Javascript immer schön dokumentiert und in verschiedenen Dateien. Bei einem Deploy kann man dann z.b. über den Dojo Compressor eine einzelne Datei generieren.

Zu guter letzt hab ich die vorhandene Test Suite von Cake aufgebohrt, um die Javascript Dateien mit der Testing Umgebung von jsUnit testen zu können.

Wir arbeiten jetzt hier bei Ormigo mit dieser Architektur. Da im Moment sehr viel zu tun ist, gibt es noch kein Release. Wer es aber nicht aushält und unbedingt sehen will wie das Ganze funktioniert schreibt einfach einen Kommentar oder ne Mail an mich.

Update: Kurzes Beispiel:

Startup Skript:

var routes = {
    '/profil/': 'Users/profile',
    '/kontakte/*/': 'Users/contact_page',
};

$(document).ready(function(){
    dispatcher = new Dispatcher(routes);

});

Controller:

/*
 * UsersController
 *
 */
UsersController = function () {
    views['Users'] = new UsersView();
    models['User'] = new User();

    /**
     * User Profile
     */
    this.profile = function(filter) {
        $('div.person div.container').tabs({on: 1});

        $('div.fragment dl dd', filter).hover(
                    function(){views.Users.showTeaser(this);},
                    function(){views.Users.hideTeaser(this);});

        $('div.fragment dd', filter).click(function(){
            views.Users.showAllInput(this);
            views.Users.showInput(this);
        });

    }

    /**
     * Contact page
     */
    this.contact_page = function(filter) {
        $('div.person div.container').tabs({on: 1});
    }

}