Search code examples
angularjscoffeescriptangularjs-directiveangularjs-scope

How can I bind deep child property of directive's scope?


My dear friends,

We can bind a scope property of a directive to the value of DOM attribute.

This works:

module.directive 'MyDirective', ->
  scope:
    directiveVar: '='

...

<div class='MyDirective' directive-var='parentVar'></div>

In example above we bind directive's directiveVar property to parentVar property of the parent scope.

This is a bi-directional binding, so if directiveVar is changed parentVar is automatically updated, and vice versa.

My question is:

Is there a way I can bind a deep child property of my directive's scope instead? Like scope.lv1.directiveVar or scope.lv1.lv2.lv3.directiveVar instead of scope.directiveVar?

Docs I read

What I want to achieve

I have an object in directive scope named lv1. I want to bind its property directiveVar to parent property.

This does not work:

scope:
    lv1:
        directiveVar: '='

And this does not work:

scope:
    "lv1.directiveVar": '=myVar'

Demo

This is what works: http://plnkr.co/edit/OClnZ2Cl3BXr60PC2qVP?p=preview

This is what I want to achieve: http://plnkr.co/edit/tQEHeKOzGjGyplCwUtU2?p=preview


Solution

  • I hope this code will help. You can pass in an object and watch its properties or you can nest things in parent/child directives. Either way adding the "=" will enable two way binding on the entire object.

    Controller:

    $scope.directiveVar = {
       level1: {
           greeting: 'Hello'
       }
    };
    $scope.otherVar = {
        levelA: {
            foo: 'bar'
        }
    };
    


    Markup:

    <div data-my-parent-directive data-other-var="otherVar">
        <div data-my-directive data-directive-var="directiveVar"></div>
    </div>
    


    Directive:

    angular.module('MyDirective', [])
    .directive('myParentDirective', ['$window',
        function ($window) {
            return{
                restrict: 'AE',
                scope:{
                    otherVar: '='
                },
                controller: function($scope, $element) {
                    var othis = this;
                    this.element = $element;
                    this.otherVar = $scope.otherVar;
                }
            };
        }
    ])
    .directive('myDirective', ['$window',
        function ($window) {
            return {
                restrict: 'AE',
                require: '?myParentDirective',
                scope: {
                    directiveVar: '='
                },
                link: function(scope, elm, attr, myParentDirectiveCtrl) {
                    console.log(myParentDirectiveCtrl.otherVar);
                    console.log(myDirectiveParentCtrl.otherVar.levelA);
                    scope.$watch('directiveVar.level1', function (newValue, oldValue){
                        console.log(newValue, oldValue);
                    });
                }
            };
        }
    ])
    


    Edit:

    You could simply do this:

    <div data-my-parent-directive data-other-var="otherVar">
        <div data-my-directive data-directive-var="directiveVar.level1"></div>
    </div>
    

    Which is useful when modeling data. For example:

    var items = apiService.getItems();
    var layers = [...],
    $scope.map = {
        points: items,
        layers: layers,
        highAltMap: 'Streets',
        lowAltMap: 'Satellite'
    };
    

    That said if you are modelling data see this ng-conf video which I believe the speaker touched on some OO patterns.

    Edit 2:

    You could use a service like this plunker