user687554
user687554

Reputation: 11131

Using One-Way Binding in AngularJS Directive

I have a directive defined like this:

myApp.directive('stoplight', function() {
    return {
        restrict:'E',
        transclude: true,
        scope: {
            value: '@'
        }, 
        link: function(scope, element) {
            if (scope.value === true) {
                element.html('<i class="icon icon-true green"></i>');
            } else if (scope.value === false) {
                element.html('<i class="icon icon-false red"></i>');
            } else {
                element.html('<i class="icon icon-unknown yellow"></i>');
            }
        }
    };
});

When I use this directive, I use the following markup:

<stoplight value="boolValue" />

My controller behind stoplight looks like this:

myApp.controller('MyController', function($scope, $http) {
    $scope.boolValue = null;
    $scope.init = function() {
      $scope.boolValue = false;
      $http.jsonp('http://anyorigin.com/get?url=www.google.com&callback=JSON_CALLBACK')
        .success(function() {
            console.log('woohoo!');
            $scope.boolValue = true;
        });
    };

    $scope.init();
}};

I have two issues, and neither make sense to me.

  1. The '@' in my directive doesn't work. If I change the '@' to a '=', the link function works somewhat as expected. However, I want to use one-way binding instead of two-binding for performance reasons.
  2. For some reason, the $scope.boolValue = true; in my success callback doesn't update the UI. The icon stays red. I can set the value to null, expecting yellow, but it stays red. If I look in the console window though, I can see 'woohoo!'. I don't understand why updating boolValue outside of the callback works, yet in the callback, it just doesn't work. I do not see any error in the console window or anything of that nature.

Can someone please help me identify why this isn't working? I'm not sure if this is one issue or two issues. I think they both have to do with the binding. However, I'm not sure how to remedy this.

Thank you.

Upvotes: 1

Views: 1281

Answers (3)

user3922673
user3922673

Reputation: 11

Regarding issue #1, I believe it is occurring because '@' always results in a string value. So you probably need scope.value === 'true' and scope.value === 'false'.

Regarding issue #2, as neilhem already answered, you need the double curly braces:

<stoplight value="{{boolValue}}" />

Upvotes: 1

domakas
domakas

Reputation: 1246

For #1: using @ doesn't mean 'one-way binding', it means to take evaluated value of the DOM attribute, not the variable, so scope.value will result in a string value of 'boolValue', you could use <stoplight value="{{ boolValue }}" /> in your template or = in your directive. Here is a great explanation how @ and = differs.

For #2: Your UI doesn't update, because you directive is not watching for changes on attribute value. When directive is initialized, it takes first value and does the stuff in your directive, however - that's it. When value changes, nothing should happen in directive. Try this:

myApp.directive('stoplight', function() {
  return {
    restrict:'E',
    transclude: true,
    scope: {
      value: '@'
    },
    link: function(scope, element) {
      scope.$watch(function() { return scope.value; }, function(value) {
        if (value === true) {
          element.html('<i class="icon icon-true green"></i>');
        } else if (value === false) {
          element.html('<i class="icon icon-false red"></i>');
        } else {
          element.html('<i class="icon icon-unknown yellow"></i>');
        }
      });
    }
  };
});

Upvotes: 0

Rakhat
Rakhat

Reputation: 4942

Use double curly braces <stoplight value="{{ boolValue }}" />

Upvotes: 0

Related Questions