Eric_WVGG
Eric_WVGG

Reputation: 2947

Angular directives: mixing attribute data and ngModel

I've had luck building directives that share scope, and ones that isolate scope, but I'm having trouble figuring out the correct way to do both.

<input type="text" ng-model="search" append-me terms="myTerms">

I'm trying to take an input like the one above, and staple a UL that ng-repeats over a list of attributes after it. I have two problems.

1) How do I correctly interface the shared ng-model scope?

2) What am I doing incorrectly with this compile function?

http://jsfiddle.net/vEQ6W/1/

Upvotes: 5

Views: 1447

Answers (2)

nordyke
nordyke

Reputation: 113

Regarding #2, "What am I doing incorrectly with this compile function?"

If you change your compile's code snippet from...

...
tElement.after(
    '<p>test</p>' + 
    '<ul><li ng-repeat="term in terms">{{term}}</li></ul>' +
    '<p>hm…</p>'
);
...

to...

...
tElement.after(
    '<p>test</p>' + 
    '<ul><li ng-repeat="term in myTerms">{{term}}</li></ul>' +
    '<p>hm…</p>'
);
...

the ng-repeat will render correctly. I cannot, however, tell you WHY it works.

Upvotes: 0

Beyers
Beyers

Reputation: 9108

Mixing isolated scope with ngModel is a documented issue, see the Isolated Scope Pitfall section in the documentation:

Isolated Scope Pitfall Note that if you have a directive with an isolated scope, you cannot require ngModel since the model value will be looked up on the isolated scope rather than the outer scope. When the directive updates the model value, calling ngModel.$setViewValue() the property on the outer scope will not be updated. However you can get around this by using $parent.

Using this knowledge and some freaky scope experiments I've come with two options that does what you want to do:

(1) See this fiddle It makes use of the $parent method as described above.

<div ng-controller="MyCtrl">
  <div>
    <input ng-form type="text" ng-model="$parent.search" append-me terms="myTerms">
  </div>
  {{search}}
</div>

myApp.directive('appendMe', ['$compile', function($compile) {
    return {
        restrict: 'A',
        scope: {terms: '='},
        link: function(scope, element, attributes) { // linking function
            console.log(scope.terms);
            var template = '<p>test</p>' + 
                '<ul><li ng-repeat="term in terms">{{term}}</li></ul>' +
                '<p>hm…</p>'
            element.after($compile(template)(scope));
        }
    }
}]);

(2) See this fiddle It does not use $parent, instead it uses the isolated scope to publish the search model as configured via ngModel.

<div ng-controller="MyCtrl">
    <div>
        <input ng-form type="text" ng-model="search" append-me terms="myTerms">
    </div>
    {{search}}
</div>

myApp.directive('appendMe', ['$compile', function($compile) {
    return {
        restrict: 'A',
        scope: {terms: '=', search: '=ngModel'},
        link: function(scope, element, attributes) { // linking function
            console.log(scope.terms);
            var template = '<p>test</p>' + 
                '<ul><li ng-repeat="term in terms">{{term}}</li></ul>' +
                '<p>hm…</p>'
            element.after($compile(template)(scope));
        }
    }
}]);

Both options seem to work just fine.

Upvotes: 5

Related Questions