Javascript AngularJS/Typescript集成模式-作用域方法
我正在尝试改变我编写AngularJS应用程序的方式,从简单的javascript改为使用TypeScript作为预处理器 在涉及作用域方法调用时,我正在努力协调这两种方法Javascript AngularJS/Typescript集成模式-作用域方法,javascript,angularjs,typescript,Javascript,Angularjs,Typescript,我正在尝试改变我编写AngularJS应用程序的方式,从简单的javascript改为使用TypeScript作为预处理器 在涉及作用域方法调用时,我正在努力协调这两种方法 为了说明的目的,让我们考虑常用菜单的用例;我希望突出显示当前显示的特定菜单项。HTML模板如下所示: <ul class="nav navbar-nav"> ... <li ng-class="{active: isSelected('/page1')}"><a href="#/page
为了说明的目的,让我们考虑常用菜单的用例;我希望突出显示当前显示的特定菜单项。HTML模板如下所示:
<ul class="nav navbar-nav">
...
<li ng-class="{active: isSelected('/page1')}"><a href="#/page1">Page 1</a></li>
...
</ul>
这个匿名函数声明似乎并不真正尊重更传统的TypeScript类模型。在typescript中,我发现自己很想写以下内容:
export interface MenuScope extends ng.IScope {
isSelected(path: String): boolean;
}
export class MenuController {
location: ng.ILocationService;
scope: MenuScope;
constructor($scope: MenuScope, $location: ng.ILocationService) {
this.scope = $scope;
this.location = $location;
this.scope.isSelected = function(path) { return this.isSelected(path) }.bind(this);
}
isSelected(path: String): boolean {
return this.location.path().substr(0, path.length) == path;
}
}
在这种情况下,isSelected
属于控制器,而不是范围。这似乎是明智的。但是,作用域和控制器之间的“链接”仍然依赖于匿名方法
更糟糕的是,我必须显式绑定this
的上下文,以确保我可以编写this.location
来访问isSelected()实现中的位置服务
我希望从TypeScript中获得的好处之一是更清晰的代码编写方式。通过绑定的匿名函数进行的间接寻址似乎与此相反。您不应该将$scope
作为变量存储在This
中,而是将This
用作$scope
,方法是这样做$scope.vm=This在构造函数的开头,现在类的每个函数和变量都将是$scope的一部分
您无法避免this.location=$location
,因为这是TypeScript的语法
顺便说一句,您应该对依赖项使用$inject。我们正在考虑类似的转换(例如,从Javascript到Angular的Typescript)。在我们开始实施时,有些事情(比如您的示例)看起来非常奇怪。最快的方法是使用控制器作为
语法。这样,就可以直接在控制器上公开方法
<!-- use controller as syntax -->
<div ng-controller="MenuController as menu">
<ul class="nav navbar-nav">
...
<li ng-class="{active: menu.isSelected('/page1')}"><a href="#/page1">Page 1</a></li>
...
</ul>
</div>
由于angular负责实例化控制器,因此返回类型any
无关紧要。但是如果你的$scope方法有错误,你可能会被抓住
要解决这个问题,您可以进一步使用controller as
语法。下面的示例不允许您自己实际更新控制器(例如,new MenuController($location)将因而失败,只能使用new关键字调用void函数,但这是可以忽略的,因为angular为您处理实例化
export interface IMenuController {
isSelected(path: string): boolean;
}
export function MenuController($location: ng.ILocationService): IMenuController {
var self:IMenuController = this;
self.isSelected = function(path:string): boolean {
return $location.path().substr(0, path.length) == path;
}
// explicitly return self / this to compile
return self;
}
TL-DR:我非常喜欢类型的编译时检查,并且喜欢使用类的概念。然而,我不认为它完全符合Angular 1.x模型。这似乎是为Angular2设计的。使用强类型函数代替Angular 1.x。这是一个带有控制器和服务的简单应用程序。我在项目中使用此样式:
/// <reference path="typings/angularjs/angular.d.ts" />
module App {
var app = angular.module("app", []);
app.controller("MainController as vm", Controllers.MainController);
app.service("backend", Services.Backend);
}
module App.Controllers {
export class MainController {
public persons: Models.Person[];
static $inject = ["$location", "backend"];
constructor(private $location: ng.ILocationService, private backend: Services.Backend) {
this.getAllPersons();
}
public isSelected(path: string): boolean {
return this.$location.path().substr(0, path.length) == path;
}
public getAllPersons() {
this.backend.getAllPersons()
.then((persons) => {
this.persons = persons;
})
.catch((reason) => console.log(reason));
}
}
}
module App.Services {
export class Backend {
static $inject = ["$http"];
constructor(private $http: ng.IHttpService) { }
public getAllPersons(): ng.IPromise<Models.Person[]> {
return this.$http.get("api/person")
.then((response) => response.data);
}
}
}
module App.Models {
export interface Person {
id: number;
firstName: string;
lastName: string;
}
}
//
模块应用程序{
var-app=angular.module(“app”,[]);
app.controller(“MainController作为vm”,Controllers.MainController);
app.service(“后端”,Services.backend);
}
模块应用程序控制器{
导出类主控制器{
公众人物:模特。人物[];
静态$inject=[“$location”,“backend”];
构造函数(私有$location:ng.ILocationService,私有后端:Services.backend){
这个.getAllPersons();
}
公共isSelected(路径:字符串):布尔值{
返回此.$location.path().substr(0,path.length)=path;
}
公众人物(){
this.backend.getAllPersons()
.然后((人)=>{
这个人=人;
})
.catch((原因)=>console.log(原因));
}
}
}
模块应用程序服务{
导出类后端{
静态$inject=[“$http”];
构造函数(私有$http:ng.IHttpService){}
public getAllPersons():ng.IPromise{
返回此。$http.get(“api/人”)
.然后((响应)=>response.data);
}
}
}
模块应用程序模型{
出口接口人员{
id:编号;
名字:字符串;
lastName:string;
}
}
我有应用程序模块、控制器模块、服务模块和模型模块
控制器定义为类,但必须通过控制器作为
语法注册到应用程序。因此,您在类中定义的所有内容都可以通过视图(控制器范围)中的vm
访问。这里有persons
,isSelected
和getAllPersons
您可以通过static$inject
注入每一个可注入的元素,这是一个字符串[]
,然后将它们分别添加为构造函数参数。此角色在定义服务时也可用,并且可以缩小
您还可以将$scope
注入控制器类,以访问特定于作用域的工具,如$apply
,on
等
您可以定义服务来将它们定义为类,而不是定义工厂
在服务中注入与在控制器中注入相同
您可以将http调用的返回类型定义为ng.IPromise
,然后返回response.data
,以确保返回方法的类型只是实体,而不是与http相关的数据
在TAX中使用“Controller as vm”将起作用,然后您应该调用vm.IsSelected请参阅Angular Style Guide(由Angular团队提供)的部分,并尽可能避免使用$scope
。
export interface IMenuController {
isSelected(path: string): boolean;
}
export function MenuController($location: ng.ILocationService): IMenuController {
var self:IMenuController = this;
self.isSelected = function(path:string): boolean {
return $location.path().substr(0, path.length) == path;
}
// explicitly return self / this to compile
return self;
}
/// <reference path="typings/angularjs/angular.d.ts" />
module App {
var app = angular.module("app", []);
app.controller("MainController as vm", Controllers.MainController);
app.service("backend", Services.Backend);
}
module App.Controllers {
export class MainController {
public persons: Models.Person[];
static $inject = ["$location", "backend"];
constructor(private $location: ng.ILocationService, private backend: Services.Backend) {
this.getAllPersons();
}
public isSelected(path: string): boolean {
return this.$location.path().substr(0, path.length) == path;
}
public getAllPersons() {
this.backend.getAllPersons()
.then((persons) => {
this.persons = persons;
})
.catch((reason) => console.log(reason));
}
}
}
module App.Services {
export class Backend {
static $inject = ["$http"];
constructor(private $http: ng.IHttpService) { }
public getAllPersons(): ng.IPromise<Models.Person[]> {
return this.$http.get("api/person")
.then((response) => response.data);
}
}
}
module App.Models {
export interface Person {
id: number;
firstName: string;
lastName: string;
}
}