I have table
with ng-repeat
for table rows. I want to make inline editing and validation of data from single row, but it is impossible to use for
Approaches for this problem (incremental):
(optional) Don't use a table. use nested lists and force it with css to look like a table.
Do it in the Angularish way: use a custom directive
on the table cells/rows (whatever) the directive
should watch
the cell content for changes and then run a any custom validation and logic you have.
I don't think you need to hide your inputs in tds, you could just use CSS to make them fill the td, with no border for example.
As @alonisser said, the angular-way is to create a directive to handle that. To solve my similar case, I created a 'super' table directive which provide my table template and handle its behavior. About the template: I wrapped the table into a form ;)
HTML example:
<form name="tableform">
<table>
<tbody>
<tr class="tr-rawdata" ng-repeat="row in tableData track by $index" ng-model="row">
<td ng-repeat="cell in row track by $index">
<input type="text" ng-model="row[$index]">
</td>
</tr>
</tbody>
</table>
</form>
directive example:
angular.module('myApp').directive('superTable', function() {
return {
restrict: 'A',
templateUrl: 'partials/super-table.html',
link: function($scope, $elem, $attrs) {
$elem.on('blur', function(e) {
//do something
});
}
}
});
I create example with simple validation to your second question:
View:
<div ng-controller="MyCtrl">
<table class="table table-condensed">
<thead>
<tr>
<th>Id</th>
<th>Firstname</th>
<th>Lastname</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items">
<td>{{item.id}}</td>
<td><input ng-model="item.firstName" ng-class="{ error: !item.firstName }"/></td>
<td><input ng-model="item.lastName" ng-class="{ error: !item.lastName }"/></td>
<td><input ng-model="item.email" ng-pattern="emailRegExp" ng-class="{ error: !item.email }"/></td>
<td><button ng-disabled="!item.lastName || !item.firstName || !item.email"/>Submit</td>
</tr>
</tbody>
</table>
</div>
Controller:
function MyCtrl($scope) {
$scope.items = [
{
id: 1,
firstName: 'Ivan',
lastName: 'Ivanov',
email: 'email@email.com'
},
{
id: 2,
firstName: 'Petr',
lastName: 'Petrov',
email: 'email@email.com'
}
];
$scope.emailRegExp = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;
}
Please, see jsfiddle :)
Jsfiddle
For your question in comment about date validation:
I see two ways for do it:
1) In yor controller you create $scope.dateRegExp = "/^\d{2}([./-])\d{2}\1\d{4}$/"
and into the view you using it with ng-pattern="dateRegExp"
2) You can use ng-change=""
directive:
View:
<tr ng-repeat="item in items">
<td><input ng-model="item.date" ng-change="validateDate(itemDate)" ng-class="{ error: dateInputError }"/></td>
Controller:
$scope.dateInputError = false;
$scope.validateDate = function(date) {
if(//some validation){
$scope.dateInputError = true; //true - it means error style shows
}
};
NG-Form works on elements that are not a HTML form. So, you should be able to use the built ng-form validations inside a table. It seems to track the forms properly per row for me.
https://docs.angularjs.org/api/ng/directive/form
<tr ng-repeat="market in markets | orderBy:'name'" ng-form name="myForm">
<td>{{market.id}}</td>
<td ng-class="{'has-error': !myForm.minimum.$valid}">
<input type="number" name="minimum" min="0" max="10000" ng-model="market.minimum" />
</td>
<td ng-class="{'has-error': !myForm.cash.$valid}">
<input type="number" ng-model="market.cash" min="0" name="cash" />
</td>
<td>
<input type="submit" ng-disabled="!myForm.$valid" ng-click="save(market)"/>
</td>
</tr>