Search code examples
javascriptangularjsangularjs-directiveangular-ngmodelangularjs-watch

Run function only the first time the user types in form input


This should be straight forward but haven't found a solution yet.

I have an input in a form. I want to detect when a user has interacted with the input and run a javascript function once if they have.

I have been thinking of using a $watch to detect if the input element has the class ng-dirty and if it has, run js function and unbind the watch.

Is there a better way? It would be great if you could provide an example.


Solution

  • This is a simple directive that should do what you're looking for.

    angular.module('myApp', [])
    .controller('MyCtrl', function($scope) {
      $scope.bar = function() {
        console.log('bar was called!');
        $scope.barWasCalled = true;
      };
    })
    .directive('once', function() {
      return {
        require: 'ngModel',
        scope: {
          fn: '&once'
        },
        link: function($scope, $element, $attrs, ngModel) {
          // add a listener and save the index for removal
          var idx = ngModel.$viewChangeListeners.push(function() {
            // user typed, run the function
            $scope.fn();
            // remove the listener
            ngModel.$viewChangeListeners.splice(idx, 1);
          }) - 1;
        }
      };
    })
    ;
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
    <div ng-app="myApp" ng-controller="MyCtrl">
      <input type="text" ng-model="foo" once="bar()" placeholder="type something...">
      <div ng-show="barWasCalled">Bar was called!</div>
    </div>

    $viewChangeListener offers just a touch better performance than a $watch, though it is nominal anyway.

    Remember to put any kind of DOM related behaviors such as this into directives. That keeps things light and neat.