AngularJS - Is it possible to change the value of ngModel attribute on directive in link or compile?

20,879

Solution 1

I was able to accomplish the goal by using a template function. I think it doesn't work in the link function because it occurs after all of the directives have been collected, so the compiler doesn't recognize that an ngModel directive has been added. I'm not sure why it doesn't work in the compile function, though (even if I set the priority to 100).

Here's the working version of the directive:

angular.module('myModule').
  directive('myDirective', function() {
    return {
      replace: true,
      template: function(elem, attr) {
        var newElem = '<input ng-model="model.' + attr.name + '">';
        return newElem;
      }
    };
  });

Solution 2

You should check out the docs for the NgModelController. It will answer your question. For further explanation, here's the gist:

You can capture a fourth argument to the link: function, which is your ng-model value. You use that object to read and set the model.

link: function(scope, element, attrs, ngModel) {
    if(!ngModel) return; // do nothing if no ng-model

    // Specify how UI should be updated
    ngModel.$render = function() {
      element.html(ngModel.$viewValue || '');
    };

    // Listen for change events to enable binding
    element.on('blur keyup change', function() {
      scope.$apply(read);
    });
    read(); // initialize

    // Write data to the model
    function read() {
      var html = element.html();
      // When we clear the content editable the browser leaves a <br> behind
      // If strip-br attribute is provided then we strip this out
      if( attrs.stripBr && html == '<br>' ) {
        html = '';
      }
      ngModel.$setViewValue(html);
    }
}

Hope that helps.

Share:
20,879
hgcrpd
Author by

hgcrpd

Updated on October 26, 2020

Comments

  • hgcrpd
    hgcrpd over 3 years

    I am trying to create a directive that will add an ngModel attribute to a tag based on the attribute value. For example:

    angular.module('myModule').
      directive('myDirective', function() {
        return {
          link: function(scope, elem, attrs) {
            var modelName = 'myPrefix.' + attrs.name;
            attrs.$set('ngModel', modelName);
          }
        };
      });
    

    So that this html:

    <input name="foo" my-directive></input>
    

    is compiled into

    <input name="foo" ng-model="myPrefix.foo" my-directive></input>
    

    It takes the name of the input, attaches a prefix, and sets the ngModel attribute to that value.

    When I try to do this in the link function, it seems like the input isn't being registered with the formController, so that form.foo returns undefined.

    Is it possible to accomplish what I'm trying to do?

    EDIT:

    It seems like the ngModel attribute is being set on the HTML, but it is not being registered with the form, or the ngModelController is not being instantiated. If I look at the value of ngModel in the scope, it does not change when I modify the input.

  • hgcrpd
    hgcrpd over 10 years
    Actually in my case, the ngModel attribute is not guaranteed to be created on the tag, so the ngModelController wouldn't be instantiated. In other words, I want my directive to be compiled first, add the ngModel attribute, then I want the compiler to recognize that an ngModel attribute has been added, and then instantiate the ngModelController
  • tennisgent
    tennisgent over 10 years
    That probably would have been helpful to know up front. According the example under the Attributes section in the docs here, what you are doing should work for observing the ngModel.
  • hgcrpd
    hgcrpd over 10 years
    You're right, it does add the attribute, but the input is not modifying the model, so it seems like it is not being properly registered.
  • user2000095-tim
    user2000095-tim almost 10 years
    Link happens after compiler, so to get your original code to work you need to call something like $compile(elem)(scope) to recompile and pickup your changes to the DOM, the thing is though, you don't want that in the link function and it will cause the code to run again (and again etc) which needs a fraction extra code to check for too. The template function will be compiled after the directive is, so that is why it solves your problem.