Stack learner Stack learner - 7 months ago 139
Javascript Question

how to update ng-model of textarea after manual insert of some text into it

I have this code which takes input from an input area and inserts the text at the caret position inside a text area, the insert part is working accurately but ng-model of text area fails to update the same. here is the code



var app = angular.module('plunker', []);

app.controller('MyCtrl', function($scope, $rootScope) {
$scope.items = [];
$scope.add = function() {
$scope.items.push($scope.someInput);
$rootScope.$broadcast('add', $scope.someInput);
}
});

app.directive('myText', ['$rootScope', function($rootScope) {
return {
link: function(scope, element, attrs) {
$rootScope.$on('add', function(e, val) {
var domElement = element[0];

if (document.selection) {
domElement.focus();
var sel = document.selection.createRange();
sel.text = val;
domElement.focus();
} else if (domElement.selectionStart || domElement.selectionStart === 0) {
var startPos = domElement.selectionStart;
var endPos = domElement.selectionEnd;
var scrollTop = domElement.scrollTop;
domElement.value = domElement.value.substring(0, startPos) + val + domElement.value.substring(endPos, domElement.value.length);
domElement.focus();
domElement.selectionStart = startPos + val.length;
domElement.selectionEnd = startPos + val.length;
domElement.scrollTop = scrollTop;
} else {
domElement.value += val;
domElement.focus();
}

});
}
}
}])

<!DOCTYPE html>
<html ng-app="plunker">

<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link href="style.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<script src="app.js"></script>
</head>

<body>
<div ng-controller="MyCtrl">
<input ng-model="someInput">
<button ng-click="add()">Add</button>
<p ng-repeat="item in items">Created {{ item }}</p>
</div>
<p>add some text and change caret position and insert test using input area above</p>
<textarea my-text="" ng-model="textarea">
</textarea>
<p>{{textarea}}</p>
</body>

</html>




Answer

The quickest fix to your code that I can think of is added below. Basically, require the ng-model controller into your directive, and inform it when the value has changed.

A little off-topic, but having your myText directive be controlled via global scope (in the form of $rootScope.$on) smells bad to me.

var app = angular.module('plunker', []);

app.controller('MyCtrl', function($scope, $rootScope) {
  $scope.items = [];
  $scope.add = function() {
    $scope.items.push($scope.someInput);
    $rootScope.$broadcast('add', $scope.someInput);
  }
});

app.directive('myText', ['$rootScope', function($rootScope) {
  return {
    require: '^ngModel', // request our containing ng-model controller
    link: function(scope, element, attrs, ngModelCtrl) { // DI
      $rootScope.$on('add', function(e, val) {
        var domElement = element[0];

        if (document.selection) {
          domElement.focus();
          var sel = document.selection.createRange();
          sel.text = val;
          domElement.focus();
        } else if (domElement.selectionStart || domElement.selectionStart === 0) {
          var startPos = domElement.selectionStart;
          var endPos = domElement.selectionEnd;
          var scrollTop = domElement.scrollTop;
          domElement.value = domElement.value.substring(0, startPos) + val + domElement.value.substring(endPos, domElement.value.length);
          domElement.focus();
          domElement.selectionStart = startPos + val.length;
          domElement.selectionEnd = startPos + val.length;
          domElement.scrollTop = scrollTop;
        } else {
          domElement.value += val;
          domElement.focus();
        }
        // inform ng-model that the value has changed
        ngModelCtrl.$setViewValue(domElement.value);
      });
    }
  }
}])
<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link href="style.css" rel="stylesheet" />
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
    <script src="app.js"></script>
  </head>

  <body>
    <div ng-controller="MyCtrl">
      <input ng-model="someInput">
      <button ng-click="add()">Add</button>
      <p ng-repeat="item in items">Created {{ item }}</p>  
    </div>
    <p>add some text and change caret position and insert test using input area above</p>
    <textarea my-text="" ng-model="textarea">
    </textarea>
    <p>{{textarea}}</p>
  </body>

</html>

Comments