Angular/Node: Erstellen eines Befehlszeilentools zum Generieren von Projekten Teil 1

Verwenden von Angular Server-Side

Also ich weiß was du denkst:

Um was geht’s hier? Angular ist ein Front-End-Framework, warum sollten Sie es im Back-End benötigen??”

Ich habe die Struktur und Organisation, die Projekten mit Angulars Nutzung von Diensten, Controllern und Anweisungen auferlegt werden, wirklich zu schätzen gelernt. Ich mag Angular so sehr, dass ich diese Struktur auf meine anderen Projekte sowie meinen Servercode ausdehnen wollte. Ich bin wirklich der Meinung, dass eine so solide Ausgangsbasis eine große Hilfe ist, um Projekte in Gang zu bringen. Also beschloss ich, herauszufinden, wie man eine Angular-App bootet und von der Befehlszeile aus zum Laufen bringt, und da ziemlich viel Code erforderlich ist, damit die verschiedenen Angular-Teile richtig funktionieren, fand ich einen großartigen Ort, um damit zu beginnen ist ein Befehlszeilentool zum Generieren der Boilerplate, die zum Bootstrapping einer Winkel-App erforderlich ist, über die Befehlszeile oder den Browser.

Was braucht Angular zum Starten

Wenn Angular ausgeführt wird, möchte es, dass ein Fensterobjekt und ein Dokumentobjekt verfügbar sind. Um diese schnell und transparent bereitzustellen, verwenden wir Node.js benv (Browserumgebung) Modul. Wir beginnen mit der Erstellung eines Projektverzeichnisses und der Installation benv:

$ mkdir PROJECT_NAME
$ cd PROJECT_NAME
$ npm install benv

Erstellen Sie dann eine Datei für den Bootstrap-Code. Ich habe bei mir angerufen: nodeNg.js.
Verwenden Sie jeden Editor, mit dem Sie vertraut sind – ich mag vim –, aber Sie können alles verwenden, was Sie möchten

$ vim nodeNg.js

und fügen Sie dies der Datei hinzu, um zu beginnen:

"use strict";
var benv = require('benv');

Dies zieht das Modul ein, das unsere Browserumgebung erstellt, die Angular verwenden wird.

Nun, dieser nächste Teil ist ein bisschen persönlicher Präferenz, aber ich habe mich dafür entschieden, den gesamten Bootstrap-Prozess in eine Funktion zu packen, die eine andere Funktion als Argument akzeptiert. Die erste Funktion führt das Bootstrapping durch und enthält die Angular-App, die wir verwenden können, und die Funktion, die wir übergeben, ist eine grundlegende anonyme Funktion, in die wir unsere App einschließen. Nachdem Angular fertig ist, nennen wir unsere übergebene Funktion so befindet sich in der ersten Funktion und führt den Code für unsere App aus. Ich weiß, was ich geschrieben habe, war ein Bissen, also schauen wir uns an, was ich meine.

Wir beginnen damit, die Funktion zu schreiben, die das Bootstrapping durchführt, und nehmen unsere App-Funktion als Argument:

var use_angular = function(callback){
    benv.setup(function(){
        benv.expose({
            angular:benv.require('./angular.js','angular')            
      });
    return callback();    
  });
};

Das sieht also komplex aus, aber wir tun eigentlich fast nichts, gehen wir Zeile für Zeile vor:

  • Zeile 1 deklariert nur die Funktion als Variable
  • Zeile 2 – 4 ist, wo die Magie wirklich ist,
    • Am 2 rufen wir die an setup Funktion der benv Modul, erstellt dies die window und document Objekte, die eckig werden, um seine zu erstellen $window und $document Dienstleistungen. Innerhalb dieser Funktionsdefinition können wir unserer Umgebung durch Aufrufen weitere Dinge hinzufügen benv.exposedas erste Argument ist ein Objekt, die Werte werden verfügbar und wir können auf sie zugreifen, indem wir die Schlüssel verwenden, die wir als Variablen bereitstellen, dh: aufrufen benv.expose({myVar:55}) machen würden myVar gleich 55.
    • Leitung 3 ruft an benv.expose mit {angular:benv.require('./angular.js','angular')} als sein Argument. was, wie ich schon sagte, hinzufügt angular als Variable in unserer Umgebung, und es gibt ihr den Wert der Rückgabe eines Anrufs an einen anderen benv Funktion requiredies entspricht im Grunde dem Hinzufügen eines script – Tags zu unserem HTML – Dokument .
    • Endlich nach Anruf benv.expose Wir können Angular verwenden, damit wir unsere übergebene Funktion zurückgeben, wie wir sie nennen, und effizient den Rückgabewert unserer übergebenen anonymen Funktion zurückgeben, die, wenn Sie sich erinnern, unsere Angular-App sein wird.

Jetzt müssen wir nur noch unsere neue Funktion mit einer anonymen Funktion aufrufen, die unsere App einzieht, um sie als Argument auszuführen.

use_angular(function(){
    var path = require('path');
    require(path.join(path.resolve('./'),process.argv[2]))();
});

Das sieht seltsam aus, ich weiß, aber alles, was wir hier tun, ist, eine Datei in einem auf der Befehlszeile angegebenen Pfad anzufordern und sie mit unserem aktuellen Verzeichnis zu verbinden, um sicherzustellen, dass wir die richtige Datei benötigen. Dann exportieren wir eine Funktion aus dieser Datei, also rufen wir sie so auf, wie wir es wollten, denn deshalb existiert sie: um ausgeführt zu werden.

Also sind wir mit dem Bootstrapping von Angular fertig?

Grundsätzlich. Tatsächlich können wir mit diesem Setup fortfahren und unser Befehlszeilentool mit Angular schreiben, aber ich habe diesen Fehler bereits gemacht. Wir werden daraus lernen und uns unsere Bootstrapping-Funktion genauer ansehen.

Wie gesagt, wir könnten weitermachen, die Dinge würden funktionieren, aber hier sind die Probleme, auf die wir stoßen würden:

  • Wenn wir Elemente aus der Angular-Welt laden (dh unsere Fabriken, Dienste und Controller), müssten wir ein Injector-Objekt erstellen, um sie zu laden
  • Dieses Objekt hat eine unbequeme API, da es nicht wirklich für den allgemeinen Gebrauch gedacht ist

Um zumindest die Erstellung des Injector-Objekts zu abstrahieren, was erfordert, dass wir die ihm zur Verfügung stehenden Module übergeben, können wir eine Funktion schreiben, etwa so:

var ng_load = function(name){
    return angular.injector(['ng']).get(name);
};

Das würde jetzt funktionieren, würde uns aber nicht erlauben, unsere eigenen Dienste und Controller zu verwenden. Es würde uns nur erlauben, Angular zu verwenden, weil wir nur das eckige ‘ng’-Modul übergeben. Wenn wir unsere eigenen Module oder andere Module von Drittanbietern übergeben möchten, müssen wir sie ein wenig ändern. Während wir dies tun, müssen wir auch bedenken, dass Angular einen Fehler ausspuckt, wenn wir zufällig ein Modul mehr als einmal injizieren:

var ng_load = function(name,extraModules){
    var mods = ['ng'];
    if(extraModules && extraModules.length){
        angular.forEach(extraModules,function(itm){
            mods.indexOf(itm) === -1 ? mods.push(itm):false;
    });
  }
    return angular.injector(mods).get(name);
};

OK. Jetzt können wir jedes beliebige Modul übergeben, wo es sie injiziert und aus der Umgebung zieht, was wir möchten, damit wir es verwenden können. Aber es gibt noch ein anderes Problem. Wenn wir 2 Dienste aus einem Modul namens holen wollten service.modmüssten wir hinzufügen service.mod zu jedem Anruf von ng_loaddh:

var serviceA = ng_load('serviceA',['service.mod']);
var serviceB = ng_load('serviceB',['service.mod']);

Wenn alles, was wir tun müssen, ist:

var serviceA = ng_load('serviceA',['service.mod']);
var serviceB = ng_load('serviceB');
var serviceC = ng_load('serviceC');

Und ich weiß, es scheint in diesem Zusammenhang nicht viel zu sein, aber vertrau mir. Das Modul jedes Mal, wenn Sie es brauchen, übergeben zu müssen, kann wirklich mühsam werden, und wenn Sie jemals vergessen, es hinzuzufügen, wird es zu einer einfachen Möglichkeit für Fehler, sich einzuschleichen.

Also werde ich unsere Funktion noch einmal so umgestalten, dass die von uns eingefügten Module zwischengespeichert werden, sodass wir sie nur einmal hinzufügen müssen und danach einfach nach Dingen fragen können. Solange wir es der ersten Serviceanfrage hinzugefügt haben, können wir andere Dienste nur nach Namen erhalten. Bisher habe ich dies als eigenständige Funktion geschrieben, aber eigentlich muss es in der definiert werden benv.setup call als Eigenschaft für das Objekt, an das wir übergeben benv.expose (unser Modul-Cache wird auch dort leben).

Fangen wir einfach von vorne an und integrieren es in unser ursprüngliches Setup:


"use strict";
var benv = require('benv');

var use_angular = function(callback){
    benv.setup(function(){
        benv.expose({
                         angular:benv.require('./angular.js','angular'),
                         ng_mod_cache:[],
                         cache_mods : function(mods){
                               angular.forEach(mods,function(itm){
                                      ng_mod_cache.push(itm);
                             });
                        },
                       ng_load : function(name,extraModules){
                                 var mods = (ng_mod_cache && ng_mod_cache.length) ? ng_mod_cache : ['ng'];
                                 if(extraModules && extraModules.length){
                                          angular.forEach(extraModules,function(itm){
                                               mods.indexOf(itm) === -1 ? mods.push(itm):false;
                                            });
                                      }
                                      cache_mods(mods);
                                      return angular.injector(mods).get(name);
                                 }
                          });
                  return callback();
              });
};



use_angular(function(){
    var path = require('path');
    require(path.join(path.resolve('./'),process.argv[2]))();
});

Bootstrapping des Angular-Frameworks

Um sicherzustellen, dass unsere App tatsächlich ausgeführt wird und unsere Controller und Dienste instanziiert, sowie um auf den Dependency Injector zuzugreifen, müssen wir unsere App booten. Wir können dazu eine einfache kleine Funktion hinzufügen.

    ng_bootstrap:function(app){
         var _app = angular.isString(app) ? angular.module(app) : app;
        _app.config(function($locationProvider){
            $locationProvider.html5Mode(false);
        });
        return angular.bootstrap(angular.element(document.createElement('body')),[_app.name]);
    }

Jetzt können wir damit eine kleine Funktion schreiben, um unseren Abhängigkeitsinjektor aus Winkel zu holen:

    ng_injector:function(app){
        return ng_bootstrap(app).invoke;
    }

und das letzte Problem, auf das wir möglicherweise stoßen, wird die Ausführung von asynchronen Knotenfunktionen mit Angular sein, weil Angle eine benutzerdefinierte Version von verwendet Q Modul für Promises verwendet der Knoten standardmäßig eine andere Methode. Also schreiben wir eine Funktion, die asynchrone Knotenfunktionen als erstes Argument nimmt und eine Funktion zurückgibt, die verwendet $q angulars Wrapper um die Q Bibliothek:

    promisify:function(asyncFunc,context){
        var $q = ng_load('$q');
        return function(){
            var defer = $q.defer(),
                   args = Array.prototype.slice.call(arguments);
            args.push(function(err,val){
                 if(err !== null){
                    defer.reject(err);
                }else{
                    defer.resolve(val);
               }
            });
            asyncFunc.apply(context || {},args);
            return defer.promise;
        };
    }

Jetzt können wir mit dem Schreiben unseres Befehlszeilentools beginnen. Jetzt sind wir bereit.

Fortgesetzt werden…..

eine Kopie des Codes in diesem Blog und ein kleines Beispiel finden Sie unter Github-Repo

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *