Dynamics CRM Coding Conventions

Moin,

so wollen wir doch mal den ersten „sinnvollen“ Beitrag erstellen:

Bei größeren Dynamics CRM Projekten habe ich schmerzlich die Erfahrung gesammelt das einheitliche coding-convetions zwar durchaus für den Plugin-Code angewendet werden, jedoch bei JavaScript gerne alle Hemmungen fallen gelassen werden und jeder munter durcheinander programmiert, was recht schnell zu ganz besonders ärgerlichen Problem führen kann.

Coding-Conventions können uns dabei helfen einheitliche Muster zu definieren die sowohl die Wartbarkeit (von der Lesbarkeit mal ganz zu schweigen) als auch die Wiederverwendbarkeit erheblich zu steigen. Sagen will mal wir möchten ein einfaches Script schreiben das, auf ein bestimmtes Event der Contact Form registriert ist.

Die erste Entscheidung die wir treffen müssen ist wo der Code abgelegt wird. Eine Möglichkeit wäre es (zumindest in CRM 2011) ein neue .js Datei anzulegen und diese als WebResources in CRM zu laden und mit dem entsprechenden onChange Event der Entität zu verknüpfen. Ich bin allerdings ein groß Freund davon für jede Entität ein zentrales Script anzulegen das zentral alle Client-Seitigen Anpassungen verwaltet. D.h. es gibt erstmal nur eine .js Datei (onload.contact.js) für die für das onLoad Event registriert ist.

Die onload.contact.js Datei beinhaltet dann folgenden Code:

///
/// OnLoad Script for the entity 'contact'
///
/// requires(jquery.1.7.1.js, underscore.1.2.3.js)
///
var contact = (function(){

    ///
    /// Private member
    ///

    ///
    /// Invoked by the crm runtime on page-load
    ///
    function Init(){

        switch (Xrm.Page.ui.getFormType()) {

            case 1: // Create
                // ...
                break;

            case 2: // Update
                // ...
                break;
        }
    };

    ///
    /// Is invoked for serveral attribute (see entity-form definition)
    ///
    function GenericOnChange(executionContext) {

        // get the attribute that triggers the event
        var attrname = executionContext.getEventSource().getName();

        switch (attrname) {

            case 'foo_fancy_attr':
                // ....
                break;
        }
    };

    ///
    /// Public API
    ///
    return {
        Init: Init,
        GenericOnChange : GenericOnChange
    };
}());

In JavaScript gibt es leider keine direkte Möglichkeit Namespaces zu definieren, was häufig dazu führt das der global Namespace unnötiger Weise mit variablen belastet wird. Verwendet man jedoch den Ansatz der „self-invoking function“ kann man diese Problem umgehen. Stark vereinfacht gesagt erlaubt es dieses Pattern die Definition eines Objects welches über public und private Methoden verfügt. Private Methoden werden einfach innerhalb des Funktionskörpers definiert und lediglich die öffentlichen werden in dem „return Object“ verwendet. Der Code erzeugt ein JavaScript Module das über zwei public Member verfügt:

  1. Init
  2. GenericOnChange

Ok, zurück zur Aufgabenstellung. Soll nun eine bestimmte Logik (z.B.: Validierung) durchgeführt werden, müsste man lediglich das Script mit dem OnLoad Event Verknüpfen und im OnChange Event auf die generische Methode verweisen:

OnLoad Event Definition

Vorteil diese Ansatzes ist es das Änderungen (z.B.: weitere Methoden sollen aufgerufen werden) nur im Code vorgenommen werden müssen und nicht in der CRM UI. Darüberhinaus muss man nicht in der Oberfläche nach der JavaScript Logik für ein bestimmtes Event suchen, sonder man muss nur die onload-js Datei konsultieren.

Zusammenfassend kann man aus dem obigen Code folgende Konventionen ableiten:

  1. Datei Name: Der Name der .js Datei sollte immer Aufschluss über den Anwendungsfall und die zugehörige Entität geben (onload.contact.js, ribbo.foo.contact.js, …)
  2. Die Methode die vom CRM aufgerufen wird, sollte immer den Namen „Init“ tragen. Damit wird es für andere Team-Mitgelieder einfacher den Aufbau der Datei nachzuvollziehen.
  3. Module ermöglichen eine einfache Trennung zwischen public und privte Methoden.
  4. Referenzierte Module /Libraries werden im Module Kommentar erfasst (/// requires(jquery.1.7.1.js, underscore.1.2.3.js)) . Damit ist sichergestellt das Abhängigkeiten erkannt und berücksichtigt werden können.

Hinterlasse einen Kommentar