xenoterracide
xenoterracide

Reputation: 16837

How can I update an angular directive attribute dynamically?

I'm playing with Angular Timer, what I want to do is to be able to change the "countdown" on event. I've tried this

<timer interval="1000" countdown="{{countdown}}">{{minutes}}:{{seconds}}</timer>

with the guts of my controller looking like

// setup
...
$scope.timerRunning = true;
$scope.countdown    = 10;
...

I want to be able to dynamically update the countdown based on conditions handled in my controller, e.g. have it add 10 on each complete ( I know how to deal with the event, it's just a matter of making the directive update the countdown)

Upvotes: 0

Views: 1464

Answers (3)

KayakDave
KayakDave

Reputation: 24676

You don't need the double curlies {{ ... }} in an attribute as it's already interpreted as an angular expression. So the following will set the attribute based on the scope variable countdown:

<timer interval="1000" countdown="countdown"/>{{minutes}}:{{seconds}}</timer>

with the following in your controller:

$scope.countdown= 120;

Dynamically Updating

The challenge is to dynamically update the timer. Directive attributes aren't watched, by default, on digest cycles. So once the countdown begins it doesn't check the attribute again. For instance if you used a button like this that updated countdown:

<button ng-click="setCountdown(20)">set to 20</button>

With this function

$scope.setCountdown = function(newVal) {
  $scope.countdown =newVal;
}

Clicking the button would update the countdown variable but the timer wouldn't be affected.

Solution

The authors of that timer directive have provided a timer start event we can use in the setCountdown function like so:

$scope.setCountdown = function(newVal) {
  $scope.countdown =newVal;
  $timeout(function(){
     $scope.$broadcast('timer-start');
  },0);
}

Here we update the countdown, then we issue timer-start event to restart the timer. Note that I issue the broadcast inside a $timeout to ensure that the attribute has been updated. Without that $timeout the results aren't consistent.

Demo

Upvotes: 4

Josh
Josh

Reputation: 1

I think you need to use the "=" isolate scope option in your directive to achieve this. From my understanding, this allows you to bind your controller property to your directive and vice versa.

You would need to change your countdown attr to use the controller object:

<timer interval="1000" countdown="countdown">{{minutes}}:{{seconds}}</timer>

Your controller would look something like this:

app.controller("TimerCtrl", function($scope) {
$scope.timerRunning = true;
$scope.countdown    = 10;
});

And your directive would be something along the lines of:

app.directive("timer", function() {
return {
    restrict: "E",
    transclude: true,
    scope: {
        countdown: "="
    },
    template: '<div ng-transclude></div>',
    link: function (scope, element) {
        scope.incrementCountdown = function (amount) {
            scope.countdown += parseint(amount, 10);
        }

        // something that would trigger the increment, could be whatever
        element.bind('click', function () {
            scope.incrementCountdown(10); // could also pull the param value from an attribute on your directive
        });
    }
}
});

This way if you change the countdown within the scope of your directive it should update the model in your controller as well.

I haven't run this code so it may not be error free and I'm an Angular n00b, so hopefully I'm not leading you astray, but I'm sure someone else will chime in if this is wrong. Good luck.

Upvotes: 0

NicolasMoise
NicolasMoise

Reputation: 7279

I would try using some kind of $watch to observe when you want to change the countdowns.

Make sure that the countdown variables in your directives' controllers are binded to the parent scope. (e.g. scope: {countdown: '='} or something similar)

Upvotes: 0

Related Questions