0

THE SITUATION:

I am exploring AngularJS by re-building a former project with it. I am using the angularjs-rails gem version 1.2.16. I have a page were I make an API call that returns an array of objects of music events. CONTROLLER:

d2jiveControllers.controller('VenueResultsCtrl', ['$scope','$http','$routeParams',
  '$compile', '$sce', 'spotifyFactory', function($scope, $http, $routeParams, 
    $compile, $sce, spotifyFactory){
    "use strict";

    var venueId = $routeParams.venueId;

    var baseUrl = 'http://api.songkick.com/api/3.0/venues/';

    var apiKey = '/calendar.json?apikey=************';

    var url = baseUrl + venueId + apiKey  + '&jsoncallback=JSON_CALLBACK' ;

    var init = function(url){
      $http.jsonp(url)
        .success(function (data) {
          $scope.events = data.resultsPage.results.event;
          console.log(data);
        }).
        error(function(){
          console.log('failure');
        });
    };

    $scope.tracks = function(artistName){

      var artistTracks = spotifyFactory.getArtistTracks(artistName);

      var spotifyIframe = $('spotifyIframe');
      $scope.show_tracks = $sce.trustAsHtml("<iframe src='https://embed.spotify.com/?uri=spotify:trackset:Playlist:"+artistTracks.spotifyTracks + "'"+ 
        "&theme=white'width='300' height='300'frameborder='0' allowtransparency='true'></iframe>")
      console.log(artistTracks)
    };

    init(url);
}]);

I list them out using ng-repeat and attach an ng-click to each listing. HTML TEMPLATE:

<div class="eventContainer row" ng-controller="VenueResultsCtrl">
  <div ng-repeat="event in events">
    <h4>
      {{event.displayName}} 
    </h4>
    <p>
      <a href="{{event.uri}}" target="_blank"> Buy Tickets</a>
    </p>
    <div ng-repeat="artist in event.performance">
      <button ng-click="tracks(artist.displayName)">Discover 
        {{artist.displayName}}<br> -- {{artist.billing}}</button><br><br>
      <div ng-bind-html="show_tracks"></div> 
    </div>
  </div>
</div>

On click I want to make another API call to Spotify to get back track IDs that I then place into an iframe. To do this I tried both making the call in a directive and factory:

DIRECTIVE:

d2jive.directive('getSpotifyTracks', [function () { 

//      <div get-spotify-tracks="artist.displayName"></div>


  var spotifyUrl = "http://ws.spotify.com/search/1/track.json?callback=JSON_CALLBACK&q=";

  return {
    restrict: 'AEC',
    scope: {
      artistName: '='
    },
    templateUrl: 'assets/d2jive/templates/artistTracks.html',
    controller: ['$scope', '$http', function($scope, $http){
      $scope.getTracks = function(artistName){
        $http.jsonp(spotifyUrl + encodeURIComponent(artistName))
          .success(function (data) {
            var trackArray = [];
            var tracks = data.tracks.slice(0,9);
            for (var track in tracks){
              grabbedTrack = tracks[track].href.slice(
                14, tracks[track].href.length);
              trackArray.push(grabbedTrack);
            }
            $scope.artistTracks = trackArray;
            console.log(data);
          });
      };
    }],
    link: function(scope, element, attrs, ctrl){
      scope.$watch('artist.displayName', function(displayName){
        if (displayName){
          scope.getTracks(displayName);
        }
      })
    }
  }

}]);

FACTORY::

d2jive.factory('spotifyFactory', ['$http','$q', function($http, $q){

  var factory = {}

  factory.getArtistTracks = function(artistName){

    var tracks = {}

    var spotifyUrl = "http://ws.spotify.com/search/1/track.json?q=";

    var deferred = $q.defer();

    var getTracks = function(artistName){
      $http.get(spotifyUrl + encodeURIComponent(artistName))
        .success(function (data) {
          deferred.resolve(data);
        });
      return deferred.promise;
    };


    // tracks.spotifyTrakcs = getTracks(artistName);
    var spotifyTracks = getTracks(artistName);
    spotifyTracks.then(function(result){
        var trackArray = [];
        var tracks = result.tracks.slice(0,9);
        for (var track in tracks){
          grabbedTrack = tracks[track].href.slice(
            14, tracks[track].href.length);
          trackArray.push(grabbedTrack);
        }  
      tracks.spotifyTracks = trackArray;    
    });

    return tracks;

  }


return factory;


}]);

THE PROBLEM:

I can't find a way to append the iframe HTML to a particular item and not each event that is listed. The directive didn't seem to work because it loaded right away and slowed down the app way too much. That is why I went with a Factory to make the API call to Spotify and append the iframe.

THE GOAL:

On ng-click make API call to Spotify, return the track ID's, insert them into the iframe, and then insert that right below the clicked item not below all of the items.

Any help will be much appreciated! Thanks.

Spencer
  • 47
  • 4

1 Answers1

0

Inside the $watch in the link function of your directive, return early on equality between newVal and oldVal parameters:

link: function(scope, element, attrs, ctrl){
  scope.$watch('artist.displayName', function(displayName, oldVal){
    if (displayName === oldVal) { return }
    if (displayName){
      scope.getTracks(displayName);
    }
  })
}

That should prevent getTracks() from being called as soon as the directive links.

Marc Kline
  • 9,399
  • 1
  • 33
  • 36
  • 1
    btw, you absolutely should use a directive to manipulate the DOM.. you can also inject your factory (which can/should handle the API stuff) into your directive. – Marc Kline May 07 '14 at 18:23
  • I tried your suggestion with $watch and the iframe still loads as the page loads. Thank you for the direction though. I thought using a directive to manipulate the DOM would be the best approach, I just wasn't sure how to avoid the auto load. – Spencer May 07 '14 at 19:34
  • Marck is there a way to load the directive on an ng-click rather than on page load? Thanks! – Spencer May 08 '14 at 19:39
  • Sorry, yes I did misunderstand the problem. You could do something similar to the accepted answer of this question: http://stackoverflow.com/questions/15279244/dynamically-add-directives-on-angularjs In other words, you could dynamically add and compile a directive to another using a ng-click event. – Marc Kline May 08 '14 at 20:16