• 9. April 2015

Validierungsframework mit Dekoratoren und Annotationen in TypeScript 1.5

TypeScript 1.5 führt neben der Modul-Grammatik von EcmaScript-6 und anderen netten Neuerungen auch das Konzept der Dekoratoren ein. Dabei handelt es sich um Funktionen, mit denen andere Sprachkonstrukte annotiert werden können. Zu diesen Konstrukten zählen Funktionen, Klassen aber auch Eigenschaften. JavaScript ruft die Dekorator-Funktionen beim Start eines Skripts auf und übergibt an diese die annotierten Konstrukte. Der Dekorator hat dann die Möglichkeit, diese Konstrukte zu erweitern oder durch andere Konstrukte zu ersetzen. Einen Überblick zu diesem Sprachkonstrukt, das auch in Angular 2 zum Einsatz kommen wird, findet man unter [1] und [2]. Dieser Beitrag demonstriert anhand der aktuell vorliegenden Alpha-Version von TypeScript den Einsatz von Dekoratoren am Beispiel eines einfachen Validierungsframeworks. Die Idee ist es, für Klassen sowie deren Eigenschaften mittels Annotationen Validierungsregeln festzulegen. Das nachfolgende Beispiel zeigt eine solche Klasse:

@Validate(h => h.minPrice <= h.maxPrice, "min < max")
class Hotel {

    @Required
    name: string;

    @MinValue(0, "Min: 3")
    @MaxValue(7, "Max: 7")
    ranking: number;

    minPrice: number;
    maxPrice: number;
}

Im gezeigten Beispiel validiert die Annotation @Required ein Pflichtfeld und @MinValue sowie @MaxValue prüfen gegen eine festgelegte untere bzw. obere Schranke. Diese Schranken sowie die Fehlermeldungen nehmen sie als Parameter entgegen. Die Annotation @Validate wird im Gegensatz zu den anderen Annotationen auf eine Klasse angewandt. Sie erhält einen Lambda-Ausdruck für die Validierung und ebenfalls eine Fehlermeldung. Zur Realisierung der Validierung soll jedes zu validierende Objekt ein Array __validators mit Validierungsfunktionen erhalten. Jede Validierungsfunktion prüft einen Aspekt des Objektes und retourniert im Fehlerfell eine Fehlermeldung; ansonsten liefert sie null zurück. Für diese Aufgabe nutzt das hier betrachtete Beispiel die Hilfs-Funktion addValidator:

function addValidator(target, fn, errorMessage) {

    var validationFn = (obj) => !fn(obj) ? errorMessage : null;

    if (!target.__validators) {
        target.__validators = [];
    }

    target.__validators.push(validationFn);

}

Diese Funktion nimmt das zu validierende Objekt, eine Funktion zum Validieren und eine eventuelle Fehlermeldung entgegen. Die übergebene Funktion liefert per Definition true, wenn die durchgeführte Validierung erfolgreich war; ansonsten false. Damit erstellt addValidator eine Validierungsfunktion, die im Fehlerfall die Fehlermeldung retourniert, und platziert diese in der Variable validationFn. Anschließend prüft addValidator, ob das Objekt bereits ein Array __validators aufweist und fügt dieses bei Bedarf hinzu. Danach hinterlegt sie die Validierungsfunktion in diesem Array. Eine erste Version des Dekorators Required findet sich im nächsten Beispiel. Da dieser Dekorator auf Eigenschaften angewendet wird, bekommt er per Definition den jeweiligen Prototyp und den Namen der Eigenschaft übergeben. Er erstellt eine Funktion, welche ein Pflichtfeld validiert. Diese prüft, ob der zu prüfende Wert null oder undefined ist. Anschließend registriert Required diese Funktion zusammen mit einer einfachen Fehlermeldung als Validierungsfunktion. Dazu kommt die zuvor betrachtete Funktion addValidator zum Einsatz.

function Required(target, name) {
    var fn = (obj) => obj[name] !== null && typeof obj[name] !== "undefined";
    var errorMessage = name + " is required!";
    addValidator(target, fn, errorMessage);
}

Weniger schön am letzten Beispiel ist die Tatsache, dass die Fehlermeldung hartcodiert hinterlegt wurde. Das kann umgangen werden, indem man eine Funktion vorsieht, die den Dekorator erzeugt. Solche Factory-Funktionen können ebenso wie Dekoratoren in Form von Annotationen im Quellcode platziert werden. Das nachfolgende Beispiel demonstriert dies. Es zeigt zwei Factory-Funktionen, die Parameter zum Steuern der Validierung entgegen nehmen und jeweils eine Dekorator-Funktion mit der gewohnten Signatur retournieren:

function MinValue(min, errorMessage) {

    return function (target, name) {
        var val = (obj) => obj[name] >= min;
        addValidator(target, val, errorMessage);
    };
}

function MaxValue(max, errorMessage) {
    return function (target, name) {
        var val = (obj) => obj[name] <= max;
        addValidator(target, val, errorMessage);
    };
}

Die Factory-Funktion für die Annotation Validate gestaltet sich ähnlich:

function Validate(fn, errorMessage) {
return function (target) {
addValidator(target.prototype, (obj) => fn(obj), errorMessage);
};
}

Zum Validieren nimmt sie einen Lambda-Ausdruck sowie eine Fehlermeldung entgegen. Der erzeugte Decorator nimmt wie gewohnt das betroffene Konstrukt entgegen. Da Validate zum Annotieren von Klassen gedacht ist, handelt es sich hierbei nicht um ein Objekt, sondern um die jeweilige Klasse bzw. Konstruktor-Funktion. Ein Parameter für den Namen der markierten Eigenschaft ist aus demselben Grund hinfällig. Damit addValidate die Validierungsfunktion zu sämtlichen aus der Klasse erzeugten Objekten hinzufügt, übergibt das betrachtete Beispiel den Prototyp dieser Klasse an addValidator. Nun benötigt man nur noch ein Stück Code, welches sämtliche Validierungsfunktionen eines Objektes aufruft und die so erhaltenen Fehlermeldungen einsammelt. Das nächste Beispiel zeigt ein eine Klasse Validator, welche sich um diese Aufgabe kümmert:

class Validator {

    static validate(obj) {

        var errMessages = [];

        if (!obj || !obj.__validators) return errMessages;

        for (var fn of obj.__validators) {
            var errMessage = fn();

            if (errMessage) {
                errMessages.push(errMessage);
            }
        }

        return errMessages;
    }
}

Zum Testen der Implementierung kann man die eingangs gezeigte Klasse zum Beispiel instanziieren und an Validator.validate übergeben:

var hotel = new Hotel();
hotel.name = null;
hotel.ranking = 99;
hotel.minPrice = 120;
hotel.maxPrice = 80;


var err = Validator.validate(hotel);

if (err.length > 0) {
    alert(err.join("\n"));
}
else {
    alert("No errors!");
}

Links

 

 
Hier können Sie eine Anfrage für eine unverbindliche Schulung ode Beratung bzw. einen Workshop erstellen.
 
Unverbindliche Anfrage
 
 

Schulungen

Angular Schulung: Strukturierte Einführung

Lernen Sie in dieser interaktiven Schulung anhand einer Beispielanwendung den Einsatz von Angular für Ihre erfolgreichen Projekte kennen. Sie durchdringen die Hintergründe und bauen von der ersten Minute an auf Best Practices auf.

Details

Advanced Angular: Enterprise-Anwendungen und Architektur

In dieser Schulung erfahren Sie alles für die Entwicklung großer Anwendungen mit Angular: Mono-Repos, Micro-Apps, State Management, Performance und mehr

Details

Angular: Strukturierte Einführung

Seit der Ankündigung von Angular (2+) fragen sich Entwicklungs-Teams, welche Migrationspfade für AngularJS-1.x-Anwendungen vorliegen werden. Das im Lieferumfang von Angular enthaltene ngUpgrade bietet eine Antwort darauf. Es erlaubt einen Parallelbetrieb von AngularJS 1.x und Angular (2+) und stellt somit die Grundlage für eine schrittweise Migration dar.

Details

Progressive Web-Apps mit Angular

Progressive Web Apps bieten den Komfort nativer Anwendungen, indem sie auf moderne Browser APIs, wie Service Worker, setzen. Sie sind installierbar sowie offlinefähig und nutzen Hintergrundprozesse für Datensynchronisation und Push-Notifications. Diese Schulung zeigt anhand eines durchgehenden Beispiels was sich genau hinter diesem neuen Konzept verbirgt, wie solche Anwendungen mit Angular entwickelt werden und wie Sie in Ihren Projekten von den dahinterstehenden Ideen profitieren.

Details

Reaktive Architekturen mit Angular und Redux

Dieses interaktive Seminar vermittelt, wie Sie reaktive Anwendungen mit Angular entwickeln können.

Details

TypeScript

TypeScript gibt Ihnen alle Möglichkeiten der neuen JavaScript-Standards und zusätzlich ein statisches Typsystem, das dabei hilft, Fehler möglichst früh zu erkennen. Außerdem ist TypeScript die Grundlage für Angular. In diesem interaktiven Seminar lernen Sie diese mächtige Sprache anhand einer Fallstudie kennen.

Details

Weitere Schulungen ...