2014-10-01 53 views
1

我正在嘗試爲Pusher(http://pusher.com)編寫AngularJS庫,並且遇到了一些問題,這些問題與我對摘要循環的理解以及它的工作方式有關。我正在寫Pusher JavaScript庫的基本上是一個Angular包裝器。如何將AngularJS服務與摘要循環集成

我面臨的問題是,當Pusher事件觸發並且我的應用程序訂閱了它時,它會收到消息但不更新訂閱的設置範圍。

我此刻的下面的代碼:

angular.module('pusher-angular', []) 

.provider('PusherService', function() { 
    var apiKey = ''; 
    var initOptions = {}; 

    this.setOptions = function (options) { 
    initOptions = options || initOptions; 
    return this; 
    }; 

    this.setToken = function (token) { 
    apiKey = token || apiKey; 
    return this; 
    }; 

    this.$get = ['$window', 
    function ($window) { 
    var pusher = new $window.Pusher(apiKey, initOptions); 
    return pusher; 
    }]; 

}) 

.factory('Pusher', ['$rootScope', '$q', 'PusherService', 'PusherEventsService', 
    function ($rootScope, $q, PusherService, PusherEventsService) { 
    var client = PusherService; 

    return { 
     subscribe: function (channelName) { 
     return client.subscribe(channelName); 
     } 
    } 
    } 
]); 

.controller('ItemListController', ['$scope', 'Pusher', function($scope, Pusher) { 

    $scope.items = []; 

    var channel = Pusher.subscribe('items') 
    channel.bind('new', function(item) { 
    console.log(item); 
    $scope.items.push(item); 
    }) 
}]); 

,並在另一個文件中設置應用起來:

angular.module('myApp', [ 
'pusher-angular' 
]). 
config(['PusherServiceProvider', 
    function(PusherServiceProvider) { 

    PusherServiceProvider 
     .setToken('API KEY') 
     .setOptions({}); 
    } 
]); 

我已經刪除了一些代碼,使其更簡潔。

ItemListController中,當從Pusher收到消息時,$scope.items變量不會更新。

我的問題是,我怎麼能這樣做,當從Pusher收到一條消息,然後觸發一個摘要,以便範圍更新和更改反映在DOM?

編輯:我知道我可以將訂閱回調包裝在$scope.$apply()中,但我不希望每個回調都這樣做。有沒有一種方法可以將其與服務集成?

回答

4

在控制器級別: 角不知道該channel.bind事件,所以你必須給自己揭開序幕週期。

您只需在$scope.items更新後致電$scope.$digest()

.controller('ItemListController', ['$scope', 'Pusher', function($scope, Pusher) { 

    $scope.items = []; 

    var channel = Pusher.subscribe('items') 
    channel.bind('new', function(item) { 
    console.log(item); 
    $scope.items.push(item); 
    $scope.$digest(); // <-- this should be all you need 
    }) 

推裝飾備選:

.provider('PusherService', function() { 
    var apiKey = ''; 
    var initOptions = {}; 

    this.setOptions = function (options) { 
    initOptions = options || initOptions; 
    return this; 
    }; 

    this.setToken = function (token) { 
    apiKey = token || apiKey; 
    return this; 
    }; 

    this.$get = ['$window','$rootScope', 
    function ($window, $rootScope) { 
    var pusher = new $window.Pusher(apiKey, initOptions), 
    oldTrigger = pusher.trigger; // <-- save off the old pusher.trigger 

    pusher.trigger = function decoratedTrigger() { 
     // here we redefine the pusher.trigger to: 

     // 1. run the old trigger and save off the result 
     var result = oldTrigger.apply(pusher, arguments); 

     // 2. kick off the $digest cycle 
     $rootScope.$digest(); 

     // 3. return the result from the the original pusher.trigger 
     return result; 
    }; 

    return pusher; 
    }]; 
+1

有沒有辦法在服務內部完成它,而不是在需要更新實際範圍的Controller中? – hamchapman 2014-10-01 16:24:33

+0

@hamchapman您可以在服務中注入'$ rootScope'並調用'$ rootScope。$ apply()'或'$ rootScope。$ digest()'。您幾乎總是應該調用'$ apply()'來確保所有內容都正確更新。 – m59 2014-10-01 16:31:30

+0

@ m59應該在哪裏調用?當我正在嘗試時,我收到一個錯誤,表示已經有一個應用或摘要操作正在進行(https://docs.angularjs.org/error/$rootScope/inprog?p0=$digest) – hamchapman 2014-10-01 16:37:05

2

我發現我可以做這樣的事情,它的工作原理:

bind: function (eventName, callback) { 
    client.bind(eventName, function() { 
     callback.call(this, arguments[0]); 
     $rootScope.$apply(); 
    }); 
    }, 

    channelBind: function (channelName, eventName, callback) { 
    var channel = client.channel(channelName); 
    channel.bind(eventName, function() { 
     callback.call(this, arguments[0]); 
     $rootScope.$apply(); 
    }) 
    }, 

我不是這個真的很高興,雖然,感覺好像有一件比我想象的更大的東西會讓這個更好。

+0

我只瞥了一下Pusher API。我沒有意識到大多數事件實際上是從服務器觸發的,所以裝飾Pusher.trigger不起作用。這看起來好像比我的答案更正確。你對這個解決方案感到不滿嗎? – 2014-10-02 15:53:09