AngularJS - Attribute directive input value change
Asked Answered
F

3

42

I've got an AngularJS attribute directive, and I would like to take an action any time its parent input's value changes. Right now I'm doing it with jQuery:

angular.module("myDirective", [])
.directive("myDirective", function()
{
    return {
        restrict: "A",
        scope:
        {
            myDirective: "=myDirective"
        },
        link: function(scope, element, attrs)
        {
            element.keypress(function()
            {
                // do stuff
            });
        }
    };
});

Is there a way to do this without jQuery? I'm finding the keyPress event isn't doing exactly what I want it to, and while I'm sure I'll come up with a solution, I get a little nervous when I resort to using jQuery in an Angular project.

So what's the Angular way to do this?

Forbiddance answered 30/4, 2013 at 20:20 Comment(0)
B
67

There's a great example in the AngularJS docs.

It's very well commented and should get you pointed in the right direction.

A simple example, maybe more so what you're looking for is below:

jsfiddle


HTML

<div ng-app="myDirective" ng-controller="x">
    <input type="text" ng-model="test" my-directive>
</div>

JavaScript

angular.module('myDirective', [])
    .directive('myDirective', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            scope.$watch(attrs.ngModel, function (v) {
                console.log('value changed, new value is: ' + v);
            });
        }
    };
});

function x($scope) {
    $scope.test = 'value here';
}


Edit: Same thing, doesn't require ngModel jsfiddle:

JavaScript

angular.module('myDirective', [])
    .directive('myDirective', function () {
    return {
        restrict: 'A',
        scope: {
            myDirective: '='
        },
        link: function (scope, element, attrs) {
            // set the initial value of the textbox
            element.val(scope.myDirective);
            element.data('old-value', scope.myDirective);

            // detect outside changes and update our input
            scope.$watch('myDirective', function (val) {
                element.val(scope.myDirective);
            });

            // on blur, update the value in scope
            element.bind('propertychange keyup paste', function (blurEvent) {
                if (element.data('old-value') != element.val()) {
                    console.log('value changed, new value is: ' + element.val());
                    scope.$apply(function () {
                        scope.myDirective = element.val();
                        element.data('old-value', element.val());
                    });
                }
            });
        }
    };
});

function x($scope) {
    $scope.test = 'value here';
}
Bimanous answered 30/4, 2013 at 20:31 Comment(11)
That could work. Any idea how to do it without requiring a model? I'd like to only add the directive.Forbiddance
If you want the directive to serve as ngModel, you can use isolate scope w/ a two-way binding, shown here: jsfiddle.net/langdonx/djtQRBimanous
I'm already using the directive, I'm afraid, but great suggestion.Forbiddance
What you have there is great. If I could do that without requiring the model, it would be perfect. It's not a deal-breaker, but I'd love to not require the model, since I'm not actually using it for anything. Marking this as the answer, since it's pretty much what I want, but if you have any ideas on how I can drop the model, I'd be interested. Thank you very much for your help.Forbiddance
What do mean already using the directive? I was suggesting that you modify your myDirective to act like the ngModelBlur directive in my example, which that doesn't require ngModel.Bimanous
@MikePateras Updated the answer, but I gather you're on the right track at this point.Bimanous
I'm using the value of the directive for something else.Forbiddance
Ahh, I see. You'll have to include another attribute in your HTML then or use ngModel. Either that, or hard code what property on your scope your directive uses (not recommended).Bimanous
Yeah, the model is fine. Come to think of it, it's pretty unlikely that anyone would be using this directive and not have a model anyway. Thank you again for all of your help. I'm very pleased with where this has ended up. Also, for anyone doing something similar in the future, if you call ngModel.$setViewValue(), don't forget to call ngModel.$render() afterward.Forbiddance
Thank you! I did not know you have to define the directive scope using HTML attributes. Very helpful.Crosswalk
Great Solution :DLavabo
H
12

Since this must have an input element as a parent, you could just use

<input type="text" ng-model="foo" ng-change="myOnChangeFunction()">

Alternatively, you could use the ngModelController and add a function to $formatters, which executes functions on input change. See http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController

.directive("myDirective", function() {
  return {
    restrict: 'A',
    require: 'ngModel',
    link: function(scope, element, attr, ngModel) {
      ngModel.$formatters.push(function(value) {
        // Do stuff here, and return the formatted value.
      });
  };
};
Hinder answered 30/4, 2013 at 20:34 Comment(1)
$formatters fire only on page load. $parsers fires on input change.Ahlgren
A
0

To watch out the runtime changes in value of a custom directive, use $observe method of attrs object, instead of putting $watch inside a custom directive. Here is the documentation for the same ... $observe docs

Alcazar answered 7/11, 2016 at 17:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.