これまでに提供されたすべてのソリューションには、共通の問題が 1 つあります。ディレクティブは再利用できません。コントローラーによって提供される親 $scope で作成された変数の知識が必要です。つまり、別のビューで同じディレクティブを使用したい場合は、以前のコントローラーで行ったすべてを再実装し、物事に同じ変数名を使用していることを確認する必要があります。ディレクティブは基本的に $scope 変数名をハードコーディングしているためです。それらの中で。同じ親スコープ内で同じディレクティブを 2 回使用することは絶対にできません。
これを回避するには、ディレクティブで分離スコープを使用します。これにより、親スコープから必要なアイテムを一般的にパラメーター化することで、親の $scope に関係なく、ディレクティブを再利用可能にすることができます。
私のソリューションでは、コントローラーが行う必要がある唯一のことは、テーブル内のどの行が現在選択されているかを追跡するためにディレクティブが使用する selectedIndex 変数を提供することです。この変数の責任をディレクティブに分離することもできましたが、コントローラーに変数を提供させることで、テーブルで現在選択されている行をディレクティブの外で操作できるようになります。たとえば、ディレクティブでのナビゲーションに矢印キーを使用しながら、コントローラに「クリックで行を選択」を実装できます。
指令:
angular
.module('myApp')
.directive('cdArrowTable', cdArrowTable);
.directive('cdArrowRow', cdArrowRow);
function cdArrowTable() {
return {
restrict:'A',
scope: {
collection: '=cdArrowTable',
selectedIndex: '=selectedIndex',
onEnter: '&onEnter'
},
link: function(scope, element, attrs, ctrl) {
// Ensure the selectedIndex doesn't fall outside the collection
scope.$watch('collection.length', function(newValue, oldValue) {
if (scope.selectedIndex > newValue - 1) {
scope.selectedIndex = newValue - 1;
} else if (oldValue <= 0) {
scope.selectedIndex = 0;
}
});
element.bind('keydown', function(e) {
if (e.keyCode == 38) { // Up Arrow
if (scope.selectedIndex == 0) {
return;
}
scope.selectedIndex--;
e.preventDefault();
} else if (e.keyCode == 40) { // Down Arrow
if (scope.selectedIndex == scope.collection.length - 1) {
return;
}
scope.selectedIndex++;
e.preventDefault();
} else if (e.keyCode == 13) { // Enter
if (scope.selectedIndex >= 0) {
scope.collection[scope.selectedIndex].wasHit = true;
scope.onEnter({row: scope.collection[scope.selectedIndex]});
}
e.preventDefault();
}
scope.$apply();
});
}
};
}
function cdArrowRow($timeout) {
return {
restrict: 'A',
scope: {
row: '=cdArrowRow',
selectedIndex: '=selectedIndex',
rowIndex: '=rowIndex',
selectedClass: '=selectedClass',
enterClass: '=enterClass',
enterDuration: '=enterDuration' // milliseconds
},
link: function(scope, element, attrs, ctr) {
// Apply provided CSS class to row for provided duration
scope.$watch('row.wasHit', function(newValue) {
if (newValue === true) {
element.addClass(scope.enterClass);
$timeout(function() { scope.row.wasHit = false;}, scope.enterDuration);
} else {
element.removeClass(scope.enterClass);
}
});
// Apply/remove provided CSS class to the row if it is the selected row.
scope.$watch('selectedIndex', function(newValue, oldValue) {
if (newValue === scope.rowIndex) {
element.addClass(scope.selectedClass);
} else if (oldValue === scope.rowIndex) {
element.removeClass(scope.selectedClass);
}
});
// Handles applying/removing selected CSS class when the collection data is filtered.
scope.$watch('rowIndex', function(newValue, oldValue) {
if (newValue === scope.selectedIndex) {
element.addClass(scope.selectedClass);
} else if (oldValue === scope.selectedIndex) {
element.removeClass(scope.selectedClass);
}
});
}
}
}
このディレクティブは、矢印キーを使用してテーブルをナビゲートできるだけでなく、コールバック メソッドを Enter キーにバインドすることもできます。そのため、Enter キーが押されると、現在選択されている行が、ディレクティブ (onEnter) に登録されたコールバック メソッドへの引数として含まれます。
ちょっとしたおまけとして、CSS クラスと期間を cdArrowRow ディレクティブに渡すこともできます。これにより、選択した行で Enter キーが押されたときに、渡された CSS クラスが行要素に適用され、渡された後に削除されます。期間 (ミリ秒単位)。これにより、基本的に、Enter キーが押されたときに行を別の色で点滅させるようなことができます。
使用状況を表示:
<table cd-arrow-table="displayedCollection"
selected-index="selectedIndex"
on-enter="addToDB(row)">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in displayedCollection"
cd-arrow-row="row"
selected-index="selectedIndex"
row-index="$index"
selected-class="'mySelcetedClass'"
enter-class="'myEnterClass'"
enter-duration="150"
>
<td>{{row.firstName}}</td>
<td>{{row.lastName}}</td>
</tr>
</tbody>
</table>
コントローラ:
angular
.module('myApp')
.controller('MyController', myController);
function myController($scope) {
$scope.selectedIndex = 0;
$scope.displayedCollection = [
{firstName:"John", lastName: "Smith"},
{firstName:"Jane", lastName: "Doe"}
];
$scope.addToDB;
function addToDB(item) {
// Do stuff with the row data
}
}