SlideShare a Scribd company logo
1 of 60
エンタープライズ分野での実践AngularJS 
2014/11/5
自己紹介 
後藤歩(@balmychan) 
株式会社オロ 
CakePHP, Rails, iOS, AndroidなどのWeb系、アプリ系の受託開発 
家族向け写真共有サービスnicori の立ち上げ、開発 
クラウド管理会計ZAC Enterprise の開発
目的 
Who 
これから業務システムでAngularJSの導入を考えている方。 
What 
弊社での開発の構成や流れと、TypeScriptでのソース例、 
業務システムであるあるな機能の実装例について、紹介します。 
Why 
AngularJSが業務システムで使われているケースの情報は少ないので、情報交換できれば
1.開発対象や体制、導入のきっかけ、開発フロー、フォルダ構成などの説明 
2.モジュール、コントローラー、サービス、ディレクティブ、バリデーションなどの説明 
3.その他Tips
1.開発対象、構成について
開発対象概要 
開発対象 
ZAC Enterprise(管理会計システム) 
開発体制 
日本3~4名 
海外(ベトナム)1名 
※将来的には、20~30名のエンジニアが開発する想定 
技術構成 
クライアントサイドAngularJS + TypeScript 
サーバーサイドASP.NET + ASP.NET Web API 2 + SQL Server
What is ZAC? 
案件・プロジェクトベースで、売上や原価の管理を行う 
統合型基幹業務システム(ERPパッケージです) 
・販売管理 
・購買管理 
・在庫管理 
・勤怠管理 
・工数管理 
・経費管理 
・and more... 
http://www.oro.co.jp/zac/ 
https://www.oro.co.jp/reforma-psa/ (小規模向けのReformaPSAというサービスも10月からスタートしました) 
ぜひご検討を!!
導入のきっかけ 
コード量を減らしたい→ data binding 
UIを統一したい→ directive 
クライアント側の開発を、多人数開発で効率よく行いたい→ フルスタック 
検討を開始したのは、2013年10月ごろ 
利用は、2014年5月ごろから
AngularJSの構成 
モジュール 
ルーティング(ui.router)は使用せず、angular-translateと、directiveの共通コンポーネント化、 
serviceのビジネスロジックの共通化、実装方式の共通化を図るために導入 
ディレクティブ 
約50個(テンプレート代わりや、新しいインターフェース) 
サービス 
約40個(サーバーサイドからのデータ取得や共通処理をまとめる) 
フィルター 
約15個
チケット管理フロー
フォルダ構成 
/b 既存のシステムと共存できる用、フォルダを分けた 
/Content ASP.NETの構成に準拠 
/css 
/common 共通のcssファイル(sassをここに入れて、VisualStudioで自動コンパイ 
ル) 
/directives directive用のcss, sassファイル 
/Script 
/vendor AngularJS本体や、各種プラグイン 
/zac 
/common 既存のライブラリなど 
/directives directive本体が入る 
/services service本体が入る 
/UI 
/Views ベースHTMLが入る 
/directives directive用のHTMLファイル 
/services 
/UI UIを伴うservice用のHTMLファイル 
/project 各機能画面(これは、例として案件管理) 
/views 案件管理機能のみで使用する、HTMLファイル(専用 
directive用も入る) 
/directives 案件管理機能のみで使用する、directive本体が入る 
project.ts ルーティング用のファイル 
project-controller.ts AngularJS用のコントローラー 
index.cshtml HTMLファイル(一部ASP.NETのテンプレートを使用) 
/approval 
~~~ /project と同じ構成~~~
hotfix_YYMMD master 
D 
feature_XXX develop release 
ブランチ 
timeline
ベースHTML 
作成 
コントローラ 
ー作成 
API作成 
(サーバーサイ 
ド) 
APIに対応す 
る 
サービス 
作成 
モデルクラス 
作る 
HTMLに肉付 
けをしていく 
HTMLをディ 
レクティブ化 
する 
必要ならフィ 
ルターも作る 
完成! 
AngularJSを中心に据えた場合の、画面開発の流れ 
※T4 Templateで、C#側のモ 
デルを修正すると、 
TypeScript側のモデルのコー 
ドも自動で吐くようになって 
ます 
明らかな場合は、ディレクテ 
ィブに分けて作り始めます 
ワイヤー 
作成 
デザイン 
作成 
HTML 
モック作成
ちなみに、普段の開発がVisualStudioという方いらっしゃいますか?
VisualStudio + TypeScript + AngularJSで幸せになれます!
VisualStudio 
Visual Studio 2013 Express for Webが無料 
WebEssentialsを入れると、sassのコンパイルやminifyもラクラク 
TypeScriptの恩恵を最大限受けられる 
TypeScript 
Visual Studio 2013 Update 2からversion 1.0がリリース 
Express for Webでも使える 
AngularJS 
最新の1.3.1もNuGetですぐ入る
AngularJSを使っているとディレクティブや、サービス、DI、色々な概念が登場します。 
その書き方をTypeScriptを交えて紹介していきます。
TypeScriptについてちょっとだけ 
TypeScriptはMicrosoft製のaltJSで、ECMAScript 6の提案を多く踏襲し、既存のJavaScriptと混在して使用することが可能です。 
JavaScriptにクラス、インターフェース、列挙型、ジェネリクス、ラムダ式、静的型付機能を加えたものです。 
VisualStudioを使うと、コード補完や定義元へのジャンプもラクラク。 
// クラス定義 
class Hoge { 
public title; 
public get() { 
} 
} 
TypeScript // 静的型付 
TypeScript 
var a:number; 
a = "hoge"; // これはエラー 
a = <any>"hoge"; // いざとなったらanyが使えるが、お行儀は良くない 
// 列挙型 
enum Type { 
One = 1, 
Two, 
}; 
TypeScript // ラムダ式(thisが維持されるので、クラス定義内で使うと便利) 
(a: number, b: string) => { 
} 
TypeScript
型情報について 
TypeScriptを使っている場合、型情報を定義したファイルが必要です。(コンパイルエラーになります) 
下記から使用したい型情報が書かれたファイルをダウンロードし参照させます。 
http://definitelytyped.org/ 
VisualStudioを使っている場合、NuGetで取得することも可能です。 
declare module ng { 
// not directly implemented, but ensures that constructed class implements $get 
interface IServiceProviderClass { 
new (...args: any[]): IServiceProvider; 
} 
interface IServiceProviderFactory { 
(...args: any[]): IServiceProvider; 
} 
// All service providers extend this interface 
interface IServiceProvider { 
$get: any; 
} 
angular.d.ts
TypeScriptのコードを紹介していきます
モジュール 
var app = angular.module("app", ["ui.router", "ui.bootstrap", "ui.sortable"]); JavaScript 
app.config(["$stateProvicer", function($stateProvider) { 
// providerの初期化処理など 
}); 
var app = angular.module("app", ["ui.router", "ui.bootstrap", "ui.sortable"]); 
app.config(["$stateProvider", ($stateProvider: ng.ui.IStateProvider) => { 
// providerの初期化処理など 
}); 
TypeScript 
まずはモジュールを作成する 
特に大きな違いはありませんが、DIするオブジェクトにも型を指定可能
コントローラー 
コントローラーは、直接タグに指定する形と、ルーティングで設定する形(今回は、ui routerを使っています) 
<body ng-controller="myController"> 
</body> 
HTML 
OR 
$stateProvider.state("hoge", { 
url: "/hoge", 
templateUrl: "index.cshtml", 
caseInsensitiveMatch: false, 
controller: "myController" 
}) 
JavaScript
app.controller("myController", ["$scope", "serviceA", function($scope, serviceA) { 
$scope.title = "hoge"; 
}]); 
JavaScript 
module Controllers { 
'use strict'; 
export interface IMyControllerScope extends ng.IScope { 
title: string; 
} 
export class MyController { 
public static $inject = ["$scope", "serviceA"]; 
constructor($scope: IMyControllerScope , serviceA: Services.ServiceA) { 
$scope.title = "hoge"; 
} 
} 
angular.module("app").controller("myController", MyController); 
} 
TypeScript 
JavaScriptの場合 
TypeScriptの場合
ディレクティブ 
<hoge-directive title="hoge" max-count="5" /> 
angular.module("app").directive("hogeDirective", ["serviceA", "serviceB", function(serviceA, serviceB) { 
return { 
restrict: "EA", 
require: "?ngModel", 
scope: { 
"title": "?=", 
"max-count": "?=" 
}, 
templateUrl: this.serviceB.RootPath + "/HogeDirective.html", 
link: function(scope, element, attrs, ctrls, transclude) { 
element.find("span.title").text(scope.title); 
serviceA.do(function(result) { 
}); 
} 
}; 
}]); 
HTML 
JavaScript 
titleとmax-countを取るような単純なディレクティブ
module Directives { 
'use strict'; 
export interface HogeDirectiveScope { 
title: string; 
maxCount: number; 
} 
export class HogeDirective { 
public restrict = "EA"; 
public require = "?ngModel"; 
public templateUrl = this.serviceB.RootPath + "/HogeDirective.html"; // templateを指定することもあります 
public static $inject = ["serviceA", "serviceB"]; // $injectorを使うため、ここでDIするモジュールを定義している 
constructor(private serviceA: ServiceA, private serviceB: ServiceB) { 
// コンストラクタにprivateなどのアクセス修飾子をつけると、変数が自動で定義されます 
} 
public link = (scope: HogeDirectiveScope, element: JQuery, attrs: ng.IAttributes) { 
element.find("span.title").text(scope.title); 
serviceA.do((result) => { 
}); 
}; 
} 
angular.module("app").directive("hogeDirective", ["$injector", ($injector) => { 
return $injector.instantiate(HogeDirective); 
}]); 
// Directives.addDirective(HogeDirective); // 実際はこういったヘルパーを用意しています 
} 
TypeScript
サービス 
angular.module("app").service("zacCookie", function() { 
return { 
read: function(name) { 
return jQuery.cookie(name); 
}, 
write: function(name, value, options) { 
jQuery.cookie(name, value, options); 
}, 
remove: function(name, options) { 
return jQuery.removeCookie(name, options); 
} 
} 
}); 
JavaScript 
jQuery cookieをラップするサービス(デフォルトのangularjsのcookieライブラリは、pathが変えられれないため 
JavaScriptの場合
module Services { 
'use strict'; 
export class ZacCookie { 
public read(name: string): any { 
return jQuery.cookie(name); 
} 
public write(name: string, value: any, options?: any): void { 
jQuery.cookie(name, value, options); 
} 
public remove(name: string, options? : any) : boolean { 
return jQuery.removeCookie(name, options); 
} 
} 
angular.module('app.services').ser 
vice("zacCookie", function () { 
return new ZacCookie(); 
}); 
} 
TypeScript 
TypeScriptの場合
APIと連携するサービス 
API側からデータを取ってくるサービスを作る場合、$resourceを使っています(内部で$httpを使う形でも良いかと思います) 
angular.module("app").service("projectService", ["$resource", function($resource) { 
return $resource('/Project', {}, { 
'get': { 
url:"/Projects/:id', 
method: 'GET', 
cache: true 
}, 
'query': { 
method: 'GET', 
url: "/Projects" 
params: { /* デフォルト値があれば設定*/ } 
cache: true, 
isArray: true 
}, 
'check': { // Restfulでないメソッドも追加可能 
method: 'GET' 
params: {} 
url: "/Projects/Check/:id 
} 
} 
}]); 
JavaScript
module Services { 
'use strict'; 
export interface IProjectService extends ng.resource.IResourceClass<Models.ProjectItem> { 
check(params?: Object, success?: Function, error?: Function): ng.IPromise<Models.ProjectItem>; 
} 
export function ProjectService($resource: ng.resource.IResourceService) { 
return $resource<Models.ProjectItem>("/Project', {}, { 
'get': { 
url:"/Projects/:id', 
method: 'GET', 
cache: true 
}, 
'query': { 
method: 'GET', 
url: "/Projects" 
params: { /* デフォルト値があれば設定*/ } 
cache: true, 
isArray: true 
}, 
'check': { // Restfulでないメソッドも追加可能 
method: 'GET' 
params: {} 
url: "/Projects/Check/:id 
} 
}]); 
} 
ProjectService.$inject = ["$resource"]; 
angular.module("app").factory("projectService", ProjectService); 
} 
TypeScript 
TypeScriptの場合
フィルター 
例として、単純に案件番号の先頭に"#"を付けるフィルター 
<div class="project-number">{{ project.id | projectNumber}}</div> HTML
module Filters { 
'use strict'; 
export function ProjectNumber(serviceA: Services.ServiceA) { 
return function (input) { 
if (angular.isUndefined(input) || input == null) return ""; 
return "#" + input; 
} 
} 
angular.module("app").filter("projectNumber", ProjectNumber); 
} 
TypeScript 
angular.module("app").filter("projectNumber", ["serviceA, function(serviceA) { 
return function(input) { 
if (angular.isUndefined(input) || input == null) return ""; 
return "#" + input; 
} 
}]); 
JavaScript 
JavaScriptの場合 
TypeScriptの場合
フォーム 
<form ng-form name="myForm"> 
<input type="text" name="title" ng-model="title" ng-maxlength="5"> 
<div ng-if="myForm.title.$error.maxlength">5文字を超えています</div> 
</form> 
HTML 
既存のフォームの書き方は、特に変わりはないです(そりゃそうだ・・・)
カスタムインプットhttp://plnkr.co/edit/AWlyEIldDBYB8rKSnF3d 
独自のカスタムインプットを作る場合(たとえば、氏名を入力するインプットをディレクティブ化) 
<form ng-form name="myForm"> 
<fullname-input></fullname-input> 
<button ng-click="reset()">リセット</button> 
</form> 
HTML
app.controller('myController', ['$scope', function($scope) { 
$scope.name = "taro yamada"; 
$scope.reset = function() { 
$scope.name = "taro yamada"; 
$scope.myForm.$setPristine(); 
}; 
}]); 
app.directive("fullnameInput", [function() { 
return { 
restrict: "EA", 
require: ["?ngModel", "?^form"], 
scope: {}, 
replace: true, 
template: "<div><input type='text' ng-model='firstName'><input type='text' ng-model='lastName'></div>", 
link: function(scope, element, attrs, ctrls, transclude) { 
var ngModelCtrl = ctrls[0]; 
var ngFormCtrl = ctrls[1]; 
if (ngFormCtrl) { 
// 標準のinput要素とは別で、状態が変わる操作があれば、明示的にngFormCtrl.$setDirty();を呼ぶ 
ngFormCtrl.$setDirty(); 
} 
JavaScript 
JavaScriptの場合
JavaScript 
replace: true, 
template: "<div><input type='text' ng-model='firstName'><input type='text' ng-model='lastName'></div>", 
link: function(scope, element, attrs, ctrls, transclude) { 
var ngModelCtrl = ctrls[0]; 
var ngFormCtrl = ctrls[1]; 
if (ngFormCtrl) { 
// 標準のinput要素とは別で、状態が変わる操作があれば、明示的にngFormCtrl.$setDirty();を呼ぶ 
ngFormCtrl.$setDirty(); 
} 
if (ngModelCtrl) { 
ngModelCtrl.$render = function() { 
var value = ngModelCtrl.$modelValue || ngModelCtrl.$viewValue; 
if (angular.isUndefined(value) || value === null) { 
// クリア処理 
scope.firstName = ""; 
scope.lastName = ""; 
} else { 
// 値をelementに設定する処理 
var names = value.split(" "); 
scope.firstName = (0 < names.length) ? names[0] : ""; 
scope.lastName = (1 < names.length) ? names[1] : ""; 
} 
} 
}
JavaScript 
} 
function setViewValue() { 
var name = scope.firstName + (scope.lastName ? " " : "") + scope.lastName; 
ngModelCtrl.$setViewValue(name); 
} 
scope.$watch("firstName", function(newValue) { 
setViewValue(); 
}); 
scope.$watch("lastName", function(newValue) { 
setViewValue(); 
}); 
} 
}; 
}]);
ngModelControllerが内部に持つ値を見ると、$viewValue, $modelValueという二つの値があります。 
この役割を押させておくのが、独自のカスタムインプットを作る上で重要です。$renderもそのうちの一つです。 
data bindingで、外部から変更された場合 
$modelValue $formatters 
$viewValue 
ユーザー操作で、変更された場合 
$viewValue $parsers $modelValue 
$viewValueが変更されると$renderが呼ばれる。そこでelementを更新する 
$render 
$viewValueが変更 
elementの更新処 
理など 
リセット処理について 
$setPristineなどの処理は、あくまでもスタイ 
ルの変更にのみ使用する 
リセット処理は、ng-modelを通して行う 
($modelValueがundefinedやnullの場合、リセ 
ットとみなし、DOMを更新する)
バリデーションhttp://plnkr.co/edit/o4WYlrbiKwH4ql6TNVoB?p=info 
<form name="myForm"> 
<input type="text" name="name" custom-maxlength="5" ng-model="name"> 
<div ng-if="myForm.name.$error.customMaxlength">5文字を超えています</div> 
</form> 
HTML 
ng-maxlengthを独自に実装してみる
app.directive("customMaxlength", function() { 
return { 
restrict: "A", 
require: "ngModel", 
link: function(scope, element, attrs, ngModelCtrl) { 
var maxlength = attrs['customMaxlength']; 
if (maxlength) { 
ngModelCtrl.$validators['customMaxlength'] = function(modelValue, viewValue) { 
var value = modelValue || viewValue; 
if (angular.isUndefined(value) || value === null) return true; 
return value.length <= maxlength; 
} 
} 
} 
} 
} 
}); 
JavaScript 
JavaScriptの場合
module Directives { 
'use strict'; 
export class CustomMaxlength { 
public restrict = "A"; 
public require = "ngModel"; 
constructor() { 
} 
public link = (scope:ng.IScope, element: JQuery, attrs: ng.IAttributes, ngModelCtrl: ng.INgModelController) { 
var maxlength: number = attrs['customMaxlength']; 
if (maxlength) { 
ngModelCtrl.$validators['customMaxlength'] = function(modelValue, viewValue) { 
var value = modelValue || viewValue; 
if (angular.isUndefined(value) || value === null) return true; 
return value.length <= maxlength; 
} 
} 
}; 
} 
angular.module("app").directive("customMaxlength", ["$injector", ($injector) => { 
return $injector.instantiate(CustomMaxlength); 
}]); 
} 
TypeScript 
TypeScriptの場合
非同期バリデーション 
<form name="myForm"> 
<input type="text" name="projectCode" check-project-code ng-model="projectCode"> 
<div ng-if="myForm.projectCode.$error.checkProjectCode">無効なコードです</div> 
</form> 
HTML 
サーバー側でコードチェックを行いたい、というのはよくあります。 
check-project-codeを実装してみる
app.directive("checkProjectCode", ["$q", "projectService", function($q, projectService) { 
return { 
restrict: "A", 
require: "ngModel", 
link: function(scope, element, attrs, ngModelCtrl) { 
var deferred =$q.defer(); 
projectService.get(function(project) { 
// なんらかの追加のチェックをここで行う 
deferred.resolve(project); 
}, function(error) { 
deferred.reject(error); 
}); 
return deferred.promise; 
} 
} 
}]); 
JavaScript 
JavaScriptの場合 
※非同期バリデーションの場合、promiseを返す。有効な値の場合はresolve、無効な値の場合は、rejectを呼び出す 
サービスがpromiseを返すようになっている場合($http.getなど)そのまま返しても良い
module Directives { 
'use strict'; 
export class CheckProjectCode { 
public restrict = "A"; 
public require = "ngModel"; 
public static $inject = ["$q", "projectService"]; 
constructor($q: ng.IQService, projectService: Services.Project) { 
} 
public link = (scope:ng.IScope, element: JQuery, attrs: ng.IAttributes, ngModelCtrl: ng.INgModelController) { 
var deferred = this.$q.defer(); 
this.projectService.get((project) => { 
deferred.resolve(project); 
}, (error) => { 
deferred.reject(error); 
}); 
return deferred.promise; 
}; 
} 
angular.module("app").directive("checkProjectCode", ["$injector", ($injector) => { 
return $injector.instantiate(CheckProjectCode ); 
}]); 
} 
TypeScript 
TypeScriptの場合
ここからは、AngularJSとは直接関係ない 
業務システムで必要になる機能を一部ご紹介
レイアウト制御 
管理画面でレイアウトを変えれるようにするとか、よくありますよね 
権限制御 
ユーザーの権限によって項目の表示/非表示を制御 
カスタマイズ対応 
利用ユーザーごと(個社)ごとに分岐するための心得
レイアウト 
<custom-layout="Layout1"> 
<div ng-repeat="layout in layouts"> 
<span>{{ layout.name }}</span> 
</div> 
</custom-layout> 
HTML 
単純なレイアウト(ng-repeatで繰り返す) 
分岐を伴うレイアウト 
<custom-layout="Layout2"> 
<div ng-repeat="layout in layouts"> 
<div ng-if="layout.type == 'hoge'"> 
</div> 
<div ng-if="layout.type == 'fuga'"> 
</div> 
<layout-item item="layout" /> <!-- 分岐の中身が複雑な場合、専用のdirectiveを作ります--> 
</div> 
</custom-layout> 
HTML
レイアウト 
module Directives { 
export interface ICustomLayoutScope extends ng.IScope { 
layouts:LayoutItem[]; // 別で定義した、モデルクラスデータ(ASP.NET側とDSLを使ってモデルクラスのスキーマは 
共有しています) 
} 
export class CustomLayout { 
public static $inject = ["layoutService"]; 
public restrict = "EA"; 
public scope = {}; 
public transclude = true; 
public template = "<div ng-transclude></div>"; 
constructor(private layoutService: Services.LayoutService) { 
} 
public link = (scope: ng.IScope, element: JQuery, attrs: ng.IAttributes) { 
var layoutName = attrs["zacLayout"]; 
if (layoutName) { 
layoutService.get(layoutName, function(layouts) { 
scope.layouts = layouts; 
}); 
} 
} 
} 
} 
TypeScript
権限 
<div class="a" has-permission="permissionA"><!-- name --></div> 
<div class="b" has-permission="permissionB"><!-- sales --></div> 
<div class="c" has-permission="permissionC"><!-- cost --></div> 
HTML 
権限の有無によって、表示/非表示を切り替える 
例えば、人によって売上データを見せたくないという場合がある。 
API側ではデータを制御し、AngularJS側では表示を制御する
module Directives { 
export interface IHasPermission { 
show: boolean; 
}; 
export class HasPermission { 
public restrict = "A"; 
public static $inject = ["permissionService"]; 
constructor(permissionService: Services.Permission) { 
} 
public link = (scope:IHasPermission, element: JQuery, attrs: ng.IAttributes) => { 
element.hide(); 
var typePermission= attrs["hasPermission"] || ""; 
if (typePermission == "") return; 
this.permissionService.get(typePermission, (hasPermission) => { // サーバーから権限情報を取得 
if (hasPermission) { 
element.show(); // 権限があれば、elementを表示する 
} 
}); 
}; 
} 
} 
TypeScript 
単純に、サーバーから権限情報を取得して、表示を制御する
<permission-manager> 
<div class="a" has-permission="permissionA"><!-- name --></div> 
<div class="b" has-permission="permissionB"><!-- sales --></div> 
<div class="c" has-permission="permissionC"><!-- cost --></div> 
</permission-manager> 
HTML 
実際は、個別に取得すると重いので(AngularJSを使ってると、気づくと非同期処理が大量になりがちです) 
親ディレクティブを追加して、子ディレクティブと連携し、まとめて取得するようにしています
export class HasPermission { 
// 省略 
public require = "?^PermissionManager"; 
public template = "<div ng-if="show" ng-transclude></div>"; 
constructor(permissionService: Services.Permission) { 
} 
public link = (scope: IHasAuthority, element: JQuery, attrs: ng.IAttributes, ctrl: PermissionManagerCtrl) => { 
var get = ctrl ? ctrl.get || permissionService.get; // PermissionManagerがいたら、そっちのgetを使う 
get(typePermission, (hasPermission) => { 
if (hasPermission) { 
element.show(); // 権限があれば、elementを表示する 
} 
}); 
}; 
} 
TypeScript
export class PermissionManagerCtrl { 
public queue: any = []; 
public get = (typePermission: string, success: (hasPermission: boolean) => void) => { 
this.queue.push({ 
typePermission:typePermission, 
success: success 
}); 
}; 
public processAllQueues = () => { 
// 1.サーバーから権限情報(queueの中身を使って)をまとめて取得する(APIを用意してお 
く) 
// 2.queue内のsuccessをそれぞれ呼び出す 
} 
} 
TypeScript 
子ディレクティブからアクセスするためのコントローラーを用意
export class PermissionManager { 
public restrict = "EA"; 
public transclude = true; 
public template = "<div ng-transclude></div>"; 
public controller = PermissionManagerController; 
constructor() { 
} 
public link = (scope: IHasPermission, element: JQuery, attrs: ng.IAttributes) => { 
this.controller.processAllQueues(); 
}; 
} 
TypeScript 
linkではprocessAllQueuesを呼び出す
カスタマイズ対応 
5つの心得 
1.Controllerは肥大化させない(scopeで渡すだけに専念させる) 個社で画面を作り直すときに、ほとんど再利用できなくなります 
2.View側も、ディレクティブを使ってパーツ化を進める上と同じ 
3.環境ごとの対応は、ControllerとViewを作り直したほうが、コストが低くなる分岐点がある(保守のコストも鑑みて) 
Controllerが肥大化していなく、Viewがディレクティブ化されていれば、新しい画面を作るのは楽です 
4.画面を作り直すほどではない、多少の分岐をどうしても入れたい場合、ディレクティブを分けて分岐する 
5.そもそも個社毎にカスタムしないで済むように作る
3.その他Tips
Tips 
フィルター処理は、サーバーor クライアントサイドどっちでするべきか 
「サーバーサイドはAPIのみの実装にしましょう。」という話はよく聞きますが、 
フォーマット処理などはどちらにすればいいの?と疑問に思うことがあります。 
僕のチームでは、フィルター処理はサーバーサイドで行っています。 
多言語対応も考えると、API側で可能な限り言語も言語ごとのフォーマットをしてから、返すべきだからです。 
どうもクライアント側に色々処理をもたせようとして、色々フィルターを作ってしまうのですが、 
それはクライアント側でするべきか考えましょう!フィルター処理はご存知の通り、重いです。 
認証後の認証情報(例えばユーザーIDなど)はどこに持たせておくべきか 
これは、ZACではheadのmetaで持たせています。(良いやり方とは思っていませんが) 
API側はOAuth認証等でクライアントと分離して認証できるべきかと思います。 
その中で、AngularJS側でセッションを管理するサービスを作り、ユーザー情報を取得するようにすると良い、と思いま 
す。 
アプリ+ APIの構成でアプリを作っていると、自ずとログイン情報を取得するAPIを作り、 
アプリ側でログイン情報をキャッシュすると思いますが、考え方はアプリを作成する時と同じです。
・VisualStudio + TypeScript + AngularJSで、Windows畑でも導入可能 
フルスタックで型セーフなのは、多人数開発にも有利です 
・TypeScriptのサンプルは少ないですが、JSとの互換性が高い 
AngularJSのモジュールの考え方と方向性があっている 
・基幹システム開発になると、権限管理や複雑なインプット 
大量データ表示など、通常のWeb開発では当たらない問題が多いが 
AngularJSを使用すると、スマートに解決できる 
サービス、ディレクティブ、コントローラー、フィルターをうまく使いこなす 
まとめ
ベストプラクティスではなく、模索中の部分が多いです 
ぜひ、情報交換させてください 
最後に
新卒、中途問わず 
最後に(パート2) 
エンジニア仲間募集中です 
興味ありましたらお声がけください!!
ありがとうございました。

More Related Content

What's hot

Angular js はまりどころ
Angular js はまりどころAngular js はまりどころ
Angular js はまりどころAyumi Goto
 
3分でわかるangular js
3分でわかるangular js3分でわかるangular js
3分でわかるangular jsShin Adachi
 
AngularJSでの非同期処理の話
AngularJSでの非同期処理の話AngularJSでの非同期処理の話
AngularJSでの非同期処理の話Yosuke Onoue
 
jQuery/Html5/ASP.NET MVC 対応コンポーネントを用いたデバイス対応業務アプリケーション開発
jQuery/Html5/ASP.NET MVC 対応コンポーネントを用いたデバイス対応業務アプリケーション開発jQuery/Html5/ASP.NET MVC 対応コンポーネントを用いたデバイス対応業務アプリケーション開発
jQuery/Html5/ASP.NET MVC 対応コンポーネントを用いたデバイス対応業務アプリケーション開発Daizen Ikehara
 
ASP.NET MVC と jQuery で実践する標準志向 Web 開発
ASP.NET MVC と jQuery で実践する標準志向 Web 開発ASP.NET MVC と jQuery で実践する標準志向 Web 開発
ASP.NET MVC と jQuery で実践する標準志向 Web 開発Akira Inoue
 
AngularJSの高速化
AngularJSの高速化AngularJSの高速化
AngularJSの高速化Kon Yuichi
 
React を導入した フロントエンド開発
React を導入したフロントエンド開発React を導入したフロントエンド開発
React を導入した フロントエンド開発 daisuke-a-matsui
 
React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門spring_raining
 
Angular js開発事例
Angular js開発事例Angular js開発事例
Angular js開発事例Shun Takeyama
 
今からでも遅くない! React事始め
今からでも遅くない! React事始め今からでも遅くない! React事始め
今からでも遅くない! React事始めynaruta
 
Enterprise x AngularJS
Enterprise x AngularJSEnterprise x AngularJS
Enterprise x AngularJSKenichi Kanai
 
はじめてのVue.js
はじめてのVue.jsはじめてのVue.js
はじめてのVue.jskamiyam .
 
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティスコンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、ReactのベストプラクティスとバッドプラクティスKohei Asai
 
angular1脳で見るangular2
angular1脳で見るangular2angular1脳で見るangular2
angular1脳で見るangular2Moriyuki Arakawa
 
チュートリアルではじめるVue.js
チュートリアルではじめるVue.jsチュートリアルではじめるVue.js
チュートリアルではじめるVue.js小川 昌吾
 
Brush up your Coding! 2013 winter
Brush up your Coding! 2013 winterBrush up your Coding! 2013 winter
Brush up your Coding! 2013 winterShogo Sensui
 

What's hot (20)

Angular js はまりどころ
Angular js はまりどころAngular js はまりどころ
Angular js はまりどころ
 
3分でわかるangular js
3分でわかるangular js3分でわかるangular js
3分でわかるangular js
 
AngularJSでの非同期処理の話
AngularJSでの非同期処理の話AngularJSでの非同期処理の話
AngularJSでの非同期処理の話
 
jQuery/Html5/ASP.NET MVC 対応コンポーネントを用いたデバイス対応業務アプリケーション開発
jQuery/Html5/ASP.NET MVC 対応コンポーネントを用いたデバイス対応業務アプリケーション開発jQuery/Html5/ASP.NET MVC 対応コンポーネントを用いたデバイス対応業務アプリケーション開発
jQuery/Html5/ASP.NET MVC 対応コンポーネントを用いたデバイス対応業務アプリケーション開発
 
ASP.NET MVC と jQuery で実践する標準志向 Web 開発
ASP.NET MVC と jQuery で実践する標準志向 Web 開発ASP.NET MVC と jQuery で実践する標準志向 Web 開発
ASP.NET MVC と jQuery で実践する標準志向 Web 開発
 
ASP.NET MVC 1.0
ASP.NET MVC 1.0ASP.NET MVC 1.0
ASP.NET MVC 1.0
 
AngularJSの高速化
AngularJSの高速化AngularJSの高速化
AngularJSの高速化
 
React を導入した フロントエンド開発
React を導入したフロントエンド開発React を導入したフロントエンド開発
React を導入した フロントエンド開発
 
React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門React.jsでクライアントサイドなWebアプリ入門
React.jsでクライアントサイドなWebアプリ入門
 
Angular js開発事例
Angular js開発事例Angular js開発事例
Angular js開発事例
 
今からでも遅くない! React事始め
今からでも遅くない! React事始め今からでも遅くない! React事始め
今からでも遅くない! React事始め
 
Enterprise x AngularJS
Enterprise x AngularJSEnterprise x AngularJS
Enterprise x AngularJS
 
Vue入門
Vue入門Vue入門
Vue入門
 
はじめてのVue.js
はじめてのVue.jsはじめてのVue.js
はじめてのVue.js
 
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティスコンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
 
20160927 reactmeetup
20160927 reactmeetup20160927 reactmeetup
20160927 reactmeetup
 
angular1脳で見るangular2
angular1脳で見るangular2angular1脳で見るangular2
angular1脳で見るangular2
 
チュートリアルではじめるVue.js
チュートリアルではじめるVue.jsチュートリアルではじめるVue.js
チュートリアルではじめるVue.js
 
React.js + Flux
React.js + FluxReact.js + Flux
React.js + Flux
 
Brush up your Coding! 2013 winter
Brush up your Coding! 2013 winterBrush up your Coding! 2013 winter
Brush up your Coding! 2013 winter
 

Similar to エンタープライズ分野での実践AngularJS

PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!Shohei Okada
 
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~normalian
 
TypeScript ファースト ステップ (v.0.9 対応版) ~ Any browser. Any host. Any OS. Open Sourc...
TypeScript ファースト ステップ (v.0.9 対応版) ~ Any browser. Any host. Any OS. Open Sourc...TypeScript ファースト ステップ (v.0.9 対応版) ~ Any browser. Any host. Any OS. Open Sourc...
TypeScript ファースト ステップ (v.0.9 対応版) ~ Any browser. Any host. Any OS. Open Sourc...Akira Inoue
 
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~Akira Inoue
 
Progressive Framework Vue.js 2.0
Progressive Framework Vue.js 2.0Progressive Framework Vue.js 2.0
Progressive Framework Vue.js 2.0Toshiro Shimizu
 
13016 n分で作るtype scriptでnodejs
13016 n分で作るtype scriptでnodejs13016 n分で作るtype scriptでnodejs
13016 n分で作るtype scriptでnodejsTakayoshi Tanaka
 
初めての Data API CMS どうでしょう - 仙台編 -
初めての Data API   CMS どうでしょう - 仙台編 -初めての Data API   CMS どうでしょう - 仙台編 -
初めての Data API CMS どうでしょう - 仙台編 -Yuji Takayama
 
初めての Data api cms どうでしょう - 大阪夏の陣
初めての Data api   cms どうでしょう - 大阪夏の陣初めての Data api   cms どうでしょう - 大阪夏の陣
初めての Data api cms どうでしょう - 大阪夏の陣Yuji Takayama
 
ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用Yatabe Terumasa
 
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話Akira Inoue
 
EWD 3トレーニングコース#30 ewd-xpressアプリケーションをモジュラー化する
EWD 3トレーニングコース#30 ewd-xpressアプリケーションをモジュラー化するEWD 3トレーニングコース#30 ewd-xpressアプリケーションをモジュラー化する
EWD 3トレーニングコース#30 ewd-xpressアプリケーションをモジュラー化するKiyoshi Sawada
 
scala+liftで遊ぼう
scala+liftで遊ぼうscala+liftで遊ぼう
scala+liftで遊ぼうyouku
 
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回Naoyuki Yamada
 
20091030cakephphandson 01
20091030cakephphandson 0120091030cakephphandson 01
20091030cakephphandson 01Yusuke Ando
 
10分でわかるFuelPHP @ 2013/04 FuelPHP入門ハンズオン vol.1
 10分でわかるFuelPHP @ 2013/04 FuelPHP入門ハンズオン vol.1 10分でわかるFuelPHP @ 2013/04 FuelPHP入門ハンズオン vol.1
10分でわかるFuelPHP @ 2013/04 FuelPHP入門ハンズオン vol.1kenjis
 
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみたYuki Takei
 
初めてのPadrino
初めてのPadrino初めてのPadrino
初めてのPadrinoTakeshi Yabe
 
CodeIgniter入門
CodeIgniter入門CodeIgniter入門
CodeIgniter入門Sho A
 
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~Akira Inoue
 

Similar to エンタープライズ分野での実践AngularJS (20)

PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!PHP 2大 web フレームワークの徹底比較!
PHP 2大 web フレームワークの徹底比較!
 
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
 
TypeScript ファースト ステップ (v.0.9 対応版) ~ Any browser. Any host. Any OS. Open Sourc...
TypeScript ファースト ステップ (v.0.9 対応版) ~ Any browser. Any host. Any OS. Open Sourc...TypeScript ファースト ステップ (v.0.9 対応版) ~ Any browser. Any host. Any OS. Open Sourc...
TypeScript ファースト ステップ (v.0.9 対応版) ~ Any browser. Any host. Any OS. Open Sourc...
 
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~
 
sveltekit-ja.pdf
sveltekit-ja.pdfsveltekit-ja.pdf
sveltekit-ja.pdf
 
Progressive Framework Vue.js 2.0
Progressive Framework Vue.js 2.0Progressive Framework Vue.js 2.0
Progressive Framework Vue.js 2.0
 
13016 n分で作るtype scriptでnodejs
13016 n分で作るtype scriptでnodejs13016 n分で作るtype scriptでnodejs
13016 n分で作るtype scriptでnodejs
 
初めての Data API CMS どうでしょう - 仙台編 -
初めての Data API   CMS どうでしょう - 仙台編 -初めての Data API   CMS どうでしょう - 仙台編 -
初めての Data API CMS どうでしょう - 仙台編 -
 
初めての Data api cms どうでしょう - 大阪夏の陣
初めての Data api   cms どうでしょう - 大阪夏の陣初めての Data api   cms どうでしょう - 大阪夏の陣
初めての Data api cms どうでしょう - 大阪夏の陣
 
ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用
 
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話
 
EWD 3トレーニングコース#30 ewd-xpressアプリケーションをモジュラー化する
EWD 3トレーニングコース#30 ewd-xpressアプリケーションをモジュラー化するEWD 3トレーニングコース#30 ewd-xpressアプリケーションをモジュラー化する
EWD 3トレーニングコース#30 ewd-xpressアプリケーションをモジュラー化する
 
scala+liftで遊ぼう
scala+liftで遊ぼうscala+liftで遊ぼう
scala+liftで遊ぼう
 
データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回データマイニング+WEB勉強会資料第6回
データマイニング+WEB勉強会資料第6回
 
20091030cakephphandson 01
20091030cakephphandson 0120091030cakephphandson 01
20091030cakephphandson 01
 
10分でわかるFuelPHP @ 2013/04 FuelPHP入門ハンズオン vol.1
 10分でわかるFuelPHP @ 2013/04 FuelPHP入門ハンズオン vol.1 10分でわかるFuelPHP @ 2013/04 FuelPHP入門ハンズオン vol.1
10分でわかるFuelPHP @ 2013/04 FuelPHP入門ハンズオン vol.1
 
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
 
初めてのPadrino
初めてのPadrino初めてのPadrino
初めてのPadrino
 
CodeIgniter入門
CodeIgniter入門CodeIgniter入門
CodeIgniter入門
 
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~
 

Recently uploaded

【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)Hiroki Ichikura
 
SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものですiPride Co., Ltd.
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNetToru Tamaki
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略Ryo Sasaki
 
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Yuma Ohgami
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdftaisei2219
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムsugiuralab
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...Toru Tamaki
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A surveyToru Tamaki
 

Recently uploaded (9)

【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
 
SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものです
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
 
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdf
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システム
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey
 

エンタープライズ分野での実践AngularJS

  • 2. 自己紹介 後藤歩(@balmychan) 株式会社オロ CakePHP, Rails, iOS, AndroidなどのWeb系、アプリ系の受託開発 家族向け写真共有サービスnicori の立ち上げ、開発 クラウド管理会計ZAC Enterprise の開発
  • 3. 目的 Who これから業務システムでAngularJSの導入を考えている方。 What 弊社での開発の構成や流れと、TypeScriptでのソース例、 業務システムであるあるな機能の実装例について、紹介します。 Why AngularJSが業務システムで使われているケースの情報は少ないので、情報交換できれば
  • 6. 開発対象概要 開発対象 ZAC Enterprise(管理会計システム) 開発体制 日本3~4名 海外(ベトナム)1名 ※将来的には、20~30名のエンジニアが開発する想定 技術構成 クライアントサイドAngularJS + TypeScript サーバーサイドASP.NET + ASP.NET Web API 2 + SQL Server
  • 7. What is ZAC? 案件・プロジェクトベースで、売上や原価の管理を行う 統合型基幹業務システム(ERPパッケージです) ・販売管理 ・購買管理 ・在庫管理 ・勤怠管理 ・工数管理 ・経費管理 ・and more... http://www.oro.co.jp/zac/ https://www.oro.co.jp/reforma-psa/ (小規模向けのReformaPSAというサービスも10月からスタートしました) ぜひご検討を!!
  • 8. 導入のきっかけ コード量を減らしたい→ data binding UIを統一したい→ directive クライアント側の開発を、多人数開発で効率よく行いたい→ フルスタック 検討を開始したのは、2013年10月ごろ 利用は、2014年5月ごろから
  • 9. AngularJSの構成 モジュール ルーティング(ui.router)は使用せず、angular-translateと、directiveの共通コンポーネント化、 serviceのビジネスロジックの共通化、実装方式の共通化を図るために導入 ディレクティブ 約50個(テンプレート代わりや、新しいインターフェース) サービス 約40個(サーバーサイドからのデータ取得や共通処理をまとめる) フィルター 約15個
  • 11. フォルダ構成 /b 既存のシステムと共存できる用、フォルダを分けた /Content ASP.NETの構成に準拠 /css /common 共通のcssファイル(sassをここに入れて、VisualStudioで自動コンパイ ル) /directives directive用のcss, sassファイル /Script /vendor AngularJS本体や、各種プラグイン /zac /common 既存のライブラリなど /directives directive本体が入る /services service本体が入る /UI /Views ベースHTMLが入る /directives directive用のHTMLファイル /services /UI UIを伴うservice用のHTMLファイル /project 各機能画面(これは、例として案件管理) /views 案件管理機能のみで使用する、HTMLファイル(専用 directive用も入る) /directives 案件管理機能のみで使用する、directive本体が入る project.ts ルーティング用のファイル project-controller.ts AngularJS用のコントローラー index.cshtml HTMLファイル(一部ASP.NETのテンプレートを使用) /approval ~~~ /project と同じ構成~~~
  • 12. hotfix_YYMMD master D feature_XXX develop release ブランチ timeline
  • 13. ベースHTML 作成 コントローラ ー作成 API作成 (サーバーサイ ド) APIに対応す る サービス 作成 モデルクラス 作る HTMLに肉付 けをしていく HTMLをディ レクティブ化 する 必要ならフィ ルターも作る 完成! AngularJSを中心に据えた場合の、画面開発の流れ ※T4 Templateで、C#側のモ デルを修正すると、 TypeScript側のモデルのコー ドも自動で吐くようになって ます 明らかな場合は、ディレクテ ィブに分けて作り始めます ワイヤー 作成 デザイン 作成 HTML モック作成
  • 15. VisualStudio + TypeScript + AngularJSで幸せになれます!
  • 16. VisualStudio Visual Studio 2013 Express for Webが無料 WebEssentialsを入れると、sassのコンパイルやminifyもラクラク TypeScriptの恩恵を最大限受けられる TypeScript Visual Studio 2013 Update 2からversion 1.0がリリース Express for Webでも使える AngularJS 最新の1.3.1もNuGetですぐ入る
  • 18. TypeScriptについてちょっとだけ TypeScriptはMicrosoft製のaltJSで、ECMAScript 6の提案を多く踏襲し、既存のJavaScriptと混在して使用することが可能です。 JavaScriptにクラス、インターフェース、列挙型、ジェネリクス、ラムダ式、静的型付機能を加えたものです。 VisualStudioを使うと、コード補完や定義元へのジャンプもラクラク。 // クラス定義 class Hoge { public title; public get() { } } TypeScript // 静的型付 TypeScript var a:number; a = "hoge"; // これはエラー a = <any>"hoge"; // いざとなったらanyが使えるが、お行儀は良くない // 列挙型 enum Type { One = 1, Two, }; TypeScript // ラムダ式(thisが維持されるので、クラス定義内で使うと便利) (a: number, b: string) => { } TypeScript
  • 19. 型情報について TypeScriptを使っている場合、型情報を定義したファイルが必要です。(コンパイルエラーになります) 下記から使用したい型情報が書かれたファイルをダウンロードし参照させます。 http://definitelytyped.org/ VisualStudioを使っている場合、NuGetで取得することも可能です。 declare module ng { // not directly implemented, but ensures that constructed class implements $get interface IServiceProviderClass { new (...args: any[]): IServiceProvider; } interface IServiceProviderFactory { (...args: any[]): IServiceProvider; } // All service providers extend this interface interface IServiceProvider { $get: any; } angular.d.ts
  • 21. モジュール var app = angular.module("app", ["ui.router", "ui.bootstrap", "ui.sortable"]); JavaScript app.config(["$stateProvicer", function($stateProvider) { // providerの初期化処理など }); var app = angular.module("app", ["ui.router", "ui.bootstrap", "ui.sortable"]); app.config(["$stateProvider", ($stateProvider: ng.ui.IStateProvider) => { // providerの初期化処理など }); TypeScript まずはモジュールを作成する 特に大きな違いはありませんが、DIするオブジェクトにも型を指定可能
  • 22. コントローラー コントローラーは、直接タグに指定する形と、ルーティングで設定する形(今回は、ui routerを使っています) <body ng-controller="myController"> </body> HTML OR $stateProvider.state("hoge", { url: "/hoge", templateUrl: "index.cshtml", caseInsensitiveMatch: false, controller: "myController" }) JavaScript
  • 23. app.controller("myController", ["$scope", "serviceA", function($scope, serviceA) { $scope.title = "hoge"; }]); JavaScript module Controllers { 'use strict'; export interface IMyControllerScope extends ng.IScope { title: string; } export class MyController { public static $inject = ["$scope", "serviceA"]; constructor($scope: IMyControllerScope , serviceA: Services.ServiceA) { $scope.title = "hoge"; } } angular.module("app").controller("myController", MyController); } TypeScript JavaScriptの場合 TypeScriptの場合
  • 24. ディレクティブ <hoge-directive title="hoge" max-count="5" /> angular.module("app").directive("hogeDirective", ["serviceA", "serviceB", function(serviceA, serviceB) { return { restrict: "EA", require: "?ngModel", scope: { "title": "?=", "max-count": "?=" }, templateUrl: this.serviceB.RootPath + "/HogeDirective.html", link: function(scope, element, attrs, ctrls, transclude) { element.find("span.title").text(scope.title); serviceA.do(function(result) { }); } }; }]); HTML JavaScript titleとmax-countを取るような単純なディレクティブ
  • 25. module Directives { 'use strict'; export interface HogeDirectiveScope { title: string; maxCount: number; } export class HogeDirective { public restrict = "EA"; public require = "?ngModel"; public templateUrl = this.serviceB.RootPath + "/HogeDirective.html"; // templateを指定することもあります public static $inject = ["serviceA", "serviceB"]; // $injectorを使うため、ここでDIするモジュールを定義している constructor(private serviceA: ServiceA, private serviceB: ServiceB) { // コンストラクタにprivateなどのアクセス修飾子をつけると、変数が自動で定義されます } public link = (scope: HogeDirectiveScope, element: JQuery, attrs: ng.IAttributes) { element.find("span.title").text(scope.title); serviceA.do((result) => { }); }; } angular.module("app").directive("hogeDirective", ["$injector", ($injector) => { return $injector.instantiate(HogeDirective); }]); // Directives.addDirective(HogeDirective); // 実際はこういったヘルパーを用意しています } TypeScript
  • 26. サービス angular.module("app").service("zacCookie", function() { return { read: function(name) { return jQuery.cookie(name); }, write: function(name, value, options) { jQuery.cookie(name, value, options); }, remove: function(name, options) { return jQuery.removeCookie(name, options); } } }); JavaScript jQuery cookieをラップするサービス(デフォルトのangularjsのcookieライブラリは、pathが変えられれないため JavaScriptの場合
  • 27. module Services { 'use strict'; export class ZacCookie { public read(name: string): any { return jQuery.cookie(name); } public write(name: string, value: any, options?: any): void { jQuery.cookie(name, value, options); } public remove(name: string, options? : any) : boolean { return jQuery.removeCookie(name, options); } } angular.module('app.services').ser vice("zacCookie", function () { return new ZacCookie(); }); } TypeScript TypeScriptの場合
  • 28. APIと連携するサービス API側からデータを取ってくるサービスを作る場合、$resourceを使っています(内部で$httpを使う形でも良いかと思います) angular.module("app").service("projectService", ["$resource", function($resource) { return $resource('/Project', {}, { 'get': { url:"/Projects/:id', method: 'GET', cache: true }, 'query': { method: 'GET', url: "/Projects" params: { /* デフォルト値があれば設定*/ } cache: true, isArray: true }, 'check': { // Restfulでないメソッドも追加可能 method: 'GET' params: {} url: "/Projects/Check/:id } } }]); JavaScript
  • 29. module Services { 'use strict'; export interface IProjectService extends ng.resource.IResourceClass<Models.ProjectItem> { check(params?: Object, success?: Function, error?: Function): ng.IPromise<Models.ProjectItem>; } export function ProjectService($resource: ng.resource.IResourceService) { return $resource<Models.ProjectItem>("/Project', {}, { 'get': { url:"/Projects/:id', method: 'GET', cache: true }, 'query': { method: 'GET', url: "/Projects" params: { /* デフォルト値があれば設定*/ } cache: true, isArray: true }, 'check': { // Restfulでないメソッドも追加可能 method: 'GET' params: {} url: "/Projects/Check/:id } }]); } ProjectService.$inject = ["$resource"]; angular.module("app").factory("projectService", ProjectService); } TypeScript TypeScriptの場合
  • 30. フィルター 例として、単純に案件番号の先頭に"#"を付けるフィルター <div class="project-number">{{ project.id | projectNumber}}</div> HTML
  • 31. module Filters { 'use strict'; export function ProjectNumber(serviceA: Services.ServiceA) { return function (input) { if (angular.isUndefined(input) || input == null) return ""; return "#" + input; } } angular.module("app").filter("projectNumber", ProjectNumber); } TypeScript angular.module("app").filter("projectNumber", ["serviceA, function(serviceA) { return function(input) { if (angular.isUndefined(input) || input == null) return ""; return "#" + input; } }]); JavaScript JavaScriptの場合 TypeScriptの場合
  • 32. フォーム <form ng-form name="myForm"> <input type="text" name="title" ng-model="title" ng-maxlength="5"> <div ng-if="myForm.title.$error.maxlength">5文字を超えています</div> </form> HTML 既存のフォームの書き方は、特に変わりはないです(そりゃそうだ・・・)
  • 34. app.controller('myController', ['$scope', function($scope) { $scope.name = "taro yamada"; $scope.reset = function() { $scope.name = "taro yamada"; $scope.myForm.$setPristine(); }; }]); app.directive("fullnameInput", [function() { return { restrict: "EA", require: ["?ngModel", "?^form"], scope: {}, replace: true, template: "<div><input type='text' ng-model='firstName'><input type='text' ng-model='lastName'></div>", link: function(scope, element, attrs, ctrls, transclude) { var ngModelCtrl = ctrls[0]; var ngFormCtrl = ctrls[1]; if (ngFormCtrl) { // 標準のinput要素とは別で、状態が変わる操作があれば、明示的にngFormCtrl.$setDirty();を呼ぶ ngFormCtrl.$setDirty(); } JavaScript JavaScriptの場合
  • 35. JavaScript replace: true, template: "<div><input type='text' ng-model='firstName'><input type='text' ng-model='lastName'></div>", link: function(scope, element, attrs, ctrls, transclude) { var ngModelCtrl = ctrls[0]; var ngFormCtrl = ctrls[1]; if (ngFormCtrl) { // 標準のinput要素とは別で、状態が変わる操作があれば、明示的にngFormCtrl.$setDirty();を呼ぶ ngFormCtrl.$setDirty(); } if (ngModelCtrl) { ngModelCtrl.$render = function() { var value = ngModelCtrl.$modelValue || ngModelCtrl.$viewValue; if (angular.isUndefined(value) || value === null) { // クリア処理 scope.firstName = ""; scope.lastName = ""; } else { // 値をelementに設定する処理 var names = value.split(" "); scope.firstName = (0 < names.length) ? names[0] : ""; scope.lastName = (1 < names.length) ? names[1] : ""; } } }
  • 36. JavaScript } function setViewValue() { var name = scope.firstName + (scope.lastName ? " " : "") + scope.lastName; ngModelCtrl.$setViewValue(name); } scope.$watch("firstName", function(newValue) { setViewValue(); }); scope.$watch("lastName", function(newValue) { setViewValue(); }); } }; }]);
  • 37. ngModelControllerが内部に持つ値を見ると、$viewValue, $modelValueという二つの値があります。 この役割を押させておくのが、独自のカスタムインプットを作る上で重要です。$renderもそのうちの一つです。 data bindingで、外部から変更された場合 $modelValue $formatters $viewValue ユーザー操作で、変更された場合 $viewValue $parsers $modelValue $viewValueが変更されると$renderが呼ばれる。そこでelementを更新する $render $viewValueが変更 elementの更新処 理など リセット処理について $setPristineなどの処理は、あくまでもスタイ ルの変更にのみ使用する リセット処理は、ng-modelを通して行う ($modelValueがundefinedやnullの場合、リセ ットとみなし、DOMを更新する)
  • 38. バリデーションhttp://plnkr.co/edit/o4WYlrbiKwH4ql6TNVoB?p=info <form name="myForm"> <input type="text" name="name" custom-maxlength="5" ng-model="name"> <div ng-if="myForm.name.$error.customMaxlength">5文字を超えています</div> </form> HTML ng-maxlengthを独自に実装してみる
  • 39. app.directive("customMaxlength", function() { return { restrict: "A", require: "ngModel", link: function(scope, element, attrs, ngModelCtrl) { var maxlength = attrs['customMaxlength']; if (maxlength) { ngModelCtrl.$validators['customMaxlength'] = function(modelValue, viewValue) { var value = modelValue || viewValue; if (angular.isUndefined(value) || value === null) return true; return value.length <= maxlength; } } } } } }); JavaScript JavaScriptの場合
  • 40. module Directives { 'use strict'; export class CustomMaxlength { public restrict = "A"; public require = "ngModel"; constructor() { } public link = (scope:ng.IScope, element: JQuery, attrs: ng.IAttributes, ngModelCtrl: ng.INgModelController) { var maxlength: number = attrs['customMaxlength']; if (maxlength) { ngModelCtrl.$validators['customMaxlength'] = function(modelValue, viewValue) { var value = modelValue || viewValue; if (angular.isUndefined(value) || value === null) return true; return value.length <= maxlength; } } }; } angular.module("app").directive("customMaxlength", ["$injector", ($injector) => { return $injector.instantiate(CustomMaxlength); }]); } TypeScript TypeScriptの場合
  • 41. 非同期バリデーション <form name="myForm"> <input type="text" name="projectCode" check-project-code ng-model="projectCode"> <div ng-if="myForm.projectCode.$error.checkProjectCode">無効なコードです</div> </form> HTML サーバー側でコードチェックを行いたい、というのはよくあります。 check-project-codeを実装してみる
  • 42. app.directive("checkProjectCode", ["$q", "projectService", function($q, projectService) { return { restrict: "A", require: "ngModel", link: function(scope, element, attrs, ngModelCtrl) { var deferred =$q.defer(); projectService.get(function(project) { // なんらかの追加のチェックをここで行う deferred.resolve(project); }, function(error) { deferred.reject(error); }); return deferred.promise; } } }]); JavaScript JavaScriptの場合 ※非同期バリデーションの場合、promiseを返す。有効な値の場合はresolve、無効な値の場合は、rejectを呼び出す サービスがpromiseを返すようになっている場合($http.getなど)そのまま返しても良い
  • 43. module Directives { 'use strict'; export class CheckProjectCode { public restrict = "A"; public require = "ngModel"; public static $inject = ["$q", "projectService"]; constructor($q: ng.IQService, projectService: Services.Project) { } public link = (scope:ng.IScope, element: JQuery, attrs: ng.IAttributes, ngModelCtrl: ng.INgModelController) { var deferred = this.$q.defer(); this.projectService.get((project) => { deferred.resolve(project); }, (error) => { deferred.reject(error); }); return deferred.promise; }; } angular.module("app").directive("checkProjectCode", ["$injector", ($injector) => { return $injector.instantiate(CheckProjectCode ); }]); } TypeScript TypeScriptの場合
  • 45. レイアウト制御 管理画面でレイアウトを変えれるようにするとか、よくありますよね 権限制御 ユーザーの権限によって項目の表示/非表示を制御 カスタマイズ対応 利用ユーザーごと(個社)ごとに分岐するための心得
  • 46. レイアウト <custom-layout="Layout1"> <div ng-repeat="layout in layouts"> <span>{{ layout.name }}</span> </div> </custom-layout> HTML 単純なレイアウト(ng-repeatで繰り返す) 分岐を伴うレイアウト <custom-layout="Layout2"> <div ng-repeat="layout in layouts"> <div ng-if="layout.type == 'hoge'"> </div> <div ng-if="layout.type == 'fuga'"> </div> <layout-item item="layout" /> <!-- 分岐の中身が複雑な場合、専用のdirectiveを作ります--> </div> </custom-layout> HTML
  • 47. レイアウト module Directives { export interface ICustomLayoutScope extends ng.IScope { layouts:LayoutItem[]; // 別で定義した、モデルクラスデータ(ASP.NET側とDSLを使ってモデルクラスのスキーマは 共有しています) } export class CustomLayout { public static $inject = ["layoutService"]; public restrict = "EA"; public scope = {}; public transclude = true; public template = "<div ng-transclude></div>"; constructor(private layoutService: Services.LayoutService) { } public link = (scope: ng.IScope, element: JQuery, attrs: ng.IAttributes) { var layoutName = attrs["zacLayout"]; if (layoutName) { layoutService.get(layoutName, function(layouts) { scope.layouts = layouts; }); } } } } TypeScript
  • 48. 権限 <div class="a" has-permission="permissionA"><!-- name --></div> <div class="b" has-permission="permissionB"><!-- sales --></div> <div class="c" has-permission="permissionC"><!-- cost --></div> HTML 権限の有無によって、表示/非表示を切り替える 例えば、人によって売上データを見せたくないという場合がある。 API側ではデータを制御し、AngularJS側では表示を制御する
  • 49. module Directives { export interface IHasPermission { show: boolean; }; export class HasPermission { public restrict = "A"; public static $inject = ["permissionService"]; constructor(permissionService: Services.Permission) { } public link = (scope:IHasPermission, element: JQuery, attrs: ng.IAttributes) => { element.hide(); var typePermission= attrs["hasPermission"] || ""; if (typePermission == "") return; this.permissionService.get(typePermission, (hasPermission) => { // サーバーから権限情報を取得 if (hasPermission) { element.show(); // 権限があれば、elementを表示する } }); }; } } TypeScript 単純に、サーバーから権限情報を取得して、表示を制御する
  • 50. <permission-manager> <div class="a" has-permission="permissionA"><!-- name --></div> <div class="b" has-permission="permissionB"><!-- sales --></div> <div class="c" has-permission="permissionC"><!-- cost --></div> </permission-manager> HTML 実際は、個別に取得すると重いので(AngularJSを使ってると、気づくと非同期処理が大量になりがちです) 親ディレクティブを追加して、子ディレクティブと連携し、まとめて取得するようにしています
  • 51. export class HasPermission { // 省略 public require = "?^PermissionManager"; public template = "<div ng-if="show" ng-transclude></div>"; constructor(permissionService: Services.Permission) { } public link = (scope: IHasAuthority, element: JQuery, attrs: ng.IAttributes, ctrl: PermissionManagerCtrl) => { var get = ctrl ? ctrl.get || permissionService.get; // PermissionManagerがいたら、そっちのgetを使う get(typePermission, (hasPermission) => { if (hasPermission) { element.show(); // 権限があれば、elementを表示する } }); }; } TypeScript
  • 52. export class PermissionManagerCtrl { public queue: any = []; public get = (typePermission: string, success: (hasPermission: boolean) => void) => { this.queue.push({ typePermission:typePermission, success: success }); }; public processAllQueues = () => { // 1.サーバーから権限情報(queueの中身を使って)をまとめて取得する(APIを用意してお く) // 2.queue内のsuccessをそれぞれ呼び出す } } TypeScript 子ディレクティブからアクセスするためのコントローラーを用意
  • 53. export class PermissionManager { public restrict = "EA"; public transclude = true; public template = "<div ng-transclude></div>"; public controller = PermissionManagerController; constructor() { } public link = (scope: IHasPermission, element: JQuery, attrs: ng.IAttributes) => { this.controller.processAllQueues(); }; } TypeScript linkではprocessAllQueuesを呼び出す
  • 54. カスタマイズ対応 5つの心得 1.Controllerは肥大化させない(scopeで渡すだけに専念させる) 個社で画面を作り直すときに、ほとんど再利用できなくなります 2.View側も、ディレクティブを使ってパーツ化を進める上と同じ 3.環境ごとの対応は、ControllerとViewを作り直したほうが、コストが低くなる分岐点がある(保守のコストも鑑みて) Controllerが肥大化していなく、Viewがディレクティブ化されていれば、新しい画面を作るのは楽です 4.画面を作り直すほどではない、多少の分岐をどうしても入れたい場合、ディレクティブを分けて分岐する 5.そもそも個社毎にカスタムしないで済むように作る
  • 56. Tips フィルター処理は、サーバーor クライアントサイドどっちでするべきか 「サーバーサイドはAPIのみの実装にしましょう。」という話はよく聞きますが、 フォーマット処理などはどちらにすればいいの?と疑問に思うことがあります。 僕のチームでは、フィルター処理はサーバーサイドで行っています。 多言語対応も考えると、API側で可能な限り言語も言語ごとのフォーマットをしてから、返すべきだからです。 どうもクライアント側に色々処理をもたせようとして、色々フィルターを作ってしまうのですが、 それはクライアント側でするべきか考えましょう!フィルター処理はご存知の通り、重いです。 認証後の認証情報(例えばユーザーIDなど)はどこに持たせておくべきか これは、ZACではheadのmetaで持たせています。(良いやり方とは思っていませんが) API側はOAuth認証等でクライアントと分離して認証できるべきかと思います。 その中で、AngularJS側でセッションを管理するサービスを作り、ユーザー情報を取得するようにすると良い、と思いま す。 アプリ+ APIの構成でアプリを作っていると、自ずとログイン情報を取得するAPIを作り、 アプリ側でログイン情報をキャッシュすると思いますが、考え方はアプリを作成する時と同じです。
  • 57. ・VisualStudio + TypeScript + AngularJSで、Windows畑でも導入可能 フルスタックで型セーフなのは、多人数開発にも有利です ・TypeScriptのサンプルは少ないですが、JSとの互換性が高い AngularJSのモジュールの考え方と方向性があっている ・基幹システム開発になると、権限管理や複雑なインプット 大量データ表示など、通常のWeb開発では当たらない問題が多いが AngularJSを使用すると、スマートに解決できる サービス、ディレクティブ、コントローラー、フィルターをうまく使いこなす まとめ

Editor's Notes

  1. レガシーなコードも混じっている、そういうパッケージも多いのでは。部分的に変えていっております。
  2. ・MSBuildでビルド、NUnitでテスト、IISにアプリを立てて、PhantomJSでJasmineを動かす(クロスブラウザも考えて、Seleniumにする予定)
  3. 既存の機能が数多く有り、それをどのようにAngularJSで表現するかが、一番迷う (さきほどのレイアウトだったり、権限だったり、他にもめちゃくちゃいっぱいあります)
  4. ちょっと開発側の人事も手伝っていることもあり、宣伝させてください。