2014-12-10 6 views
6

Ich konvertiere eine Angular App, um TypeScript zu verwenden, aber das ist eine allgemeine TypeScript-Frage, nicht über Angular. Die Winkel js Dateien sind entlang der Linien:TypeScript - sie muss es haben? (wo es == globaler Gültigkeitsbereich)

(function() { 
    var app = angular.module('myModule', []); 
    app.controller('myController', ['$scope', 
    function ($scope) { 
     $scope.myNewProperty = "Bob"; 
    } 
    ]); 
})(); 

Und ich habe umgewandelt, das zu schönen Typoskript Klasse Syntax:

class myController { 
    constructor($scope) { 
     $scope.myNewProperty = "Bob"; 
    } 
} 

angular.module('myModule', []).controller("myController", myController); 

Alle funktioniert gut, mit Ausnahme der erzeugte JS wird nach dem JS nicht umgebrochen Modulmuster (dh in einer äußeren anonymen Funktion):

So myController ist jetzt global. Wenn ich die Klasse in einem Typoskript Modul setzen, dann erzeugt die js mit dem Modulnamen als globale Variable:

var TsModule; 
(function (TsModule) { 
    var myController =  (function() { 
      app.controller('myController', ['$scope', 
      function ($scope) { 
       $scope.myNewProperty = "Bob"; 
      } 
      ]); 
     })(); 

    var app = angular.module('myModule', []); 
})(TsModule || (TsModule = {})); 

Wie verhindere ich, Typoskript den globalen Bereich auf diese Weise umweltfreundlich? Ich will nur, dass alles in einem netten lokalen Rahmen verpackt ist. Ich habe gesehen, dass es an anderer Stelle sagte "einfach zurück zu der alten JS-Syntax" (d. H. Keine TypeScript-Klasse). How can I define an AngularJS service using a TypeScript class that doesn't pollute the global scope?

Aber der ganze Sinn von uns mit TypeScript/ist/die Klassensyntax. Ist TypeScript im Widerspruch zu irgendeinem (vernünftigen) JS-Programmierer, der nicht global sein will?

+0

Schon eine Weile her, seit ich TypScript verwendet habe, also könnte ich auf diesem einen sein, aber haben Sie versucht, den oben genannten TypeScript-Code in einer sofort aufgerufenen Lambda-Funktion zu verpacken? '(() => { // Code hier ... })();' –

+0

Ich bekomme die Frustration, aber die Verwendung eines einzigen, wohldefinierten Modulnamens für all Ihre Dateien verschmutzt den globalen Geltungsbereich kaum. Es wird eine einzelne Variable im globalen Gültigkeitsbereich erstellen und selbst dann, wenn Sie Ihre Klassen nicht exportieren, wird es niemals mehr als ein leeres Objekt sein. Das ist ein ziemlich kleines Problem. – Josh

+1

Nur FYI (Sie könnten bereits wissen) Klasse Ausdrücke kommen in TS nächsten, die sie in IIFE erlauben. Auch für OP könnte dieses Video (ich gemacht habe) zu Modulmustern in TypeScript für Sie nützlich sein: https://www.youtube.com/watch?v=KDrWLMUY0R0&hd=1 – basarat

Antwort

4

Sie können einfach wickeln Sie Ihre Klasse in einem module

Das Modul selbst global sein wird, aber wenn Sie die Klasse nicht exportieren, gibt es wenig Sorge über die globale Reichweite mit einem einzigen Modulnamen zu verschmutzen.

Also das Typoskript:

module MyModule { 
    class MyClass { 
    constructor(){} 
    } 
} 

wird die folgende JS produzieren:

var MyModule; 
(function (MyModule) { 
    var MyClass = (function() { 
     function MyClass() { 
     } 
     return MyClass; 
    })(); 
})(MyModule || (MyModule = {})); 
+1

Hallo, vielen Dank für die Antwort. Ich war gerade dabei, meine Frage zu bearbeiten, um meine Zeile zu erweitern: "Wenn ich die Klasse in ein TypeScript-Modul lege, dann generiert die js mit dem Modulnamen eine globale Variable:" - Die Bearbeitung war der generierte Code, der hübsch ist ähnlich wie deine. Der Punkt ist, dass (in Ihrem Beispiel) var MyModule jetzt global ist. Wir haben einen globalen Controller für ein globales Modul ausgetauscht. Wenn eine andere JavaScript-Bibliothek eine globale Variable MyModule hat, entsteht Chaos! – user603563

+1

@ user603563 - Nicht wirklich, weil es das enthüllende Modulmuster verwendet. Das Modul wird also nicht neu definiert, wenn es bereits existiert. Chaos wird nur entstehen, wenn Sie etwas zu diesem Modul hinzufügen, von dem Sie erwarten, dass es extern verfügbar ist. In diesem Fall fügen Sie die Klasse nicht dem Modul hinzu, so dass es nie zu einem Konflikt kommt. Es ist nur ein Durchgang. – Josh

+1

Ich ging einfach zurück zu meinen JS-Referenzen und ja wieder gelernt, dass anstelle einer zuvor deklarierten MyObject, nachfolgende Variierung von MyObject "s nur zum bestehenden Objekt hinzufügen (oder nicht ändern, wenn, wie in diesem Fall, nichts ist (obwohl ich es verabscheue, weil es nicht beantwortet, ob TypeScript den globalen Geltungsbereich vollständig vermeiden kann), werde ich dies als die Antwort markieren, weil dies mit diesen Kommentaren angemessen erklärt, warum es nicht funktioniert Egal, wie ich dachte ... – user603563

2

Quick Update

In den neuesten Versionen von Typoskript Sie können Nest eine Klasse innerhalb eines IIFE:

(() => { 
    class Example { 

    } 
})(); 

Die resultierende Ausgabe ist:

(function() { 
    var Example = (function() { 
     function Example() { 
     } 
     return Example; 
    }()); 
})(); 

Original-Antwort

Sie können vermeiden, zu t hinzuzufügen Der globale Gültigkeitsbereich verwendet ein Modulmuster wie AMD oder CommonJS. Wenn Sie eine dieser beiden Typen verwenden, wird jede TypScript-Datei als externes Modul betrachtet und nicht im globalen Gültigkeitsbereich.

In diesem Beispiel wird Angular für die Zwecke des Beispiels entfernt, aber während RequireJS die Methode zu global hinzufügt, wird keiner Ihrer Codes in diesem Bereich platziert.

MyModule.ts

export class myController { 
    constructor($scope) { 
     $scope.myNewProperty = "Bob"; 
    } 
} 

app.ts

import MyModule = require('MyModule'); 

var controller = new MyModule.myController(''); 

HTML

<script src="Scripts/require.js" data-main="app.js"></script> 

Was app.js wie folgt aussieht:

define(["require", "exports", 'MyModule'], function (require, exports, MyModule) { 
    var controller = new MyModule.myController(''); 
}); 

Alternativ ... wie Sie wissen ... können Sie es immer noch mit dem JavaScript, das Sie bereits verwenden, implementieren, wenn Sie möchten - Sie erhalten immer noch eine automatische Vervollständigung und Typprüfung, was große Vorteile sind, auch wenn Sie nicht t Klassen bekommen.

+0

Danke für die Antwort. Ich kenne AMD (RequireJS) oder CommonJS nicht. Das Hinzufügen einer zusätzlichen Bibliothek war eine Komplexität, die ich vermeiden wollte (in der Hoffnung auf eine TS Lösung) ... aber ich werde es untersuchen. – user603563

+0

Es hängt davon ab, wie viel Sie außerhalb des globalen Geltungsbereichs bleiben möchten. Sie könnten ein einzelnes Modul verwenden und es mit jedem Modul erweitern, und das wäre "nur eine Sache im globalen Umfang", was für Sie akzeptabel sein könnte. Diese Antwort ist die Antwort "Kein Code im globalen Gültigkeitsbereich". – Fenton

1

Josh ist richtig. Verwenden Sie das Modul. Außerdem ist es wahrscheinlich eine gute Idee, den Grunt- oder Schluck-Heller zu verwenden, der eine Option hat, um Ihre gesamte Anwendung zum Zeitpunkt der Erstellung in eine Schließung zu verpacken.

Dies ist keine Antwort auf Ihre Frage, sondern eher ein Vorschlag.

Auf einer Seite zur Kenntnis, sollte mit dieser Syntax diese Syntax für Controller

module myModule { 

    // We export the class so that we can access its interface in tests. 
    // The build time gulp or grunt uglify will wrap our app in a closure 
    // so that none of these exports will be available outside the app. 
    export class MyController { 

     myNewProperty = "bob"; 

     // Next we will use Angulars $inject annotation to inject a service 
     // into our controller. We will make this private so that it can be 
     // used internally. 
     static $inject = ['someService']; 
     constructor(private someService: ng.ISomeService) {} 

     // we can access our class members using 'this' 
     doSomething() { 
      this.someService.doSomething(this.myNewProperty); 
     } 

    } 

    angular.module('app').controller('MyController', MyController); 

} 

Zusammen Sie die controllerAs Syntax verwenden würden.

0

Genau wie in JavaScript können Sie die Verunreinigung von Namensräumen vollständig vermeiden, indem Sie Ihre Deklarationen in einen sofort aufgerufenen Funktionsausdruck einfügen.

So das ursprüngliche JavaScript:

(function() { 
    var app = angular.module('myModule', []); 
    app.controller('myController', ['$scope', 
    function ($scope) { 
     $scope.myNewProperty = "Bob"; 
    } 
    ]); 
})(); 

wird die folgende Typoskript:

(function() { 
    class MyController { 
    static $inject = ['$scope']; 
    contructor($scope: ng.IScope & { myNewProperty: string }) { 
     $scope.myNewProperty = 'Bob'; 
    } 
    } 

    angular.module('myModule', []) 
    .controller('MyController', MyController); 
})(); 

Beachten Sie, dass dies keine Namen in den umgebenden Rahmen einführt. Eigentlich ist das ursprüngliche JavaScript ein perfekt gültiges TypeScript, nutzt aber TypeScript nicht aus. Beachten Sie auch, dass ich für Stil leicht bearbeitet habe.

Wenn Sie keine Module mit einem Modulladeprogramm wie RequireJS, SystemJS oder was auch immer verwenden, können Sie auf jeden Fall die Namensraumverschmutzung vermeiden, indem Sie dem bewährten IIFE-Muster folgen. Das ist meine Empfehlung.

Verwandte Themen