Многие из моих недавних постов и презентаций в блоге были посвящены чистому JavaScript. Я считаю, что TypeScript — невероятно полезный инструмент, особенно при разработке тяжелых клиентских приложений большими группами. Я не использую его в большинстве примеров, поэтому они остаются актуальными для разработчиков, которые не приняли его. TypeScript — сильный актив для приложений AngularJS. Недавно меня спросили о структуре моего приложения Angular с использованием TypeScript, поэтому я разработал небольшой пример.
В моем недавнем выступлении о Advanced AngularJS Tips and Tricks я привел довольно интерактивный пример с использованием списка картинок с выбором. Цель состояла в том, чтобы продемонстрировать контроллер как синтаксис и показать, что вы можете выполнить то, что необходимо, без зависимости от явных наблюдателей. Вы можете просмотреть источник ; контроллеры и т. д. находятся под подпапками «01».
Первый шаг к разработке приложений в TypeScript — это выбор любых связанных файлов определений. Я использую репозиторий, который называется « Типизированный» . Существуют определения для большинства общих библиотек, в том числе Angular. Более того, вы также можете установить его в свои проекты Visual Studio с помощью NuGet .
После того, как у меня есть определения типов в моем проекте, я определяю модуль приложения. Модуль инкапсулирует функциональность и предотвращает конфликты имен. Я должен дать вам предупреждение, согласно рекомендациям Google, вы всегда должны ссылаться на свои модули, вызывая angular.module с именем модуля (после того, как вы определили модуль путем передачи зависимостей), а не пытаться захватить глобальную ссылку. Лично у меня нет проблемы с сохранением ссылки, когда я уже создаю область модуля для приложения, поэтому я решил использовать ее.
declare var angular: ng.IAngularStatic;
module Application {
"use strict";
export var angularApp: ng.IModule =
angular.module("angularApp", ["ngAnimate"]);
}
Код использует преимущества однозначно типизированной библиотеки для определения углового статического типа. Это дает мне IntelliSense / автоматическое завершение и проверку во время разработки функциональных вызовов, которые я буду делать. Единственный реальный сгенерированный код — это ссылка на текущий модуль, содержащаяся в переменной angularApp. Сгенерированный код выглядит так:
var Application;
(function (Application) {
"use strict";
Application.angularApp = angular.module(
"angularApp", ["ngAnimate"]);
})(Application || (Application = {}));
Следующий логический компонент для определения — это данные. Для данных я делаю две вещи. Во-первых, я экспортирую интерфейс, чтобы упростить работу с другими приложениями. Это обеспечит обнаружение и проверку типа во время разработки. Во-вторых, я создаю функцию для инкапсуляции определения, прежде чем передать его в Angular. Это закрытие предотвращает «утечку» данных к остальной части приложения (и, возможно, столкновение или дальнейшее загрязнение логических пространств имен), поэтому после вызова единственный способ ссылаться на них — через зависимость от Angular. Я усек список для отображения:
module Application {
"use strict";
export interface ISunsetData {
image: string;
location: string;
title: string;
}
function initData(): void {
var data: ISunsetData[] = [
{
image: "backyard.jpg",
location: "Woodstock, GA",
title: "Sunset in Woods"
}
];
angularApp.value("sunsets", data);
};
initData();
}
После того, как данные определены, я помещаю их в сервис. Сервис предоставляет фильтр, который контроллеры могут использовать, и предоставляет «выбранный элемент», используемый для визуализации полноразмерного изображения. Обратите внимание, что в TypeScript я могу выставить контракт для сервиса, не раскрывая сам сервис. Класс для сервиса не экспортируется, поэтому он доступен только для регистрации в Angular. Когда я ссылаюсь на это, я использую интерфейс.
module Application {
"use strict";
export interface ISunsetService {
sunsets: ISunsetData[];
selected: ISunsetData;
filter: string;
select: (sunset: ISunsetData) => void;
}
class SunsetService implements ISunsetService {
constructor(
public sunsets: ISunsetData[],
public $log: ng.ILogService) {
$log.log("Sunsets service created.");
}
public selected: ISunsetData;
public filter: string;
public select(sunset: ISunsetData): void {
this.$log.log("Selected sunset "
+ sunset.title);
this.selected = sunset;
}
}
angularApp.service("sunsetSvc", [
"sunsets", "$log", SunsetService]);
}
В этом примере я регистрирую службу, используя переменную, которую я установил в основном приложении для ссылки на модуль. Я также использую версию внедрения зависимости во время регистрации, так как я регистрирую класс одновременно с его определением. Если бы я использовал систему для определения класса, а затем зарегистрировал его в другом месте, я бы использовал статическую сигнатуру $ inject: string [], чтобы можно было легко обнаружить зависимости.
У меня нет необходимости выставлять определение контроллера для остальной части приложения, поэтому я просто определяю класс, не экспортируя его, и регистрирую его в Angular. Я использую экспортированные контракты, чтобы привести свойства в конструктор. Обратите внимание, как легко TypeScript позволяет определять свойства get и setter.
module Application {
"use strict";
class SearchController {
private _filter:string;
constructor(
public $log: ng.ILogService,
public sunsetSvc: ISunsetService) {
this._filter = "";
$log.log("Search controller created.");
}
public get filter(): string {
return this._filter;
}
public set filter(val: string) {
this.sunsetSvc.filter = val;
this._filter = val;
}
}
angularApp.controller("searchCtrl", ["$log", "sunsetSvc", SearchController]);
}
Это сгенерированный JavaScript для свойства фильтра:
Object.defineProperty(SearchController.prototype,
"filter", {
get: function () {
return this._filter;
},
set: function (val) {
this.sunsetSvc.filter = val;
this._filter = val;
},
enumerable: true,
configurable: true
});
Контроллер списка похож. Вы можете просмотреть полный исходный код преобразованного проекта TypeScript по этой ссылке . Он является частью исходного проекта и содержит только исходные файлы TypeScript для простоты.
Как видите, TypeScript очень хорошо работает с AngularJS. Файлы определений помогают в изучении API и обеспечении его правильного использования во время разработки. Интерфейсы помогают описать структуру, и их легко содержать в определенных модулях или областях. Хотя все это возможно с чистым JavaScript, язык TypeScript не только облегчает определение этих шаблонов, но и реализует их согласованным образом.
Используете ли вы TypeScript с AngularJS или вы рассматриваете это? Если это так, поделитесь своими мыслями или вопросами в комментариях ниже!