EricC
EricC

Reputation: 5870

AngularJS watch in directive giving newValue and oldValue undefined

I am trying to make an alert service with a directive. The service I use to keep track of all the alerts in my app. And the directive I use to show the alerts. I am adding a watch in the directive so I can add a timeout for those alerts that need to automatically disappear after a certain amount of time.

But the watch gives only undefined for newValue and oldValue. Why is that? The messages are added nicely, but without the timeout... One thing is why the watch in my directive is not working, but another thing is: I am approaching this problem the correct way, perhaps I should do something completely different?

The directive that looks like this:

angular.module('alertModule').directive('ffAlert', function() {
  return {
    templateUrl: 'components/alert/ff-alert-directive.html',
    controller: ['$scope','alertService',function($scope,alertService) {
      $scope.alerts = alertService;
    }],
    link: function (scope, elem, attrs, ctrl) {
      scope.$watch(scope.alerts, function (newValue, oldValue) {
        console.log("alerts is now:",scope.alerts,oldValue, newValue);
        for(var i = oldValue.list.length; i < newValue.list.length; i++) {
          scope.alerts.list[i].isVisible = true;
          if (scope.alerts.list[i].timeout > 0) {
            $timeout(function (){
              scope.alerts.list[i].isVisible = false;
            }, scope.alerts.list[i].timeout);
          }
        }
      }, true);
    }
  }
});

The reason for the for-loop is to attach a timeout for the alerts that has this specified (and it is a for-loop in case there are several alerts that has been added before the watch kicks in).

The directive-template:

<div class="row">
  <div class="col-sm-1"></div>
  <div class="col-sm-10">
    <div alert ng-repeat="alert in alerts.list" type="{{alert.type}}" ng-show="alert.isVisible" close="alerts.close(alert.id)">{{alert.msg}}</div>
  </div>
  <div class="col-sm-1"></div>
</div>

And here is the alertService:

angular.module('alertModule').factory('alertService', function() {
  var alerts = {};
  var id = 1;

  alerts.list = [];

  alerts.add = function(alert) {
    alert.id = id;
    alert.isVisible = true;
    alerts.list.push(alert);
    alert.id += 1;
    console.log("alertService.add: ",alert);
    return alert.id;
  };

  alerts.add({type: "info", msg:"Dette er til info...", timeout: 1000});

  alerts.addServerError = function(error) {
    var id = alerts.add({type: "warning", msg: "Errormessage from server: " + error.description});
//    console.log("alertService: Server Error: ", error);
    return id;
  };

  alerts.close = function(id) {
    for(var index = 0; index<alerts.list.length; index += 1) {
      console.log("alert:",index,alerts.list[index].id);
      if (alerts.list[index].id == id) {
        console.log("Heey");
        alerts.list.splice(index, 1);
      }
    }
  };

  alerts.closeAll = function() {
    alerts.list = [];
  };

Upvotes: 0

Views: 3002

Answers (2)

Rahil Wazir
Rahil Wazir

Reputation: 10132

scope.$watch already looking within current controller scope. You don't need to provide the property in watch expression along with its scope.

scope.$watch(scope.alerts, function (newValue, oldValue) { ...

should be (property should be pass as a string literal)

   scope.$watch('alerts', function (newValue, oldValue) { ...
// ^^^^^^ already looking within current controller scope

Upvotes: 2

Maarten Winkels
Maarten Winkels

Reputation: 2417

It would be much better to handle the timeout in the service instead of in the directive: if you put ng-show="alert.isVisible" in the template of your directive, angular will automatically create a watch for you. Not on the service or the collection as you are doing, but on the property of the object directly.

In your service, you can set the timeout in the add method:

alerts.add = function (alert) {
  ...
  if (alert.timeout > 0) {
    $timeout(function (){
      alert.isVisible = false;
    }, alert.timeout);
  }
}

Upvotes: 1

Related Questions