2015-11-03 11 views
10

Ich habe eine Wand getroffen, die ich nicht kaputt machen kann. Ich benutze angular + typescript und möchte es mit requirej arbeiten lassen. Es sind mehrere eckige Apps definiert, die abhängig von einem Attribut "data-app-name" im Body geladen werden.Requirejs + (multi) eckig + maschinenschriftlich

Ordnerstruktur (vereinfacht):

|- Libs 
    |- angular 
    |- some-plugin 
    |- angular-some-plugin // Exposes an angular.module("ngSomePlugin") 
    |- require.js 
|- Common 
    |- Common.ts // Exposes an angular.module("common") 
|- App1 
    |- Controllers 
     |- SomeController.ts 
     |- SomeOtherController.ts 
    |- Services 
     |- SomeService.ts 
    |- Main.ts 
    |- App.ts 
|- App2 
    // same as above 
|- AppX 
    // same as above 
|- Index.html 
|- Main.ts 

Inhalt:

Index.HTML:

// All these attributes are set dynamically server-side 
<body id="ng-app-wrapper" data-directory="App1" data-app-name="MyApp"> 
    <script src="Libs/require.js" data-main="Main"></script> 
</body> 

Main.ts:

console.log("Step 1: Main.js"); 

requirejs.config({ 
    paths: { 
     "angular": "Libs/angular/angular", 
     "common": "Common/common" 
    }, 
    shim: { 
     "angular": { 
      exports: "angular" 
     } 
    } 
}); 

require(["angular"], (angular: angular.IAngularStatic) => { 
    angular.element(document).ready(function() { 

     var $app = angular.element(document.getElementById("ng-app-wrapper")); 
     var directory = $app.data("directory"); 
     var appName = $app.data("app-name"); 

     requirejs.config({ 
      paths: { 
       "appMain": directory + "/Main" 
      } 
     }); 

     require([ 
      'common', 
      'appMain' 
     ], function() { 
      console.log("Step 5: App1/Main.js loaded"); 
      console.log("Step 6: Bootstrapping app: " + appName); 
      angular.bootstrap($app, [appName]); 
     }); 
    }); 
}); 

Main.ts in App1 :

console.log("Step 2: App1/Main.js"); 

requirejs.config({ 
    paths: { 
     "app": "App1/App", 
     "somePlugin": "Libs/some-plugin/some-plugin", // This is an AMD module 
     "ngSomePlugin": "Libs/angular-some-plugin/angular-some-plugin" 
    }, 
    shim: { 
     "ngSomePlugin": { 
      exports: "ngSomePlugin", 
      deps: ["somePlugin"] 
     } 
    } 
}); 

define([ 
    "app" 
],() => { 
    console.log("Step 4: App.js loaded"); 
}); 

App1/App.ts:

console.log("Step 3: App.js"); 

import SomeController = require("App1/Controllers/SomeController"); 
import SomeOtherController = require("App1/Controllers/SomeOtherController"); 
import SomeService = require("App1/Services/SomeService"); 

define([ 
    "angular", 
    "ngSomePlugin" 
], (angular: angular.IAngularStatic) => { 

    // This isn't called, so obviously executed to late 
    console.log("Defining angular module MyApp"); 

    angular.module("MyApp", ["common", "ngSomePlugin"]) 
     .controller("someCtrl", SomeController.SomeController) 
     .controller("someOtherCtrl", SomeOtherController.SomeOtherController) 
     .service("someService", SomeService.SomeService) 
     ; 
}); 

Dies scheint jedoch zu brechen, mit dem guten alten Fehler: nicht erfasste Fehler: [$ Injektor: nomod] Module 'MeineAnw' ist nicht verfügbar! Sie haben entweder den Modulnamen falsch geschrieben oder vergessen, ihn zu laden.

Frage 1:

Was mache ich hier falsch? Wie kann ich sicherstellen, dass der Anruf angular.module() erfolgt, bevor ich meine App bootstrappst?

Dies ist die Ausgabe der console.logs, wo man Winkel sehen kann, hat noch nicht das Modul angular.module („MyApp“) definiert, so dass dies geschieht, um spät offensichtlich: Console log

UPDATE Ich kann die winkelförmigen Aufrufe in App.ts auspacken, so dass es nichts erfordert (außer für die Importe an der Spitze). Wenn ich dann App zum Shim in App1/Main.ts hinzufüge und die Abhängigkeiten dorthin lege, scheint es zu funktionieren. Ist das ein guter Weg, dies zu lösen?

UPDATE2 Wenn ich verlangen stattdessen in App.ts von definieren, hat es die Winkelmodul instanziiert, aber immer noch, nachdem er es zu Bootstrap versucht.

Frage 2:

Gibt es eine Möglichkeit benutzerdefinierte Konfiguration zu überliefern, zum Beispiel, wenn der Name die Libs sind? Ich habe versucht, die folgenden, nicht zu arbeiten schien (Main.ts):

requirejs.config({ 
    paths: { 
     "appMain": directory + "/Main" 
    }, 
    config: { 
     "appMain": { 
      libsPath: "Libs/" 
     }, 
     "app": { 
      name: appName 
     } 
    } 
}); 

App1/Main.ts:

define(["module"], (module) => { 
    var libsPath = module.config().libsPath; 
    requirejs.config({ 
     paths: { 
      "somePlugin": libsPath + "somePlugin/somePlugin" 
      // rest of paths 
     } 
    }); 

    define([ // or require([]) 
     "app" 
    ],() => {}); 
}); 

App.ts:

define([ 
    "module" 
    // others 
], (module) => { 
    angular.module(module.config().name, []); 
}); 

Aber auf diese Weise logischerweise wartet angular.bootstrap() nicht darauf, dass App.ts geladen wird. Daher ist das Winkelmodul noch nicht definiert und kann nicht gestartet werden. Es scheint, dass Sie keine verschachtelte Definition wie in App1/Main.ts tun können? Wie sollte ich das konfigurieren?

+0

ich Zweifel, dass die mehreren Schichten von 'require.config()' Anrufe wie erwartet funktioniert. (Side-question: oder?) Aber da das 'data-directory' /' data-app-name' serverseitig generiert wird, würde ich vorschlagen, dass du den 'require.config()' Aufruf auch dynamisch erzeugst. Möglicherweise möchten Sie sogar das entsprechende Fragment direkt serverseitig direkt in die Index.html einbinden. Von dort aus müssen Sie die Pfade in den AMD-Modulen optimieren. Und eine Randbemerkung: Warum verwenden Sie nicht die native Unterstützung von Typescript für AMD, d. H. Die 'import Foo = require ('Foo')' Syntax? –

+0

Ich mache das 'import' Zeug auf Angular-App-Ebene (in App.ts und allen anderen eckigen Komponenten). Ist es besser, die Importsyntax überall zu verwenden, wenn ich Typoskript verwende? Ich denke nicht, dass es auf Drittanbieter-Bibliotheken von Drittanbietern funktioniert. – devqon

+0

Und ja, die mehreren Ebenen von 'require.config()' scheint zu funktionieren :) – devqon

Antwort

-1

Verwandte auf Frage 1:

In App1/App.ts sollte nicht die Funktion der Winkelmodul Rückkehr für sie injiziert werden.

z.

define([ 
    "angular", 
    "ngSomePlugin" 
], (angular: angular.IAngularStatic) => { 
    return angular.module("MyApp", ["common", "ngSomePlugin"]) 
     .controller("someCtrl", SomeController.SomeController) 
     .controller("someOtherCtrl", SomeOtherController.SomeOtherController) 
     .service("someService", SomeService.SomeService) 
     ; 
}); 
+0

Ich brauche das Modul nicht wirklich, ich muss nur sicher sein, dass es instanziiert ist, damit ich meine Anwendung starten kann, was nur mit dem Namen geschieht: 'angular.bootstrap ($ app, [" MyApp "])' – devqon

+0

Und ich habe das auch probiert :) – devqon

+0

Es hängt von der Ausführung des Körpers ab, nicht vom Wert des zurückgegebenen Inhalts. – EricG

0

Frage 1

Alexander Beletsky schrieb einen great article über binden requireJS und Angular zusammen (und darüber, warum es lohnt sich). Ich denke, er hat die Antwort auf Ihre erste Frage: Laden Sie eckig von einem anderen Modul und machen Sie dann den Bootstrapping-Aufruf. Dies erzwingt, dass JS Abhängigkeiten für diese Module lädt, bevor Code ausgeführt wird.

dies Unter der Annahme, ist Ihr main.ts

console.log("Step 1: Main.js"); 

requirejs.config({ 
    paths: { 
     "angular": "Libs/angular/angular", 
     "common": "Common/common". 
     "mainT1": "t1/main.js" 
    }, 
    shim: { 
     "angular": { 
      exports: "angular" 
     } 
    } 
}); 

bei hinzufügen die sehr Anruf an das nächste Modul beenden

requirejs(["app"], function(app) { 
    app.init(); 
}); 

Hier in t1 Haupt Sie die tatsächliche app machen und sie haben laden alles auf einmal.

t1/main.ts

define("app-name", ['angular'], function(angular){ 

    var loader = {}; 

    loader.load = function(){ 

     var app = angular.module("app-name",[]); 

     return app; 
     } 

return loader; 

} 

Schließlich sagen wir, Sie eine Testdatei hier es Sie app.js aufgerufen trägt. Hier würden Sie die Sequenzierung einrichten, um ein Objekt zu erhalten, das die Ladefolge von angular bereits beendet hat. Sobald es abgeschlossen ist, dann rufen Sie das Bootstrapping in der Funktion init().

t1/app.js

define("app", function(require){ 
    var angular = require('angular'); 
    var appLoader = require('mainT1'); 

    var app = {} 
    app.init = function(){ 
      var loader = new appLoader(); 
      loader.load(); 
      angular.bootstrap(document, ["app-name"]); 
    } 

}