Início
Conheça mais
Enviar pesquisa
Carregar
Entrar
Cadastre-se
Anúncio
Check these out next
Angular js はまりどころ
Ayumi Goto
3分でわかるangular js
Shin Adachi
AngularJSでの非同期処理の話
Yosuke Onoue
jQuery/Html5/ASP.NET MVC 対応コンポーネントを用いたデバイス対応業務アプリケーション開発
Daizen Ikehara
ASP.NET MVC と jQuery で実践する標準志向 Web 開発
Akira Inoue
ASP.NET MVC 1.0
Shinpei Ohtani
AngularJSの高速化
Kon Yuichi
React を導入したフロントエンド開発
daisuke-a-matsui
1
de
60
Top clipped slide
エンタープライズ分野での実践AngularJS
5 de Nov de 2014
•
0 gostou
17 gostaram
×
Seja o primeiro a gostar disto
mostrar mais
•
9,745 visualizações
visualizações
×
Vistos totais
0
No Slideshare
0
De incorporações
0
Número de incorporações
0
Baixar agora
Baixar para ler offline
Denunciar
Tecnologia
Ayumi Goto
Seguir
Product Manager / Lead Engineer em オロ (oRo co.,ltd.)
Anúncio
Anúncio
Anúncio
Recomendados
AngularJS入門
Kenji Shirane
5.3K visualizações
•
58 slides
そろそろ押さえておきたい AngularJSのセキュリティ
Muneaki Nishimura
25.7K visualizações
•
34 slides
AngularJSを浅めに紹介します
nkazuki
5K visualizações
•
13 slides
受託開発でのAngularJS - 第1回AngularJS 勉強会 at LIG
Hayashi Yuichi
24K visualizações
•
62 slides
Angular.jsについてちょっとしゃべる
Masashi Haga
7.7K visualizações
•
15 slides
AngularJSについて
昌生 高橋
3.7K visualizações
•
24 slides
Mais conteúdo relacionado
Apresentações para você
(20)
Angular js はまりどころ
Ayumi Goto
•
24.1K visualizações
3分でわかるangular js
Shin Adachi
•
2.2K visualizações
AngularJSでの非同期処理の話
Yosuke Onoue
•
10.9K visualizações
jQuery/Html5/ASP.NET MVC 対応コンポーネントを用いたデバイス対応業務アプリケーション開発
Daizen Ikehara
•
2.2K visualizações
ASP.NET MVC と jQuery で実践する標準志向 Web 開発
Akira Inoue
•
17.7K visualizações
ASP.NET MVC 1.0
Shinpei Ohtani
•
19K visualizações
AngularJSの高速化
Kon Yuichi
•
8.6K visualizações
React を導入したフロントエンド開発
daisuke-a-matsui
•
60.4K visualizações
React.jsでクライアントサイドなWebアプリ入門
spring_raining
•
16.6K visualizações
Angular js開発事例
Shun Takeyama
•
3.7K visualizações
今からでも遅くない! React事始め
ynaruta
•
114.3K visualizações
Enterprise x AngularJS
Kenichi Kanai
•
15.6K visualizações
Vue入門
Takeo Noda
•
5.7K visualizações
はじめてのVue.js
kamiyam .
•
5.1K visualizações
コンポーネント指向による、Reactのベストプラクティスとバッドプラクティス
Kohei Asai
•
75.4K visualizações
20160927 reactmeetup
Naoki Kurosawa
•
4K visualizações
angular1脳で見るangular2
Moriyuki Arakawa
•
29.9K visualizações
チュートリアルではじめるVue.js
小川 昌吾
•
11.9K visualizações
React.js + Flux
dsuke Takaoka
•
18.3K visualizações
Brush up your Coding! 2013 winter
Shogo Sensui
•
6.2K visualizações
Similar a エンタープライズ分野での実践AngularJS
(20)
PHP 2大 web フレームワークの徹底比較!
Shohei Okada
•
13.4K visualizações
基礎から見直す ASP.NET MVC の単体テスト自動化方法 ~ Windows Azure 関連もあるかも~
normalian
•
10.3K visualizações
TypeScript ファースト ステップ (v.0.9 対応版) ~ Any browser. Any host. Any OS. Open Sourc...
Akira Inoue
•
3.6K visualizações
Visual Studio 2012 Web 開発 ~ One ASP.NET から TypeScript まで ~
Akira Inoue
•
3.2K visualizações
sveltekit-ja.pdf
ssuser65180a
•
254 visualizações
Progressive Framework Vue.js 2.0
Toshiro Shimizu
•
2.2K visualizações
13016 n分で作るtype scriptでnodejs
Takayoshi Tanaka
•
2.3K visualizações
初めての Data API CMS どうでしょう - 仙台編 -
Yuji Takayama
•
4.8K visualizações
初めての Data api cms どうでしょう - 大阪夏の陣
Yuji Takayama
•
1.5K visualizações
ソーシャルアプリ勉強会(第一回資料)配布用
Yatabe Terumasa
•
1.5K visualizações
Featuring Project Silk & Liike: 楽しい "モダン" Web 開発のちょっとディープなお話
Akira Inoue
•
2K visualizações
EWD 3トレーニングコース#30 ewd-xpressアプリケーションをモジュラー化する
Kiyoshi Sawada
•
299 visualizações
scala+liftで遊ぼう
youku
•
2K visualizações
データマイニング+WEB勉強会資料第6回
Naoyuki Yamada
•
3.6K visualizações
20091030cakephphandson 01
Yusuke Ando
•
1.3K visualizações
10分でわかるFuelPHP @ 2013/04 FuelPHP入門ハンズオン vol.1
kenjis
•
26.5K visualizações
自作node.jsフレームワークとnginxを使ってラジオサイトを作ってみた
Yuki Takei
•
21.2K visualizações
初めてのPadrino
Takeshi Yabe
•
7.3K visualizações
CodeIgniter入門
Sho A
•
13.1K visualizações
TypeScript ファーストステップ ~ Any browser. Any host. Any OS. Open Source. ~
Akira Inoue
•
2K visualizações
Anúncio
Último
(20)
SoftwareControl.pdf
ssusercd9928
•
15 visualizações
触感に関わる共感覚的表現と基本6感情の対応関係の検証
Matsushita Laboratory
•
22 visualizações
【DL輪読会】TrOCR: Transformer-based Optical Character Recognition with Pre-traine...
Deep Learning JP
•
7 visualizações
20230602_enebular_meetup_kitazaki_v1.pdf
Ayachika Kitazaki
•
40 visualizações
ChatGPT触ってみた
infinite_loop
•
64 visualizações
【DL輪読会】DINOv2: Learning Robust Visual Features without Supervision
Deep Learning JP
•
86 visualizações
統計学の攻略_正規分布ファミリーの全体像.pdf
akipii Oga
•
304 visualizações
Wandb LLM Webinar May 30 2023 (配布用).pdf
Yuya Yamamoto
•
140 visualizações
【DL輪読会】HyperDiffusion: Generating Implicit Neural Fields withWeight-Space Dif...
Deep Learning JP
•
6 visualizações
コードレビュー改善のためにJenkinsとIntelliJ IDEAのプラグインを自作してみた話
Rakuten Group, Inc.
•
40 visualizações
Voyager: An Open-Ended Embodied Agent with Large Language Models
harmonylab
•
23 visualizações
初学者のためのプロンプトエンジニアリング実践.pptx
Akifumi Niida
•
616 visualizações
モバイル・クラウド・コンピューティング-データを如何に格納し、組み合わせ、情報として引き出すか
Masahiko Funaki
•
2 visualizações
ChatGPT + LlamaIndex 0 .6 による チャットボット の実装
Takanari Tokuwa
•
73 visualizações
【DL輪読会】大量API・ツールの扱いに特化したLLM
Deep Learning JP
•
162 visualizações
社内ソフトスキルを考える
infinite_loop
•
91 visualizações
HTTPの仕組みについて
iPride Co., Ltd.
•
11 visualizações
GraalVMでのFlight Recorderを使ったパフォーマンス解析(JJUG CCC 2023 Spring)
NTT DATA Technology & Innovation
•
9 visualizações
JSTQB_テストマネジメントとレビュープロセス.pdf
akipii Oga
•
266 visualizações
生成AIのビルド方法 (ChatGPT)
Citynow Asia Inc
•
0 visão
エンタープライズ分野での実践AngularJS
エンタープライズ分野での実践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) エンジニア仲間募集中です
興味ありましたらお声がけください!!
ありがとうございました。
Notas do Editor
レガシーなコードも混じっている、そういうパッケージも多いのでは。部分的に変えていっております。
・MSBuildでビルド、NUnitでテスト、IISにアプリを立てて、PhantomJSでJasmineを動かす(クロスブラウザも考えて、Seleniumにする予定)
既存の機能が数多く有り、それをどのようにAngularJSで表現するかが、一番迷う (さきほどのレイアウトだったり、権限だったり、他にもめちゃくちゃいっぱいあります)
ちょっと開発側の人事も手伝っていることもあり、宣伝させてください。
Anúncio