TypeScriptで書くAngularJSフィールドのキー操作処理
(2014/10/25 追記)この記事は古いです。キー操作などのロジックは Directive 毎にまとめるべきで、以下はノウハウが少ない頃の内容です。記録として残します。
--
前回(TypeScript で書く AngularJS の MVC)の記事がご好評頂けたようで、ありがとうございました。今回も TypeScript+AngularJS ネタです。
input フィールドの値をキー入力で処理する
最近のブラウザでは<input type="number">
のときにインクリメントボタン(上下三角)が表示されフォーカスが当たっていると上下キーで数値の増減が出来たりしますが、この実装をどう独自に書けるか考えました。
AngularUI の ui.utilにはキーイベントを捌くモジュールが含まれているのでこれを使えば済むと思いきや、上下キーについては反応が無いようでこの案は失敗。そこで TypeScript で一から書くことにしました。
動作する例
JavaScript ですが動作する例を書きました。フィールド内で上下キーを押してみてください。Value フィールドは Shift キーを押しながらで別の動作をします。(jsfiddle)
Module の実装
この例ではプレフィクスをmy
としています。(ここの 1 文字目を大文字にすると読み込まず、最初けっこうハマりました。)
/// <reference path="../vendor/angular.d.ts" />
angular.module("my.keyFocusHelper", []).directive("myKeyFocus", () => {
return {
restrict: "A",
link: (scope, element, attr) => {
var thisScope = scope;
element.bind("keyup", (event) => {
scope.$parent[attr.myKeyFocus](event, thisScope);
});
},
};
});
##app.ts の例
用意したモジュールの使用を宣言します。
/// <reference path="/vendor/angular.d.ts" />
interface MyApp extends ng.IModule {}
var app: MyApp = angular.module("myApplication", [
"ngResource",
"my.keyFocusHelper",
]);
##Controller での書き方
前回の記事と同様の Controller を例に使っています。詳しくは前回の記事を参照してください。
/// <reference path="../vendor/angular.d.ts" />
/// <reference path="../app.ts" />
app.controller("IndexCtrl", ($scope) => {
return new IndexCtrl($scope);
});
interface MainScope extends ng.IScope {
entities: string[];
keyFocusColor: Function;
keyFocusValue: Function;
}
class IndexCtrl {
private scope: MainScope;
constructor($scope: MainScope) {
this.scope = $scope;
$scope.keyFocusColor = angular.bind(this, this.keyFocusColor);
$scope.keyFocusValue = angular.bind(this, this.keyFocusValue);
}
public keyFocusColor(event: MouseEvent, myScope): void {
// up arrow
if (event.keyCode == 38) {
myScope.e.color += 1;
$scope.$apply(); // 書き忘れると操作しても表示が更新されない
}
// down arrow
else if (event.keyCode == 40) {
myScope.e.color -= 1;
$scope.$apply();
}
}
public keyFocusValue(event: MouseEvent, myScope): void {
// up arrow with shift
if (event.keyCode == 38 && event.shiftKey) {
myScope.e.value += 10;
$scope.$apply();
}
// down arrow with shift
else if (event.keyCode == 40 && event.shiftKey) {
myScope.e.value -= 10;
$scope.$apply();
}
// up arrow
else if (event.keyCode == 38 && !event.shiftKey) {
myScope.e.value += 1;
$scope.$apply();
}
// down arrow
else if (event.keyCode == 40 && !event.shiftKey) {
myScope.e.value -= 1;
$scope.$apply();
}
}
}
View での使い方
my-key-focus
という属性が今回のポイントです。
<!--略-->
<div ng-controller="IndexCtrl">
<table class="records">
<thead>
<tr class="table-header">
<th class="head-color">COLOR</th>
<th class="head-value">VALUE</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="e in entities">
<td class="cell-color" ng-class="'color-'+[e.color]">
<input ng-model="e.color" my-key-focus="keyFocusColor" />
</td>
<td class="cell-value">
<input ng-model="e.value" my-key-focus="keyFocusValue" />
</td>
</tr>
</tbody>
</table>
</div>
<!--略-->
解説
キー入力イベントを受け取って処理するメソッドは Controller 内にも書けますが、複数の Controller で用いる場合は Module としてまとめたほうがいいでしょう。
冒頭でmy.keyFocusHelper
Module を作成しましたが、ここでは directivemyKeyFocus
を宣言しています。これで View の HTML 内でmy-key-focus
が設定された要素に対して処理が行えます。(directive が何かについては割愛します)
my-key-focus="someValue"
の値は、directive 内ではattr
の引数に渡されます。ここにキー入力によって実行したいメソッド名を()抜きで表記します。(例:my-key-focus="keyFocusColor"
)
Controller で実装したkeyFocusColor()
は directive のscope.$parent[attr.myKeyFocus]
にて呼び出せます。これでキー入力を受け取る実装とキー入力後の処理を分けることができます。あとは Controller 内にメソッドを実装して$scope.func = angular.bind(this, this.func);
の形式でバインドすれば完成。
以上です。TypeScript や Module を駆使してスマートに書きましょう!
注意点
directive でのscope.$parent[...
の部分はng-repeat
の外だとscope[...
と書く必要があります。まだ抽象化が足りず、今後の課題です。
おまけ
今回の件とは関係ないですが、ng-class="'color-'+[e.color]"
とするとクラス名を動的に設定できます。文字列連結には+を使うようです。ng-class
に慣れると UI 実装が簡単になり面白いです。
Discussion