25

I'm trying to write a directive which make use of isolated scope and ngModel directive.

Problem:
When the model is updated in the directive the value of the caller is not getting updated.

HTML:

<test-ng-model ng-model="model" name="myel"></test-ng-model>

Directive:

app.directive(
    'testNgModel', [
    '$timeout',
    '$log',

function ($timeout, $log) {

    function link($scope, $element, attrs, ctrl) {
        var counter1 = 0, counter2 = 0;

        ctrl.$render = function () {
            $element.find('.result').text(JSON.stringify(ctrl.$viewValue))
        }

        $element.find('.one').click(function () {
            if ($scope.$$phase) return;
            $scope.$apply(function () {
                var form = angular.isObject(ctrl.$viewValue) ? ctrl.$viewValue : {};
                form.counter1 = ++counter1;
                ctrl.$setViewValue(form);
            });
        });
        $element.find('.two').click(function () {
            if ($scope.$$phase) return;
            $scope.$apply(function () {
                var form = angular.isObject(ctrl.$viewValue) ? ctrl.$viewValue : {};
                form.counter2 = ++counter2;
                ctrl.$setViewValue(form);
            });
        });

        $scope.$watch(attrs.ngModel, function (current, old) {
            ctrl.$render()
        }, true)
    }

    return {
        require: 'ngModel',
        restrict: 'E',
        link: link,
        //if isolated scope is not set it is working fine
        scope: true,
        template: '<div><input type="button" class="one" value="One"/><input type="button" class="two" value="Two"/><span class="result"></span></div>',
        replace: true
    };

}]);

Demo: Fiddle

If the isolated scope is not set it works fine: fiddle

Arun P Johny
  • 384,651
  • 66
  • 527
  • 531
  • 1
    `scope: true` does not create an isolate scope, it creates a new child scope that prototypically inherits from the parent scope, hence the reason `$parent.model` works. (An isolate scope is created when we use the `scope: { ... }` syntax. Here, an new child scope is also created, but it does not prototypically inherit from the parent.) In general, a child scope should be used with ng-model since you are creating a component that needs to interact with other directives (i.e., ng-model). So I suggest you go with your second, working, fiddle. – Mark Rajcok Aug 31 '13 at 15:26
  • @MarkRajcok that is not an option for me since the directive is more complex and it adds some custom attributes to the scope which will lead to pollution of the parent scope – Arun P Johny Aug 31 '13 at 15:32
  • Ok, use `scope: true`, but also use an object, not a primitive: ``. [fiddle](http://jsfiddle.net/mrajcok/6ZaB5/). – Mark Rajcok Aug 31 '13 at 15:38

2 Answers2

13

As discussed in the comments, it is generally not recommended to use a child scope (scope: true or scope: { ... }) with ng-model. However, since Arun needs to create additional scope properties, scope: true can be used with an object, not a primitive. This leverages prototypical inheritance, so $parent is not neeed:

<test-ng-model ng-model="someObj.model" ...>

fiddle

Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • is it possible to make it work with isolated scope instead of new scope http://jsfiddle.net/arunpjohny/gbfNY/1/ – Arun P Johny Sep 02 '13 at 03:29
  • @ArunPJohny, the parent scope is not getting updated -- notice that the `{{someObj.model | json}}` output is empty. For more about how isolate scopes and ng-model don't mix, see http://stackoverflow.com/questions/11896732/ngmodel-and-component-with-isolated-scope. – Mark Rajcok Sep 03 '13 at 14:39
7

Because you created an isolated scope, ngModel="model" refers to your new isolated scope. If you want to refer to your AppController scope, you should use $parent:

<test-ng-model ng-model="$parent.model" name="myel"></test-ng-model>
Wagner Francisco
  • 2,210
  • 1
  • 16
  • 15