Alle Lösungen haben dargebracht bisher ein einziges gemeinsames Problem. Die Direktiven sind nicht wiederverwendbar und erfordern Kenntnisse über Variablen, die im übergeordneten Bereich $ vom Controller erstellt wurden. Das heißt, wenn Sie die gleiche Anweisung in einer anderen Ansicht verwenden möchten, müssen Sie alles, was Sie den vorherigen Controller getan haben, erneut implementieren und sicherstellen, dass Sie die gleichen Variablennamen für Dinge verwenden, da die Direktiven grundsätzlich fest codierte $ Scope-Variablennamen haben in ihnen. Sie wären definitiv nicht in der Lage, dieselbe Anweisung zweimal innerhalb des gleichen übergeordneten Bereichs zu verwenden.
Die Art und Weise, um dies ist isoliert Anwendungsbereich der Richtlinie zu verwenden. Auf diese Weise können Sie die Richtlinie machen wiederverwendbar, unabhängig von der Mutter $ Umfang von allgemein Parametrier- Elementen aus dem übergeordneten Bereich erforderlich.
In meiner Lösung der einzige Sache, dass der Controller eine selectedIndex Variable tun muss, ist, dass die Richtlinie, welche Zeile in der Tabelle verfolgen verwendet, wird zur Zeit ausgewählt. Ich hätte die Verantwortlichkeit dieser Variable für die Direktive isolieren können, aber indem ich den Controller dazu veranlasse, die Variable zur Verfügung zu stellen, die es erlaubt, die aktuell ausgewählte Zeile in der Tabelle außerhalb der Direktive zu manipulieren. Zum Beispiel könnten Sie „auf Klick wählen Sie Reihe“ in Ihrem Controller implementieren, während noch die Pfeiltasten für die Navigation in der Direktive.
Die Richtlinie:
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);
}
});
}
}
}
Diese Richtlinie ermöglicht Ihnen nicht nur eine Tabelle navigieren mit den Pfeiltasten, aber es erlaubt Ihnen, eine Callback-Methode auf die Enter-Taste zu binden. So dass, wenn die Enter-Taste gedrückt wird, um die Zeile, die derzeit als ein Argument an der Callback-Methode mit der Richtlinie (onEnter) registriert aufgenommen wird ausgewählt.
Als kleines Plus können Sie auch eine CSS-Klasse und -Dauer an die cdArrowRow-Direktive übergeben, so dass die übergebene CSS-Klasse dann auf das Zeilenelement angewendet wird, wenn die Eingabetaste auf einer ausgewählten Zeile gedrückt wird nach Ablauf der Dauer (in Millisekunden) entfernt. Dies ermöglicht es Ihnen, etwas zu tun, wenn die Zeile beim Drücken der Eingabetaste eine andere Farbe erhält.
Ansicht Verbrauch:
<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>
Controller:
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
}
}
Es ist ein netter Ansatz, aber es funktioniert nicht, wenn die Einträge geordnet sind ('record in records | orderBy: '-name''). Haben Sie auch dafür eine Lösung? (nicht nur für diesen Fall, sondern ein generischer) – akirk
Vielen Dank für das Feedback. Es macht immer Spaß und Druck, mit schwierigeren Anwendungsfällen herausgefordert zu werden. Ich füge den zusätzlichen Code hinzu, der sortierte/gefilterte Liste unterstützt. – Tosh
Danke! Ihre Lösung war sehr inspirierend. – akirk