Reputation: 7729
I'm trying to port a special table/grid form made in Windows (Delphi VCL) to an Angular app.
A simplified version of the angular app can be tested here: jsFiddle demo.
User can add as many rows as he wants, as demonstrated in the jsFiddle.
The question is: Is it possible to solve the following with an Angular directive or some other Angular magic?
HTML:
<tbody>
<tr ng-repeat="p in persons">
<td>
<input ng-model="p.name">
</td>
<td>
<input ng-model="p.age">
</td>
<td>
<button ng-click="add($index)">Add new person</button>
</td>
</tr>
</tbody>
JS:
function TestingCtrl($scope) {
$scope.persons = [{
name: 'Alice',
age: 20
}, {
name: 'Bob',
age: 30
}];
$scope.add = function(index) {
var newPerson = function() {
return {
name: '',
age: ''
};
};
$scope.persons.splice(index + 1, 0, new newPerson());
}
}
Upvotes: 1
Views: 8778
Reputation: 7729
I figured this out. This Plunk will navigate on enter key AND all arrow keys (up, down, left, right). Thanks @Armen for pointing me in the right direction.
<div ng-app="myApp">
<div ng-controller="TestingCtrl">
<table navigatable>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="p in persons">
<td>
<input type="text" ng-model="p.name">
</td>
<td>
<input type="text" ng-model="p.age">
</td>
<td>
<button ng-click="add($index)">Add new person</button>
</td>
</tr>
</tbody>
</table>
</div>
angular.module("myApp", [])
.controller("TestingCtrl", ["$scope",
function TestingCtrl($scope) {
$scope.persons = [{
name: 'Alice',
age: 20
}, {
name: 'Bob',
age: 30
}];
$scope.add = function(index) {
var newPerson = function() {
return {
name: '',
age: ''
};
};
$scope.persons.splice(index + 1, 0, new newPerson());
}
}
])
.directive('navigatable', function() {
return function(scope, element, attr) {
element.on('keypress.mynavigation', 'input[type="text"]', handleNavigation);
function handleNavigation(e) {
var arrow = {left: 37, up: 38, right: 39, down: 40};
// select all on focus
element.find('input').keydown(function(e) {
// shortcut for key other than arrow keys
if ($.inArray(e.which, [arrow.left, arrow.up, arrow.right, arrow.down]) < 0) {
return;
}
var input = e.target;
var td = $(e.target).closest('td');
var moveTo = null;
switch (e.which) {
case arrow.left:
{
if (input.selectionStart == 0) {
moveTo = td.prev('td:has(input,textarea)');
}
break;
}
case arrow.right:
{
if (input.selectionEnd == input.value.length) {
moveTo = td.next('td:has(input,textarea)');
}
break;
}
case arrow.up:
case arrow.down:
{
var tr = td.closest('tr');
var pos = td[0].cellIndex;
var moveToRow = null;
if (e.which == arrow.down) {
moveToRow = tr.next('tr');
}
else if (e.which == arrow.up) {
moveToRow = tr.prev('tr');
}
if (moveToRow.length) {
moveTo = $(moveToRow[0].cells[pos]);
}
break;
}
}
if (moveTo && moveTo.length) {
e.preventDefault();
moveTo.find('input,textarea').each(function(i, input) {
input.focus();
input.select();
});
}
});
var key = e.keyCode ? e.keyCode : e.which;
if (key === 13) {
var focusedElement = $(e.target);
var nextElement = focusedElement.parent().next();
if (nextElement.find('input').length > 0) {
nextElement.find('input').focus();
} else {
nextElement = nextElement.parent().next().find('input').first();
nextElement.focus();
}
}
}
};
})
I have done som copy/paste from different sources. Needs refactoring.
Upvotes: 7
Reputation: 287
Have a look at this plunk. Pay attention on attribute 'navigatable' added on table. This is not a complete solution of your problem (added only the "enter" button behavior), but just an idea how you can elegantly add navigation functionality to your table inputs. Please note that here is loaded jquery! checkout this doc to better understand jquery usage in angular. Citation from documentation:
jqLite is a tiny, API-compatible subset of jQuery that allows Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most commonly needed functionality with the goal of having a very small footprint.
Upvotes: 2