0

On my website is a player that shows what song I'm currently listing to. However in order to update te song displayed, I had te refresh the page.

As for now, I want the code to check an external json file. It should check the songname and when a change is detected, it should update the player on the website.

Unfortunately, I'm hitting a roadblock here.

First I tried using the $interval function, while this did work, it would refresh the <div> like refreshing a page.

I tried the scope.$watch function, but it doesn't seem to work. It looks like the $watch is only triggering once when the page is loading. This could be due to the scope not being a listener. However, when I try to add the listener, I'd need to change the module since it's a directive...

angular.module('lastfm-nowplaying', [])
    .directive('lastfmnowplaying', ['uiCreation', 'lastFmAPI', 'lastFmParser', '$http', function (uiCreation, lastFmAPI, lastFmParser, $http) {
        var link = function (scope, element, attrs) {

            $http({
                method: 'GET',
                url: 'https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=--NAME--&api_key=--API--&format=json&limit=1'
            }).then(function (songdata) {
                var currentsongs = songdata.data.recenttracks.track[0].name;
                console.log(songdata);
                console.log(currentsongs);
            }, function (error) {
                //oopsie
            });

            scope.$watch('currentsongs', function (value) {
                load(value);
                console.log("Change detected");
            }, true);

            var load = function () {
                var latestTrack;

                if (scope.config) {

                    if (scope.config.apiKey) {

                        lastFmAPI.getLatestScrobbles(scope.config).then(function (data) {

                            latestTrack = lastFmParser.getLatestTrack(data);
                            angular.element(element).addClass('lastfm-nowplaying');
                            uiCreation.create(element[0], scope.config.containerClass, latestTrack);
                        }, function (reason) {
                            //Last.fm failure
                        });
                    } else {
                        var latestTrack = {
                            title: scope.config.title,
                            artist: scope.config.artist,
                            largeImgUrl: scope.config.imgUrl,
                            xLargeImgUrl: scope.config.backgroundImgUrl,
                        }
                        angular.element(element).addClass('lastfm-nowplaying');
                        uiCreation.create(element[0], scope.config.containerClass, latestTrack);
                    }

                }
            }


        };

        return {
            scope: {
                config: '=config'
            },
            link: link
        };
  }])

HTML

<body ng-app="app" ng-controller="mainCtrl">
    <div lastfmnowplaying config="lastFmConfig"></div>
</body>

So through the console I can call the json file and get the name of the song, but I can't update the player by using scope.$watch, it only fires once(?)

I only know the basics of Angular and thus I'm not capable of solving this on my own anytime soon.

georgeawg
  • 48,608
  • 13
  • 72
  • 95
Niels
  • 33
  • 6
  • In your [previous question](https://stackoverflow.com/q/57337849/5535245) the watcher was watching a scope variable which was bound with an attribute binding to a parent controller variable. Now the watcher is watching a local scope variable and I don't see code assigning anything to that variable. – georgeawg Aug 04 '19 at 19:29
  • Hi, I made a [codepen](https://codepen.io/anon/pen/BXmKLM). Maybe this gives you a clear view of what's happening. As said, I'm completely new to angular and I'm getting a feeling that I'm very close to solving it, but I can't do it on my own, sadly – Niels Aug 04 '19 at 20:01
  • The code is awkwardly factored. The directive should simply display the track info that the controller specifies.The main controller should poll the API and update the specified track when the name of the song is different from the previous name. – georgeawg Aug 04 '19 at 21:57
  • Yes, you're totally right! This isn't fully my code, so I'm (trying) to adjust it to my liking. I've just got no idea what the best option is to update the track automatically. I don't expect you to come up with a fully working code all of the sudden (though, heck, that'd be awesome!). Any tips/leads? – Niels Aug 04 '19 at 22:38

2 Answers2

0

Here is a main controller which polls an API every 6 seconds:

app.controller("mainCtrl", function($scope, $interval, service) {
    service.getLastTrack.then(function (track) {
        $scope.latestTrack = track;
    });
    $interval(getNextTrack, 6000);

    function getNextTrack() {
        service.getLastTrack.then(function (track) {
            if ($scope.latestTrack.name != track.name) {
                $scope.latestTrack = track;
            };
        });
    }
})

Usage:

<body ng-app="app" ng-controller="mainCtrl">
    <div lastfmnowplaying config="latestTrack"></div>
</body>

This is an example of how to poll an API and update the config of a direcive when the a property of the data changes.

For more information, see

georgeawg
  • 48,608
  • 13
  • 72
  • 95
  • Hey George, thanks a bunch for your help! I tried adjusting the code to your model, but since it's, like you said "awkwardly factored", I can't seem to work it out, without getting into a new wave of errors. I had a different idea, which I'm testing out right now. I left it as a comment under bryan60's reply. – Niels Aug 05 '19 at 09:10
0

the reason watch isn't working is because currentsongs isn't a directive scope variable. it's scoped to your http handler. do this instead:

        scope.currentsongs = '';

        // start watching prior to http call just in case?
        scope.$watch('currentsongs', function (value) {
            load(value);
            console.log("Change detected");
        }, true);

        $http({
            method: 'GET',
            url: 'https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=--NAME--&api_key=--API--&format=json&limit=1'
        }).then(function (songdata) {
            scope.currentsongs = songdata.data.recenttracks.track[0].name;
            console.log(songdata);
            console.log(scope.currentsongs);
        }, function (error) {
            //oopsie
        });
bryan60
  • 28,215
  • 4
  • 48
  • 65
  • Hi Bryan, thanks a lot for your suggested code. Sadly, it don't seem to be working. I'm getting a `reference error: currentsong is not defined`. Maybe you could fork my [codepen](https://codepen.io/anon/pen/BXmKLM) and put some suggestion in it? I was thinking of trying an if statement which reads the name of the currentsong and latest song, if those differ the song will update. This is done through the use of $interval. – Niels Aug 05 '19 at 09:06
  • it was an error in your logging statement, code is updated – bryan60 Aug 05 '19 at 13:03
  • Thank! No errors this time :D However, the console.log("change detected"); is only firing once. Which means that the scope.$watch is not updating when a change is detected(?) Any guess why this happens? (updated the codepen) – Niels Aug 05 '19 at 14:31