Ephemeral Ephemeral - 3 months ago 15
Javascript Question

Delay directive/div until scope value available

I have a custom directive for soundcloud that requires the soundcloud url. The soundcloud url is fetched from the database through the $http service, however, the div for the soundcloud custom directive is loaded and requires the value of the soundcloud url before it is even defined.

The Plangular Directive Code I got is here:
https://github.com/jxnblk/plangular/blob/master/src/plangular.js *I did not develop this

This is my HTML code:

<div plangular="{{soundcloud}}">
<button ng-click="playPause()">Play/Pause</button>
<progress ng-value="currentTime / duration || 0">
{{ currentTime / duration || 0 }}
</progress>
</div>


And this is the Angular Code:

displaySong.controller('song', ['$scope', '$http', 'fetchSong', function($scope, $http, fetchSong) {
$scope.songID
$scope.songName;

//Controller properties
$scope.songPromise; //The song promise for fetching

$scope.init = function(songID, userID) {
$scope.songID = songID;
$scope.userID = userID;
$scope.songPromise = $http({
method: "post",
url: fetchSong,
data: {
song_id: $scope.songID
},
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).then(function(successResponse) {
console.log('Successfully fetched song');
console.log(successResponse);
var song = successResponse.data;
$scope.songID = song.song_id;
$scope.songName = song.song_name;
$scope.songType = song.song_type;
$scope.songEmbed = song.song_embed;
$scope.soundcloud = song.song_embed;
}, function(errorResponse) {
console.log('Error fetching');
$scope.songID = null;
});
};
}]);


I know it's a problem with the asynchronous nature because when I add this line in the beginning of my song controller:

$scope.soundcloud = "https://soundcloud.com/jshigley/shine";


It works perfectly fine. I've also noticed that when I spam the play/pause button that DOES come up from the directive, I get multiple console errors of "HTTP 404 Not Found", which leads me to believe it's trying to find a track of undefined url

Since it's a div directive and not a function call I can't use promises such as chaining a then to my $scope.songPromise. I've thought of putting it into a controller and having the controller do something like $timeout for 5 seconds, but I don't think this delays the execution of the DOM.

The soundcloud URL DOES end up getting loaded, but it remains undefined in the eyes of the plangular directive (I've actually encountered lots of these problems with bad timing of loading scope and directives). Any Angular Wizards willing to teach me how to tame the asynchronous nature of AngularJS?

Answer

You can use $watch in the custom directive to watch when url attributes is changed. In

    link: function(scope, el, attr) {

change from

if (src) {
    resolve({ url: src, client_id: client_id }, function(err, res) {
      if (err) { console.error(err); }
      scope.$apply(function() {
        scope.track = createSrc(res);
        if (Array.isArray(res)) {
          scope.tracks = res.map(function(track) {
            return createSrc(track);
          });
        } else if (res.tracks) {
          scope.playlist = res;
          scope.tracks = res.tracks.map(function(track) {
            return createSrc(track);
          });
        }
      });
    });
  }

to

 scope.$watch('attr.plangular', function(newVal) {

    resolve({ url: attr.plangular, client_id: client_id }, function(err, res) {
      if (err) { console.error(err); }
      scope.$apply(function() {
        scope.track = createSrc(res);
        if (Array.isArray(res)) {
          scope.tracks = res.map(function(track) {
            return createSrc(track);
          });
        } else if (res.tracks) {
          scope.playlist = res;
          scope.tracks = res.tracks.map(function(track) {
            return createSrc(track);
          });
        }
      });
    });
 }, true);

If you dont want to change the directive then you might want to use ng-if to load that plangular div only when you get the url.

<div plangular="{{soundcloud}}" ng-if="haveurl">

and in the angular code :

}).then(function(successResponse) {
        console.log('Successfully fetched song');
        console.log(successResponse);
        $scope.haveurl = true;
Comments