AngularJS: Editable Grid

An an example of an excel type editable grid using AngularJS for easy Model <=> View binding

HTML

<script src="http://code.angularjs.org/angular-1.0.1.js"></script>
<script src="https://raw.github.com/coreyti/showdown/master/compressed/showdown.js"></script>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<div ng-controller="demo">
  <div class="w3-container">
    <h4>AngularJS: Editable Grid</h4>
  </div>
  <div class="w3-container">
      <table class="w3-table-all w3-hoverable">
        <tr class="w3-blue">
          <td>First Name</td>
          <td>Last Name</td>
        </tr>
        <tr ng-repeat="row in rows">
          <td valign="right">
            <div contentEditable="true" ng-model="row.firstName" ng-click="elementClicked($index)"></div>
          </td>
          <td valign="right">
            <div contentEditable="true" ng-model="row.lastName" ng-click="elementClicked($index)"></div>
          </td>
        </tr>
      </table>
      <br/>
      <button class="w3-button w3-white w3-border w3-border-blue w3-round-large" ng-click="addRow()">Add Row</button>
  </div>
  <div class="w3-container">
    <h5>Analytics</h5>
    <p class="analytics"><b>Row Count:</b> {{ rows.length }}</p>
    <p class="analytics"><b>Focused Index:</b> {{ focusedIndex }}</p>
  </div>
  <div class="w3-container">
    <h5>Rows</h5>
    <div ng-repeat="row in rows">
      <p class="analytics"><b>Row Index:</b> {{$index}} {{ row.firstName }} {{ row.lastName }}</p>
    </div>
  </div>
</div>

CSS

body{
   font-family:  Sans-serif;
}

div{
   width:250px;
}

div[contentEditable] {
    cursor: pointer;
    background-color: white;
    padding: .2em;
}

div[contentEditable]:focus {
    cursor: pointer;
    background-color: lightyellow;
    padding: .2em;
}

h1{
    font-size: x-large;
    font-weight: bold;
}

h2{
 font-family: Lucida Console;  
 font-size: larger;
 font-weight: 700;
 margin-bottom: 5px;
}

h3{
 font-family: Lucida Console;  
 font-size: medium;
 font-weight: 500;
 margin-bottom: 5px;
}

p {
    width:  400px;
}

.analytics{
    margin: .3em;
}

.userInputPrompt{
    color:  gray;
    opacity: 0.7;
    font-style: italic;
}

JavaScript

//Instantiate the parent angualr module
var myApp = angular.module('myApp', []);
//Define and instantiate the views controller
myApp.controller('demo', ['$scope', function($scope){
    //define the default value for a new row
    $scope.defaultValue = 'Please Enter a Value';
    //define the rows array variable and first row with seed values that are not the enter data cell prompt
    $scope.rows = [{firstName:'Michael', lastName:'Griehm'}];
    
    $scope.focusedIndex = null;
    //define the add row function to add a row the rows array
    $scope.addRow = function() {
        $scope.rows.push({firstName: '', lastName: ''});
    };
    
    $scope.elementClicked = function($index){
      	$scope.focusedIndex = $index;  
    };
}]);

myApp.directive('contenteditable', function() {
    return {
        require: 'ngModel',
        link: function(scope, elm, attrs, ctrl) {
            //element render event handler that applies the value of the controller's view to the element, or add a user input prompt if the value is a blank string
            ctrl.$render = function() {
                if (ctrl.$viewValue.length === 0){
                    elm.html(scope.defaultValue);
                    elm.addClass('userInputPrompt');   
                }
                else{
                    elm.html(ctrl.$viewValue);
                }
            };
            //element keydown event handler to avoid allowing the enter button to add a new line feed in the cell
            elm.bind('keydown', function(event) {
                if (event.which === 13) {
                    event.preventDefault();                        
                } 
            });
            //element keyup event handler to capture the new value of the element and apply it to the view
            elm.bind('keyup', function() {
                scope.$apply(function() {
                    ctrl.$setViewValue(elm.html());
                });
            });
            //element focus event handler...