AngularJS - how to change the value of ngModel in custom directive?

73,321

Solution 1

There are different ways of doing it:

  1. $setViewValue() updates the view and the model. Most cases it is enough.
  2. If you want to disconnect view from the model (e.g. model is a number but view is a string with thousands separators) then you could access directly to $viewValue and $modelValue
  3. If you also want to overwrite the content of ng-model (e.g. the directive changes the number of decimals, updating also the model), inject ngModel: '=' on the scope and set scope.ngModel

e.g.

  return {
     restrict: 'A',
     require: 'ngModel',
     scope: {
         ngModel: '='
     },
     link: function (scope, element, attrs, ngModelCtrl) {

        function updateView(value) {
            ngModelCtrl.$viewValue = value;
            ngModelCtrl.$render(); 
        }

        function updateModel(value) {
            ngModelCtrl.$modelValue = value;
            scope.ngModel = value; // overwrites ngModel value
        }
 ...

LINKS:

Solution 2

To work with complex binding expressions you should use the $parse service and the assign method.

For more information watch this video from ng-conf - it's all about the cool things you can do with the ng-model directive: https://www.youtube.com/watch?v=jVzymluqmg4

app.directive('datepicker', ['$parse',
    function($parse) {
        return {
            require: '?ngModel',
            link: function(scope, element, attributes, controller) {
                // $parse works out how to get the value.
                // This returns a function that returns the result of your ng-model expression.
                var modelGetter = $parse(attributes['ngModel']);
                console.log(modelGetter(scope));

                // This returns a function that lets us set the value of the ng-model binding expression:
                var modelSetter = modelGetter.assign;

                // This is how you can use it to set the value 'bar' on the given scope.
                modelSetter(scope, 'bar');

                console.log(modelGetter(scope));
            }
        };
    }
]);

Solution 3

What you tried is actually working: see this Plunker

You don't "see" it in the input because changing the model this way doesn't call controller.$render() to set the new controller.$viewValue.

But why don't you simply change the $scope value (unless you don't know it, but it would be weird):

angular.module('main').directive('datepicker', [function() {
    return {
        require: '?ngModel',
        link: function(scope, element, attributes, controller) {
          var model = attributes['ngModel'];
          scope[model] = 'bar';
        }
    };
}]);

And in your html:

<input ng-model="yourVariable" datepicker>

EDIT: (dynamic solution)

angular.module('main').directive('datepicker', [function() {
    return {
        require: '?ngModel',
        link: function(scope, element, attributes, controller) {
          // get the value of the `ng-model` attribute
          var model = attributes['ngModel'];

          // update the scope if model is defined
          if (model) {
            scope[model] = 'bar';
          }
        }
    };
}]);

Solution 4

Here's the best explanation I've encountered. This helped me big time, and brings together details from a number of the other answers here.

TIP: Be careful to read the whole article rather than skim it, or else you are likely to miss some key bits!

https://www.nadeau.tv/post/using-ngmodelcontroller-with-custom-directives/

Solution 5

This works for a DatePicker on my site

link: function(scope, elem, attrs, ngModel) {
         scope.$apply(function(){
             ngModel.$viewValue = value;
         }
} 
Share:
73,321

Related videos on Youtube

Sady
Author by

Sady

Updated on July 09, 2022

Comments

  • Sady
    Sady almost 2 years

    Lets take a look to my directive:

    angular.module('main').directive('datepicker', [
    function() {
        return {
            require: '?ngModel',
            link: function(scope, element, attributes, ngModel) {
                ngModel.$modelValue = 'abc'; // this does not work
                // how do I change the value of the model?
    

    So, how do I change the value of the ng-model?

    • glepretre
      glepretre over 10 years
      Why don't you change the $scope value related? For now I've only used $modelValue to read it. Btw, the fourth attribute of the link function is ngModelController in your case, you should call it controller ;)
    • Sady
      Sady over 10 years
      @glepretre, how can I use scope? model is passing via html like this <input ng-model="anythingModel"> and I dont know the name of the model to use it with scope. and about controller call. Take a look at linkthis, how do they call it. But its wrong too, I think the best name probably will be ngModelCtrl
    • vaske
      vaske almost 9 years
      I tried all solutions from here but neither is supposed to work :(
  • Sady
    Sady over 10 years
    but what if I change <input ng-model="yourVariable" datepicker> to <input ng-model="myVariable" datepicker>. How directive will react? Note, that I need reusable directive
  • glepretre
    glepretre over 10 years
    Your directive is reusable but ng-model="foo" must match $scope.foo to work, that's the point. I edited my answer to provide another solution to make it more dynamic.
  • Sady
    Sady over 10 years
    its still not reusable enough. What if I use complex models as users[0].educations[1].name ?
  • Alex Lapa
    Alex Lapa about 9 years
    It will not work if your ngModel is bound to smth like "objectOnScope.nestedValue"
  • glepretre
    glepretre about 9 years
    @AlexLapa Yes I know, it was not required in the question. That's why I did not used $parse in my answer but I'm a fair-player, I upvoted Sly_cardinal's one :)
  • vaske
    vaske almost 9 years
    I tried this one but still do not update model on page.
  • dball
    dball almost 9 years
    Can you post your html?
  • vaske
    vaske almost 9 years
    I was desperate and this is my question stackoverflow.com/questions/31122371/…
  • Volte
    Volte over 8 years
    Ahhh! Finally. Thanks "brah"! I missed the scope ngModel: '='