【问题标题】:AngularJS broadcast from one service not triggering a call to second service来自一项服务的 AngularJS 广播不会触发对第二项服务的调用
【发布时间】:2025-11-22 18:25:01
【问题描述】:

我已经定义了两个 AngularJS 服务……一个用于 YouTube Player API,另一个用于 YouTube iFrame Data API。它们看起来像这样:

angular.module('myApp.services',[]).run(function() {
    var tag = document.createElement('script');
    tag.src = "//www.youtube.com/iframe_api";
    var firstScriptTag = document.getElementsByTagName('script')[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
})
.factory('YtPlayerApi', ['$window', '$rootScope', function ($window, $rootScope) {
    var ytplayer = {"playerId":null,
    "playerObj":null,
    "videoId":null,
    "height":390,
    "width":640};

    $window.onYouTubeIframeAPIReady = function () {
        $rootScope.$broadcast('loadedApi');
    };

    ytplayer.setPlayerId = function(elemId) {
        this.playerId=elemId;
    };

    ytplayer.loadPlayer = function () {
       this.playerObj = new YT.Player(this.playerId, {
            height: this.height,
            width: this.width,
            videoId: this.videoId
        });
    };
    return ytplayer;
}])
.factory('YtDataApi', ['appConfig','$http', function(cfg,$http){
    var _params = {
        key: cfg.youtubeKey
    };
    var api="https://www.googleapis.com/youtube/v3/";
    var yt_resource = {"api":api};
    yt_resource.search = function(query, parameters) {
        var config = {
            params: angular.extend(angular.copy(_params), 
                       {maxResults: 10, 
                        part: "snippet"}, parameters)
            };
        return $http.get(api + "search?q=" + query, config);
    };
    return yt_resource;
}]);

(另请注意,我的播放器服务的“setPlayerId”函数由自定义指令调用......但这对我的问题并不重要)。

所以,这就是问题所在。我需要确保在设置视频 ID 并创建播放器之前加载 Player API 代码,这就是为什么我让它广播“loadedApi”消息的原因。这很好用,如果我在我的控制器中传递一个硬编码的视频 ID,就像这样:

function ReceiverCtrl($scope,$rootScope,$routeParams,ytplayer,ytdataapi) {
  $scope.$on('loadedApi',function () {
    ytplayer.videoId='voNEBqRZmBc';
    ytplayer.loadPlayer();
  });
}

但是,在我使用数据 api 服务进行 API 调用之前,我的视频 ID 不会被确定,因此我还必须确保该调用的结果已经返回。这就是我遇到问题的地方......如果我这样做:

      $scope.$on('loadedApi',function () {
        ytdataapi.search("Mad Men", {'topicId':$routeParams.topicId,
                                     'type':'video',
                                     'order':'viewCount'})
        .success(function(apiresults) { // <-- this never gets triggered
          console.log(apiresults); // <-- likewise, this obviously doesn't either
        });
      });

那么由于某种原因,与数据服务的交互永远不会发生。我知道数据服务工作得很好,因为当我从 $on 语句中取消嵌套它时,它会返回 api 结果。但有时延迟会导致结果返回的速度不够快,无法在播放器服务中使用它们。关于在收到播放器 API 准备就绪的消息后我可以做些什么来进行数据搜索的任何想法,但仍然将这两个服务保持为两个独立的服务(因为其他控制器只使用一个或另一个,所以我不想要它们在服务级别上相互依赖)?

【问题讨论】:

    标签: angularjs youtube-api


    【解决方案1】:

    想通了;我不得不调用 $scope.$apply(),像这样:

    function ReceiverCtrl($scope,$rootScope,$routeParams,ytplayer,ytdataapi) {
      $scope.$on('loadedApi',function () {
        ytdataapi.search("",{'topicId':$routeParams.topicId,'type':'video','maxResults':1,'order':'viewCount'}).success(function(apiresults) {
          ytplayer.videoId=apiresults.items[0].id.videoId;
          ytplayer.loadPlayer();
        });
        $scope.$apply();
      });
    }
    

    但是,有没有人可以阐明为什么会这样? $scope.$digest() 也有效......但我认为这些方法仅在您需要更新绑定时使用,因为 Angular 不知道一些 javascript 代码。我在这里的嵌套是这样做的吗(我认为不应该这样做,因为我的 ytdataapi 服务正在使用 $http)?

    【讨论】:

    • 看起来搜索功能并没有对范围进行任何更改,它只是触发了一个 http 请求。这很奇怪。
    • 您好,感谢您与我们分享您的答案!我不明白的一件事是你如何将你的管播放器绑定到一个 DOM 元素。还有,如何在不重新加载插件的情况下一个接一个地加载多个不同的视频
    • 这是一个 github 存储库,它是这个问题的演变:github.com/jlmcdonald/angular-youtube ... 它包括用于将播放器绑定到 DOM 元素的角度指令。我还没有考虑加载多个视频,但这应该不难......也许我会将它添加到 repo 代码中。