来自服务的火灾事件,而不会“污染”$ rootScope

时间:2014-08-11 08:47:11

标签: angularjs angularjs-service

我在angularjs中构建应用程序,我有一个中央通知队列。任何控制器都可以进入队列并消化消息。

我已经建立了一个服务:

angular.module('app').factory('notificationSvc', ['translateSvc', notification]);

function notification(translate) {
    var notificationQ = [];

    var service = {
        add: add,
        getAll: getAll
    };

    return service;

    function add(message, type) {
        notificationQ.push({
            message: message,
            type: type
        });
    }

    function getAll() {
        return notificationQ;
    }
}

(其中一个问题是,notificationQ可以通过调用svc.getAll()[3].message = "I have changed a message";或类似的东西进行不安全的修改。我原本想要一个"仅推送"服务与不可变消息,但这个问题超出了这个问题的范围。)

如果我在控制器中消化此队列,如:

$scope.notifications = svc.getAll();
$scope.current= 0; // currently visible in the panel

并使用它:

<div ng-repeat="notification in notifications" ng-show="$index == current">
    <p>{{notification.message}}</p>
</div>

我可以绑定它,看到它改变,一切都很好。我可以通过更改变量current来循环显示过去的通知。

问题:

当队列获得新元素时,我希望$scope.index变量更改为notifications.length - 1。我该怎么做?

我见过使用$rootScope.$broadcast('notificationsChanged');$scope.$on('notificationsChanged', function() { $scope.index = $scope.notifications.length - 1; });的示例,但我并不喜欢这种模式。

我有一个知道有关该服务的控制器,有直接引用它,但我们使用$rootScope进行通信?其他所有内容都会看到$rootScope,来自不同服务的所有事件都会混乱。

我不能把事件放在服务上吗?类似于服务中的this.$broadcast('notificationsChanged')和控制器中的svc.$on('notificationsChanged', function() { ... });

或者直接观看数据会更清晰吗?如果有,怎么样?我不喜欢这样,因为我没有计划直接暴露整个阵列(我正在计划get(index)方法)它只是发生在我不知道我在做什么的线上发生了很高兴至少某事有效。

2 个答案:

答案 0 :(得分:1)

您可以自己管理活动。例如(未经测试):

function EventManager() {
    var subscribers = [];

    var service = {
        subscribe: subscribe;
        unsubscribe: unsubscribe;
        publish: publish
    }

    return service;

    function subscribe(f) {
        subscribers.push(f);
        return function() { unsubscribe(f); };
    }

    function unsubscribe(f) {
        var index = subscribers.indexOf(f);
        if (index > -1)
            subscribers.splice(index, 1);
    }

    function publish(e) {
        for (var i = 0; i < subscribers.length; i++) {
            subscribers[i](e);
        }
    }
}

function notification(translate) {
    var notificationQ = [];
    var addEvent = new EventManager();

    var service = {
        add: add,
        getAll: getAll,
        onAdded: addEvent.subscribe;
    };

    return service;

    function add(message, type) {
        var notification = {
            message: message,
            type: type
        };
        notificationQ.push(notification);
        addEvent.publish(notification);
    }

    function getAll() {
        return notificationQ;
    }
}

然后,从您的控制器:

...
var unsubscribe = notificationSvc.onAdded(function(n) { /* update */ });

警告:使用此方法,服务将维护对使用subscribe传递给它的订阅者功能的引用,因此您必须使用$scope.$on('$destroy', unsubscribe)

管理订阅

答案 1 :(得分:0)

通知方法肯定会奏效。根据您的实施情况,这将是正确的解决方案。

另一种方法是在控制器中观察通知数组,如下所示:

$scope.$watchCollection('notifications', function(newValue, oldValue) {
    $scope.index = newValue.length - 1;
});

这应该可行,因为您的控制器会直接引用notifications数组,因此可以直接查看更改。

正如runTarm在评论中指出的那样,你也可以直接$观察数组的长度。如果您只对长度变化感兴趣,那么这将是一种更节省内存的方法(因为您不需要观看整个集合):

$scope.$watch('notifications.length', function (newLength) {
    $scope.index = newLength - 1;
});