25

AngularJS noobは、AngularEnlightenmentへの道のりです:)

状況は次のとおりです。

モジュール「アプリ」内にサービス「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>

next()メソッドはPlayerCtrlで定義され、AudioPlayerサービスで同じメソッドを呼び出すだけです。

問題

これは、手動操作がある場合(つまり、next()ボタンをクリックした場合)に正常に機能します。フローは次のとおりです。

  1. PlayerCtrlはクリックをインターセプトし、独自のnext()メソッドを起動します
  2. 次に、AudioPlayer.next()メソッドを起動します
  3. プレイリスト内の次のトラックを探し、loadTrack()メソッドを呼び出します
  4. loadTrack()$ broadcasts'trackLoaded'イベント(トラック自体を送信します)
  5. PlayerCtrlはブロードキャストイベントをリッスンし、トラックを現在のオブジェクトに割り当てます
  6. ビューが正しく更新され、current.titleとcurrent.authorの情報が表示されます

ただし、「バックグラウンド」でAudioService内からnext()メソッドが呼び出された場合(つまり、トラックが終了した場合)、1から5までのすべてのステップが実行されますが、ビューには変更が通知されません。 PlayerCtrlの「現在の」オブジェクト内。

PlayerCtrlで割り当てられている新しいトラックオブジェクトをはっきりと見ることができますが、ビューに変更が通知されないようです。私は初心者で、これが役立つかどうかはわかりませんが、私が試したのは、PlayerCtrlに$watch式を追加することです。

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

これは、「手動」インタラクション中にのみ印刷されます。

繰り返しますが、私が言ったように、次のように$ onリスナーにconsole.log(current)を追加すると、次のようになります。

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

これは常に正しく印刷されます。

私は何が間違っているのですか?

(ps私はHTML5オーディオプレーヤーにAudioJSを使用していますが、これがここでの責任ではないと思います...)

4

4 に答える 4

21

クリック イベントがあると、$scope が更新されます。イベントがない場合は、$apply を使用する必要があります

$scope.$apply(function () {
    $scope.current = track;
});
于 2012-10-17T13:28:49.907 に答える
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パラメーターなしでより単純な構文を取得できます。
于 2013-04-29T09:04:27.723 に答える
0
// apply changes
$scope.current = track;
try {
 if (!$scope.$$phase) {
  $scope.$apply($scope.current);
 }
} catch (err) {
 console.log(err);
}
于 2014-02-12T10:43:32.700 に答える
0

すべてを試してみましたが、私にとってはうまくいきました$rootScope.$applyAsync(function() {});

于 2016-03-22T18:27:21.860 に答える