Node.Js: Eine kleine Demo mit Express, TypeScript, Angular und MongoDB Teil 2

Nachdem im letzten Teil (http://blog.ninja4.net/node-js-eine-kleine-demo-mit-express-typescript-angular2-und-mongodb-teil-1/) die Grundlagen gelegt wurden, werden nun folgende Funktionen implementiert:

  • Express soll Webseiten und JavaScript bereitstellen können
  • Einbinden von JADE zur Generierung von HTML Dateien
  • Erster einfacher Angular Controller

Express soll Webseiten und JavaScript bereitstellen können

Aktuell gibt unser Server immer nur einen statischen Text zurück:

Zunächst ist "path" zu installieren:

Als nächstes kommentieren wir den statischen send aus und definieren, dass für root (/) zukünftig das Verzeichnis 'dist/app' verwendet werden soll. Hierzu benötigen wir das Objekt 'path', das wir importiern müssen:

Starten wir nun Express funktioniert es nicht!

Zeit den Debugger auszuprobieren!

Hierzu wechseln wir in den Debugger. Markieren den BreakPoint natürlich im "JavaScript" File server.js und nicht im "TypeScript". Und starten.

Mittels MouseOver über den __dirname, sehen wir ziemlich schnell das Problem:

Der Pfad zeigt hier nicht auf das Basisverzeichnis, sondern auf ".....\dist\js".

In der Debugkonsole können wir nun den richtigen Befehl ermitteln:

Wir beenden den Debugger undn tragen wir in unser TypeScript den korrekten Pfad ein.

Starten wir nun unsere Application zeigt uns der Browser den korrekten Inhalt an.

Angluar im Frontend Verfügbar machen

Als nächstes wollen wir für das Front-End Angular verfügbar machen. Hierzu muss es zunächst installiert werden.

Die Datei "angular.js" ist an dieser Position allerdings noch nicht für das Front End Verfügbar, sondern muss noch in das "dist" kopiert werden.

Dies erfolgt am besten wieder mit grunt:

Nun muss der neue Task "grunt-contrib-copy" geladen werden und die Copy Definiton in Gruntfile.js hinzugefügt werden:

module.exports = function (grunt) {

    grunt.initConfig({

    ts: {
        base: {
        src: ['src/typescript/**/*.ts'],
        dest: 'dist/js',
        options: {
            module: 'commonjs', 
            target: 'es5',
            sourceMap: true,
            declaration: true
        }
        }
    },

    nodemon: {
        base: {
            script: 'index.js'
        }        
    },

    concurrent: {
        base: {
            tasks: ['watch','nodemon']
        }
    },

    copy: {
        lib: {
            files: [{expand: false, 
                src: ['node_modules/angular/angular.js'], dest: 'dist/lib/angular.js' }]
        }
    },

    watch: {
      ts: {
        tasks: ['ts'],
        files: ['src/typescript/**/*.ts']
      },
      livereload: {
        options: { livereload: true },
        files: [ 'dist/**/*.{css,js,html}']
      }
    }

    });

    grunt.loadNpmTasks('grunt-ts');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-nodemon');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-concurrent');
}

Nun führen wir Grunt copy aus und "Angular.js" wird in ein neues Verzeichnis "lib" in der "dist" kopiert:

Jetzt müssen wir Express nur noch den neuen Pfad mitgeben:

Und schon ist dieser Pfad über http://localhost:3000/lib/angular.js auch verfügbar im Browser:

Hiermit haben wir nun eine erste Basis für die Front-End Entwicklung bereitgestellt.

Einbinden von JADE zur HTML Generierung

Mit JADE ist es möglich effizienter HTML Seiten zu generieren, als diese per Hand ein zu tippen. Häufig hat man in HTML das Problem, dass man die Close-Tags abzählen muss oder beim Einrücken in Schwierigkeiten kommt. Zudem ermöglicht es JADE mit Include und Mixings Intelligente Code Fragmente zu genieren. Details findet man hierzu auch unter: http://jade-lang.com/

Zunächst legen wir ein neues Verzeichnis "jade" unter "src" an und dort eine Datei "index.jade". Diese hat vorerst nur eine Zeile Inhalt:

Nun braucht man ... wieder einen Grunt Task:

Auch diesen Grunt Task laden wir:

grunt.loadNpmTasks('grunt-contrib-jade');  

Dann noch den die Konvertierung angeben:

    jade: {
      main: {
        options: {
          pretty: true
        },
        files: [{
            expand: true,
            cwd: 'src/jade',
            src: ['**/*.jade'],
            dest: 'dist/app/',
            ext: '.html'
        }] 
      }
    },

Und natürlich auch im Watch das mit angeben, damit wenn sich die JADE-Dateien ändern automatisch ein neues HTML generiert wird.

Das Gruntfile.js sieht nun wie folgt aus und hat mittlerweile doch eine beachtliche Länge erreicht:

module.exports = function (grunt) {

    grunt.initConfig({

    ts: {
        base: {
        src: ['src/typescript/**/*.ts'],
        dest: 'dist/js',
        options: {
            module: 'commonjs', 
            target: 'es5',
            sourceMap: true,
            declaration: true
        }
        }
    },

    nodemon: {
        base: {
            script: 'index.js'
        }        
    },

    concurrent: {
        base: {
            tasks: ['watch','nodemon']
        }
    },

    copy: {
        lib: {
            files: [{expand: false, 
                src: ['node_modules/angular/angular.js'], dest: 'dist/lib/angular.js' }]
        }
    },

    jade: {
      main: {
        options: {
          pretty: true
        },
        files: [{
            expand: true,
            cwd: 'src/jade',
            src: ['**/*.jade'],
            dest: 'dist/app/',
            ext: '.html'
        }] 
      }
    },

    watch: {
      ts: {
        tasks: ['ts'],
        files: ['src/typescript/**/*.ts']
      },
      jade: {
        tasks: ['jade'],
        files: ['src/jade/**/*.jade']
      },
      livereload: {
        options: { livereload: true },
        files: [ 'dist/**/*.{css,js,html}']
      }
    }

    });

    grunt.loadNpmTasks('grunt-ts');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-nodemon');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-jade');
    grunt.loadNpmTasks('grunt-concurrent');
}

Startet man nun wieder "grunt concurrent", dann kann man diese Änderungen wieder mittels livereload im Browser miterleben:

Einen einfachen Controller mit TypeScript integrieren

Damit wir einen Controller in TypeScript mit Hilfe von AngularJS bauen können, müssen wir erst mal die Typings von Angular installieren:

Sowie auch JQuery:

Als nächstes legen wir ein eigenes Verzeichnis für TypeScript Client Code an:

/// <reference path="../../typings/index.d.ts" />
"use strict";

var demoapp = angular.module('demoapp',[]);  
demoapp.controller("demoCtrl", ['$scope', ($scope) => new demoCtrl($scope)] );

class demoCtrl {  
    constructor( $scope ) {
        // do nothing now
    }
}

Damit Grunt auch Client Code kopiert, benötigen wir eine weiteren Eintrag im Gruntfile.js:

Den Watch erweitern wir um ein weiteren Pfad für den TypeScript Compiler:

Das Gruntfile.js sieht nun folgendermaßen aus:

module.exports = function (grunt) {

    grunt.initConfig({

    ts: {
        base: {
        src: ['src/typescript/**/*.ts'],
        dest: 'dist/js',
        options: {
            module: 'commonjs', 
            target: 'es5',
            sourceMap: true,
            declaration: true
            }
        },
        client: {
        src: ['src/ts-client/**/*.ts'],
        dest: 'dist/lib',
        options: {
            module: 'commonjs', 
            target: 'es5',
            sourceMap: true,
            declaration: true
            }
        }
    },

    nodemon: {
        base: {
            script: 'index.js'
        }        
    },

    concurrent: {
        base: {
            tasks: ['watch','nodemon']
        }
    },

    copy: {
        lib: {
            files: [{expand: false, 
                src: ['node_modules/angular/angular.js'], dest: 'dist/lib/angular.js' }]
        }
    },

    jade: {
      main: {
        options: {
          pretty: true
        },
        files: [{
            expand: true,
            cwd: 'src/jade',
            src: ['**/*.jade'],
            dest: 'dist/app/',
            ext: '.html'
        }] 
      }
    },

    watch: {
      ts: {
        tasks: ['ts'],
        files: ['src/typescript/**/*.ts','src/ts-client/**/*.ts']
      },
      jade: {
        tasks: ['jade'],
        files: ['src/jade/**/*.jade']
      },
      livereload: {
        options: { livereload: true },
        files: [ 'dist/**/*.{css,js,html}']
      }
    }

    });

    grunt.loadNpmTasks('grunt-ts');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-contrib-nodemon');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-jade');
    grunt.loadNpmTasks('grunt-concurrent');
}

Läuft der "grunt concurrent", dann wandelt er jetzt das main.ts in main.js um:

Nun müssen wir unserem Jade-File noch sagen, dass es die JavaScript importieren soll und "ng-app" sowie "ng-controller" setzten:

Im Detail sieht das index.jade nun wie folgt aus:

html  
  head
    script(src="/lib/angular.js")
    script(src="/lib/main.js")
body(ng-app="demoapp" ng-controller="demoCtrl")  
  h1 Hallo das ist eine Demo!
  input(type="text" ng-model="model.test")
  p {{model.test}}

Schaut man sich das jetzt im Browser an, dann sollte jetzt eine einfache funktionierende AngularJS Applikation laufen:

Fazit

Nun steht rudimentär neben der Server Seite nun auch die Client Seite.