Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS broadcast from one service not triggering a call to second service

I have defined two AngularJS services ... one is for the YouTube Player API, and other for the YouTube iFrame Data API. They look like this:

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;
}]);

(also note that the 'setPlayerId' function of my player service is called by a custom directive ... but that's not important for my question).

So, here's the issue. I need to ensure that the Player API code is loaded before I set the video id and create the player, which is why I have it broadcasting the 'loadedApi' message. And this works great, if I then in my controller pass a hard-coded video id, like this:

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

However, my video IDs won't be determined until I make an API call with the data api service, so I ALSO have to ensure that the results of that call have come back. And that's where I'm running into problems ... if I do something like this:

      $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
        });
      });

Then the interaction with the data service never happens for some reason. I know the data service works just fine, for when I un-nest it from the $on statement, it returns the api results. But sometimes latency makes it so that the results don't come back fast enough to use them in the player service. Any thoughts on what I can do to make the data search after receiving the message that the player API is ready, but still keep the two services as two separate services (because other controllers only use one or the other, so I don't want them dependent on each other at the service level)?

like image 276
jlmcdonald Avatar asked Aug 16 '13 21:08

jlmcdonald


1 Answers

Figured it out; I had to call $scope.$apply(), like this:

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();
  });
}

Is there anyone who could shed light on why this works, though? $scope.$digest() also works ... but I thought those methods were only used when you need to update bindings because of some javascript code that Angular isn't aware of. Is the nesting I've got here doing that (I wouldn't think it should, as my ytdataapi service is using $http)?

like image 152
jlmcdonald Avatar answered Nov 15 '22 06:11

jlmcdonald