<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js@1.2.17" data-semver="1.2.17" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.17/angular.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body ng-app="appName" ng-controller="TestCtrl">
<p><button ng-click="addRow()">add row</button>
<button ng-click="updateTableValue()">show table data</button></p>
<table border="1">
<thead>
<tr>
<th>checked</th>
<th>row index</th>
<th class="editableRow">first name</th>
<th class="editableRow">last name</th>
<th>delete row</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="item in items">
<td><input type="checkbox" ng-model="item.checked"></td><!-- チェックボックス -->
<td>{{$index}}</td><!-- 行番号 -->
<td cm-editable-text ng-model="item.firstName"></td><!-- 名前 -->
<td cm-editable-text ng-model="item.lastName"></td><!-- 苗字 -->
<td><button ng-click="removeRow($index)">delete</button></td><!-- 行削除ボタン -->
</tr>
</tbody>
</table>
<textarea>{{tableValue}}</textarea>
</body>
</html>
// Code goes here
(function () {
'use strict';
angular.module('appName', function(){});
angular.module('appName').controller('TestCtrl', function ($scope) {
var exportTableValue = function() {
var str = '';
angular.forEach($scope.items, function(item) {
str += '[' + new Date().getTime() + '] : ' + item.checked + ' : ' + item.firstName + ' ' + item.lastName + '\n';
});
$scope.tableValue = str;
};
$scope.addRow = function() {
$scope.items.push( { checked:false, firstName:'firstName', lastName:'lastName' } );
};
$scope.removeRow = function(rowIndex) {
$scope.items.splice(rowIndex, 1);
};
$scope.updateTableValue = function() {
exportTableValue();
};
$scope.items = [
{ checked: false, firstName: 'Taiga', lastName: 'Hirohata' },
{ checked: false, firstName: 'Eichi', lastName: 'Arikawa' },
{ checked: false, firstName: 'Masayuki', lastName: 'Tago' }
];
var collectionWatcher = $scope.$watchCollection('items', function(newCollection, oldCollection, scope) {
exportTableValue();
});
$scope.$on('$destroy', function() {
if(!angular.equals(collectionWatcher, null)) {
collectionWatcher(); //モデル監視リスナーの解放
}
});
});
angular.module('appName').directive('cmEditableText', function () {
return {
restrict : 'A',
require : '^ngModel',
link : function(scope, element, attrs, ngModel) {
ngModel.$render = function() {
element.html(ngModel.$viewValue);
};
element.on('dblclick', function() {
var clickTarget = angular.element(this);
var EDITING_PROP = 'editing';
if ( !clickTarget.hasClass(EDITING_PROP) ) {
clickTarget.addClass(EDITING_PROP);
clickTarget.html('<input type="text" value="' + ngModel.$viewValue + '" />');
var inputElement = clickTarget.children();
inputElement.on('focus', function() {
inputElement.on('blur', function() {
var inputValue = inputElement.val() || this.defaultValue;
clickTarget.removeClass(EDITING_PROP).text(inputValue);
inputElement.off();
scope.$apply(function() {
ngModel.$setViewValue(inputValue);
});
});
});
inputElement[0].focus();
}
});
var destroyWatcher = scope.$on('$destroy', function () {
if ( angular.equals(destroyWatcher, null) ) {
return;
}
element.off();
destroyWatcher();
destroyWatcher = null;
});
}
};
});
}());
/* Styles go here */
th,td {
width: 100px;
}
input {
width: 97px;
}
textarea {
width: 500px;
height: 300px;
}
.editableRow {
color: red;
font-weight: bold;
}