34

I have a need for inheriting scope from a parent controller in a directive. I don't necessarily want to leave scope: false. I also don't necessarily want to use an isolated scope, because it requires a lot of work to get the values I do care about linked properly (think lots of values in a parent controller).

Does it make sense to use scope:true in my directive if I want to update parent scope?

<div ng-controller="MyCtrl">
      Hello, {{name}}!
        <my-directive></my-directive>
</div>
var myApp = angular.module('myApp',[]);

//myApp.directive('myDirective', function() {});
//myApp.factory('myService', function() {});

function MyCtrl($scope) {
    $scope.name = 'Dave';
}


myApp.directive('myDirective', function() {
    return {
        scope: true,
        restrict: 'EA',
        link: function(scope, elem, attrs) {
            scope.updateName = function(newName) {
                console.log('newName is: ' + newName);
                scope.name = newName;
            }
        },
        template: '<input ng-model="updatedName" placeholder="new name value"> <button ng-click="updateName(updatedName)">Update</button>'
    }
})

Please check out the fiddle

kapex
  • 28,903
  • 6
  • 107
  • 121
binarygiant
  • 6,362
  • 10
  • 50
  • 73

3 Answers3

52

Although @user1737909 already referenced the SO question to read (What are the nuances of scope prototypal / prototypical inheritance in AngularJS?, which will explain the problem and recommended various ways to fix it), we normally try to give the answer on SO.

So, the reason your fiddle doesn't work is because when a primitive type (i.e., a string, number, or boolean type) is written to -- e.g., scope.name = newName -- the "write" always goes to the local scope/object. In other words, the child scope gets its own name property that shadows the parent property of the same name. The fix is to use an object, rather than a primitive type, in the parent scope. The child scope will then get a reference to that object. Any writes to the object properties (whether from the parent or the child) will go to that one object. (The child scope does not get its own object.)

$scope.obj = {name: 'Dave'};

Then in your directive:

scope.obj.name = newName;

and HTML:

Hello, {{obj.name}}!

fiddle

Community
  • 1
  • 1
Mark Rajcok
  • 362,217
  • 114
  • 495
  • 492
  • understanding only this part and not how scopes work more generally is imho not a good idea. – Ven May 31 '13 at 09:23
  • This is what I was looking for. This is the piece that glued it (or a large portion of it) together. Thanks, Mark for the detailed answer, this is really great. – binarygiant May 31 '13 at 13:06
  • 1
    Sorry to dig an old thread but, in material-angular one can use for instance the mdSelect directive like this: – gpothier May 19 '15 at 22:57
  • So how can this work? http://adamalbrecht.com/2013/12/12/creating-a-simple-modal-dialog-directive-in-angular-js/ The directive overwrites show but the reference to modalShown is still kept. – user2906759 Oct 22 '15 at 08:40
15

Scope inheritance is not meaning setting the value of a child is setting the value of its parent.

Instead of doing scope.name = newName on the child scope, add a method to the parent scope, which will do the same job but on the parent scope, and call it from the child scope since the child inherits this method.

atondelier
  • 2,424
  • 15
  • 11
  • This is what I went with, it seemed a cleaner to provide a setter method than allowing direct access to the properties of the parent scope. – Gruffy Feb 03 '16 at 16:20
6

Within your link function you would write to the parent scope(the global "$scope" scope) like so: scope.$parent.name = newName;

Rebekah Waterbury
  • 22,236
  • 5
  • 23
  • 28