2012-10-17 23 views
25

AngularJS菜鳥,我的道路上的角啓蒙:)

這裏的情況:

我實現了一個服務「AudioPlayer」內我的模塊「應用」,並像這樣註冊:

app.service('AudioPlayer', function($rootScope) { 

    // ... 

    this.next = function() { 
     // loads the next track in the playlist 
     this.loadTrack(playlist[++playIndex]);  
    }; 

    this.loadTrack = function(track) { 
     // ... loads the track and plays it 
     // broadcast 'trackLoaded' event when done 
     $rootScope.$broadcast('trackLoaded', track); 
    }; 
} 

,這裏是「接收器」控制器(大多爲UI /演示邏輯)

app.controller('PlayerCtrl', function PlayerCtrl($scope, AudioPlayer) { 


    // AudioPlayer broadcasts the event when the track is loaded 

    $scope.$on('trackLoaded', function(event, track) { 
     // assign the loaded track as the 'current' 
     $scope.current = track; 
    }); 

    $scope.next = function() { 
     AudioPlayer.next(); 
    }; 
} 

在我的意見我顯示當前曲目信息,像這樣:

<div ng-controller="PlayerCtrl"> 
    <button ng-click="next()"></button> 
    // ... 
    <p id="info">{{current.title}} by {{current.author}}</p> 
</div> 

下一個()方法是在PlayerCtrl定義,它只是調用的AudioPlayer服務的方法相同。

問題

能正常工作時,有一個人工交互(即當我點擊下按鈕()) - 流程如下:

  1. PlayerCtrl攔截點擊並觸發其自身的next()方法
  2. 這反過來又激發了AudioPlayer.next()方法
  3. 其目的在播放列表中的下軌,並調用loadTrack()方法
  4. loadTrack()$廣播「trackLoaded」事件(發送出軌道本身與它)
  5. 的PlayerCtrl偵聽的廣播事件和軌道正確地
  6. 視圖更新分配給當前對象,顯示當前.title和current.author信息

然而,當在'後臺'的AudioService中調用next()方法時(即軌道結束時),所有從1到5的步驟都會執行發生,但該視圖不會得到有關PlayerCtrl的「當前」對象中的更改的通知。

我可以清楚地看到在PlayerCtrl中分配的新軌道對象,但它好像視圖沒有得到有關更改的通知。我是小白,我不知道這是任何幫助,但我已經試過是隻有在「手冊中的PlayerCtrl

$scope.$watch('current', function(newVal, oldVal) { 
    console.log('Current changed'); 
}) 

這被打印出來,加入了$監視表達式「互動......

同樣,就像我說的,如果我在$監聽者添加的console.log(電流),像這樣:

$scope.$on('trackLoaded', function(event, track) { 
    $scope.current = track; 
    console.log($scope.current); 
}); 

這得到正確隨時打印。

我在做什麼錯?

(ps我使用AudioJS的HTML5音頻播放器,但我不認爲這是應該責怪在這裏...)

回答

21

當你有一個單擊事件$範圍更新,而事件中,你需要使用$應用

$scope.$apply(function() { 
    $scope.current = track; 
}); 
+4

你先生讓我很快樂。不過我發現這樣做總是如此: '$ scope.current = track; $ scope。$ apply(); ' –

+1

沒有想過這樣做 - 很高興知道,歡呼聲。 – Fuzzpedal

+0

實際上,在控制檯中給出了'錯誤:$摘要已經在進行中...... mh? –

7

,因爲它不是安全窺視的摘要內部,最簡單的方法是使用$timeout

$timeout(function() { 
    $scope.current = track; 
}, 0); 

該回調總是在良好的環境中執行。

編輯:其實,這應該被包裹在應用階段的功能是

this.loadTrack = function(track) { 
    // ... loads the track and plays it 
    // broadcast 'trackLoaded' event when done 
    $timeout(function() { $rootScope.$broadcast('trackLoaded', track); }); 
}; 

否則,廣播會得到錯過。

~~~~~~

實際上,另一種可能會更好(至少從一個語義點),它會工作得同樣的內部或摘要週期之外:

$scope.$evalAsync(function (scope) { 
    scope.current = track; 
}); 
  • 相對於$scope.$apply的優勢:您不必知道自己是否處於摘要循環。
  • 相對於$timeout的優勢:您並不是真的想要超時,並且您可以使用更簡單的語法,而不需要額外的0參數。
+1

$ timeout執行$ apply塊中的函數。 來自角度文檔: 用法 $ timeout(fn [,delay] [,invokeApply]); 參數 fn - {function()} - 一個函數,其執行應該被延遲。 delay(optional = 0) - {number =} - 以毫秒爲單位的延遲。 invokeApply(可選= true) - {boolean =} - 如果設置爲false跳過模型髒檢查,否則將調用$ apply塊中的fn。 – TestersGonnaTest

0
// apply changes 
$scope.current = track; 
try { 
if (!$scope.$$phase) { 
    $scope.$apply($scope.current); 
} 
} catch (err) { 
console.log(err); 
} 
0

嘗試一切,它爲我工作與$rootScope.$applyAsync(function() {});