10

I have been trying to figure out the solution but I think i hit a dead end.

So here is my directive

directives.directive('postprocess', function($compile)
{
    return {
        restrict : 'E',
        require: '^ngModel',
        scope: {
            ngModel: '='
        },
        link: function(scope, element, attrs) {
            var parsed = scope.ngModel;
            el = $compile(parsed)(scope);
            element.html("");
            //add some other html entities/styles.
            element.append(el);
            console.log(parsed);
        }  
    };
});

The html

<postprocess ng-model="some_model.its_property" style="padding-top: 10px;" />

Somewhere in the controller, I update the model property

some_model.its_property = 'Holla';

But it doesn't update the corresponding directive. It works perfectly when loading which tells me that it might not be entirely a scoping issue.

drs
  • 5,679
  • 4
  • 42
  • 67
Saikiran Yerram
  • 2,994
  • 3
  • 17
  • 20

3 Answers3

16

It's much simpler, so I have removed some extra code you had there.

Please take a look at the code below or working Plunker:

<!doctype html>
<html lang="en" ng-app="myApp">
    <head>
    <meta charset="UTF-8">
    <title>Document</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.8/angular.min.js"></script>

    <script>
        var myApp = angular.module('myApp', []);
        myApp.directive('postprocess', function ($timeout) {
            return {
                restrict : 'E',
                transclude: 'true',
                scope: {
                    myVariable: '='
                },
                link: function(scope, element, attrs) {
                    $timeout(function () {
                        scope.myVariable = 'Bye bye!'
                    }, 200);
                }  
            };
        });

        myApp.controller('myAppCtrl', ['$scope', '$timeout', function ($scope, $timeout) {
            $scope.myVariable = {
                value : 'Holla'
            };

            console.log($scope.myVariable.value); // -> prints initial value
            $timeout(function () {
                console.log($scope.myVariable.value); // -> prints value after it is changed by the directive
            }, 2000);
        }])
    </script>

    </head>
    <body ng-controller="myAppCtrl">
        <postprocess my-variable="myVariable.value" style="padding-top: 10px;" />
    </body>
</html>
  1. The controller sets the initial value to 'Holla'
  2. The directive receives that value by the my-variable attribute
  3. Using two way data-binding any changes made to scope.myVariable updates the $scope.myVariable of the main controller
  4. After few seconds $scope.myVariable changes to 'Bye Bye'
  5. Take a look at your console.log

$watch and $apply

Angular's two-way data binding is the root of all awesome in Angular. However, it's not magic, and there are some situations where you need to give it a nudge in the right direction.

When you bind a value to an element in Angular using ng-model, ng-repeat, etc., Angular creates a $watch on that value. Then whenever a value on a scope changes, all $watches observing that element are executed, and everything updates.

Sometimes, usually when you're writing a custom directive, you will have to define your own $watch on a scope value to make the directive react to changes.

On the flip side, sometimes you change a scope value in some code but the app doesn't react to it. Angular checks for scope variable changes after pieces of your code have finished running; for example, when ng-click calls a function on your scope, Angular will check for changes and react. However, some code is outside of Angular and you'll have to call scope.$apply() yourself to trigger the update. This is most commonly seen in event handlers in custom directives.

Community
  • 1
  • 1
Denison Luz
  • 3,575
  • 23
  • 25
  • thanks for the prompt response. Changing value inside of the directive works as I expected but not the other way around. See this fiddle http://jsfiddle.net/T7cqV/3/ – Saikiran Yerram Oct 01 '13 at 17:58
  • 1
    It does work the way around too! I have updated your jsfiddle (http://jsfiddle.net/denisonluz/T7cqV/8/). You need to add a `scope.$watch` so you can track the changes made by the controller to the $scope. – Denison Luz Oct 01 '13 at 18:46
  • Thanks @dluz. I wanted to avoid `watch` but I guess I have no option. – Saikiran Yerram Oct 02 '13 at 00:52
  • 1
    @Sai, in fact there's no reason "to avoid `watch`". This implementation is completely normal and expected. I have updated my answer about with some text from AngularJS FAQ explaining the use of `$watch` and `$apply`. Take a look. – Denison Luz Oct 02 '13 at 07:38
  • Thanks @dluz. So a general question, having all these watches have an impact on performance? – Saikiran Yerram Oct 02 '13 at 19:42
  • 1
    @Sai, that's a really good question and it normally starts a good debate too :) There's no one better than the AngularJS's father to answer that. You can see his answer here (http://stackoverflow.com/a/9693933/1310945) – Denison Luz Oct 02 '13 at 22:54
  • thats a great answer. I am surprised I didn't see that.[going thru all of Misko's entries now]. So its plain old dirty checking which i believe is run after our code is exec e.g. it calls `ng-click` func which then calls `$apply` internally, which then sequential goes thru it updates dom. I do agree on event based update, in fact the initial question stemmed from that concern. I am more of a backend coder so still weeding out nuances of JS. thanks for the answer. – Saikiran Yerram Oct 03 '13 at 23:17
  • Though your answer looks thorough, I'm weary about using $timeout. Care to elaborate on why you use that? – Micros Dec 09 '15 at 12:36
  • @Micros this code example should work without the $timeout. The reason for the $timeout it was only to demonstrate that the value has changed after few seconds. But the solution does not depend on the $timeout. – Denison Luz Dec 09 '15 at 15:18
  • @DenisonLuz: is there a specific reason why it you've chosen `$scope.myVariable.value` instead of `$scope.myVariable` ? – el.severo Jul 26 '16 at 13:55
  • 1
    @DenisonLuz This is awesome! This issue was driving me nuts. Wasted so much time.... The moment I put "value" key in the controller, everything started working. Thanks a bunch! – Dean Sha Apr 12 '17 at 17:50
1

Some help from angularjs irc, & dluz, updated. Though I wish there was an easier way for the directive to be called, since the link function contains behavior and there should be a way to call that.

http://jsfiddle.net/T7cqV/5/

Saikiran Yerram
  • 2,994
  • 3
  • 17
  • 20
1

be sure that you use the dot rule

http://jimhoskins.com/2012/12/14/nested-scopes-in-angularjs.html

Benjamin McFerren
  • 822
  • 5
  • 21
  • 36