Met een form genereer ik een spotify json url.
Op submit wil ik twee dingen doen:
- met file_get_contents de content van deze url ophalen
- deze content als input gebruiken voor een javascript functie die vervolgens de json data output in een html table.

Deze twee handelingen lopen niet synchroon. D.w.z. Op submit neemt het javascript niet de gegevens mee die in php bewerkt zijn.
Ik heb geprobeerd om dit probleem op te lossen met json_encode of $_ajax, maar kom hier niet uit. Zou iemand me hiermee kunnen helpen? De code staat hieronder.

html:

<script type="text/javascript" src="spotify.js"></script>
<form id="spotifyform" action="spotifylist.php" method="post">
      <select id="country" name="country">
        <option value="GB">UK</option>
        <option value="US">USA</option>
      </select>
      <select id="interval" name="interval">
        <option value="daily">Daglijst</option>
        <option value="weekly">Weeklijst</option>
      </select>
      <select id="chart" name="chart">
        <option value="most_streamed">Meest gestreamd</option>
        <option value="most_viral">Meest gedeeld</option>
      </select>
      <input type="submit" name="formSubmit" value="Submit"/>
</form>
<div id="spotifylist"></div>

spotify.js:

function loadJSON()
{
    var http_request = new XMLHttpRequest();
    try{
      // Opera 8.0+, Firefox, Chrome, Safari
      http_request = new XMLHttpRequest();
    }catch (e){
      // Internet Explorer Browsers
      try{
         http_request = new ActiveXObject("Msxml2.XMLHTTP");
      }catch (e) {
         try{
            http_request = new ActiveXObject("Microsoft.XMLHTTP");
         }catch (e){
            // Something went wrong
            alert("Your browser broke!");
            return false;
         }
      }
    }

   http_request.open("GET", "spotifylist.php", true);
   http_request.send();
   http_request.onreadystatechange  = function(){
      if (http_request.readyState == 4  )
      {
        // Javascript function JSON.parse to parse JSON data
        var jsonObj = JSON.parse(http_request.responseText);

        // jsonObj variable now contains the data structure and can
        // be accessed as jsonObj.artist_name and jsonObj.track_name.

        HTML = "<table id='chart'> <thead><tr id='row2'><th id='dw'></th><th   id='song'>Artiest</th><th id='song'>Titel</th></tr></thead><tbody>";
        var x=jsonObj.tracks;
        for (i=0;i<x.length;i++)
          { 
        HTML += "<tr id='row1'><td id='dw'>";
        HTML += i+1;
        HTML += "</td><td id='song'>";
        HTML += x.artist_name;
        HTML += "</td><td id='song'>";
        HTML += x.track_name;
        HTML += "</td></tr>";
          }
        HTML += "</tbody></table>";

        document.getElementById("spotifylist").innerHTML = HTML; 

      }
    }
 }

$("#spotifyform").submit(function(){
    loadJSON();
    return false;
});

spotifylist.php
[code]
<?php
if($_POST['formSubmit'] == "Submit")
{
$chart = $_POST['chart'];
$country = $_POST['country'];
$interval = $_POST['interval'];
}

$data_file="http://charts.spotify.com/api/tracks/".$chart."/".$country."/".$interval."/latest";
$url = file_get_contents ($data_file);
echo $url;
?>
Waarom haal je niet rechtstreeks de content op met bijvoorbeeld jQuery.get()? Dat tussenliggende script doet weinig speciaals, die URL zou je ook in JavaScript (zelfs zonder jQuery) kunnen bouwen.
Een volledige javascript oplossing krijgt voor mij ook de voorkeur. Met httprequest lukte dat niet, omdat een httprequest naar een externe website soms wordt geblokkeerd.
Is een jQuery.get() naar een externe website wel altijd toegstaan?
Wat je doet is en blijft toch een vorm van webscraping. Maakt het dan uit hoe je dat doet? :)

Probeer het uit, zou ik zeggen.

Heb je gekeken of spotify hier een API voor heeft? Dat lijkt mij dan (als er zoiets is) de "goede" voordeur voor dit soort zaken.
API: http://charts.spotify.com/docs

Trouwens jQuery.get() komt toch overeen met:
$.ajax({
url: url,
data: data,
success: success,
dataType: dataType
});

Hoe moet je dat dan toepassen op dit voorbeeld?

[size=xsmall]Toevoeging op 25/01/2015 20:10:42:[/size]

Ik kom er niet uit hoe ik jQuery.get() kan toepassen op dit voorbeeld, kan iemand me hierbij helpen?
Okay, dat was de API al.

Nou, vervolgens vraag je die URL op met jQuery.get() of $.ajax() (whatever floats your boat) en verwerk je het resultaat als JSON? Je hebt het tussenliggende PHP-script niet nodig, deze doet hetzelfde als een GET-request via jQuery, ik zou dit niet op twee verschillende plaatsen op twee verschillende manieren doen als het direct op 1 plek kan.

Je bent er al bijna volgens mij, je weet welke URL's je kunt aanroepen, je weet hoe de datastructuur eruit ziet die je terugkrijgt.

Cake?
Het php file heb ik weggedaan.

Maar dit is voor mij nog zeker geen piece of cake hoor. Zou je me kunnen helpen?

[size=xsmall]Toevoeging op 25/01/2015 20:30:14:[/size]

Ik heb nu dit, maar hij doet het helaas niet.
Ik heb vast ergens een fout gemaakt.


// deze functie wordt automatisch aangeroepen als de pagina geladen is.
window.onload = function loadJSON()
{
  // de HTML-elementen waar we een gebeurtenis van willen registreren of waaraan we iets willen wijzigen.
  country2 = document.getElementById("country");
  chart2 = document.getElementById("chart");
  interval2 = document.getElementById("interval");
  button = document.getElementById("go_button");

  // we maken een functie die aangeroepen moet worden als er op de button geklikt wordt
  $("#spotifyform").submit(function()
  {
	// we slaan de gekozen optie van de <select> country op in een variabele met de naam country2
	country = country2.options[country2.selectedIndex].value;
	chart = chart2.options[chart2.selectedIndex].value;
	interval = interval2.options[interval2.selectedIndex].value; 

	var data_file = "http://charts.spotify.com/api/tracks/" + chart + "/" + country + "/" + interval + "/latest";
	
	// haal de data op van de url en sla deze op in de variabele data
  $.ajax({
	url: data_file,
	data: data,
	success: success,
	dataType: "json"
  });
	
	// Javascript function JSON.parse to parse JSON data
	var jsonObj = JSON.parse(data);
	
	// jsonObj variable now contains the data structure and can
	// be accessed as jsonObj.artist_name and jsonObj.track_name.

	HTML = "<table id='chart'> <thead><tr id='row2'><th id='dw'></th><th id='song'>Artiest</th><th id='song'>Titel</th></tr></thead><tbody>";
	var x=jsonObj.tracks;
	for (i=0;i<x.length;i++)
	  { 
	  HTML += "<tr id='row1'><td id='dw'>";
	  HTML += i+1;
	  HTML += "</td><td id='song'>";
	  HTML += x[i].artist_name;
	  HTML += "</td><td id='song'>";
	  HTML += x[i].track_name;
	  HTML += "</td></tr>";
	  }
	HTML += "</tbody></table>";
	 
	document.getElementById("spotifylist").innerHTML = HTML; 
	
  });
}
Dit had ik moeten weten, maar dit ligt wat gecompliceerder want je doet een request van een ander domein, wat normaal vanwege restricties vanwege veiligheid niet (zomaar) kan. Het idee is dat je JSONP gebruikt (in de API documentatie wordt deze hint ook min of meer gegeven), maar ik heb tot nu toe nog geen werkend voorbeeld. Ik kan de data wel binnentrekken maar nog niet gebruiken in mijn jQuery, ik zal even moeten kijken waar dat dan aan ligt...

EDIT: okay, dit was alles behalve simpel lol.

Heb ondertussen ook van alles opgezocht over JSONP, omdat ik daar nooit echt mee gewerkt hebt.

Kort door de bocht zit het als volgt, als ik het goed begrijp: als je cross-site requests doet via AJAX dan moet dit (iig in jQuery) via JSONP. Het idee is dat je het resultaat aan een callback functie voert. Als je in je URL '?callback=?' meegeeft dan genereert jQuery zelf een functienaam. Het voordeel daarvan is dat je deze call (verder) kunt afhandelen met de 'success' parameter.

De server waar je de requests doet moet echter geen JSON teruggeven maar een JavaScript applicatie die een functie-call doet naar de callback. Dus in plaats van [JSON] moet de server <callback>([JSON]) teruggeven.

Het lijkt erop dat alle endpoints BEHALVE de track endpoints gewoon JSON teruggeven, ik weet niet of dat precies de bedoeling is. Als je dan via JSONP zo'n lijstje wilt ophalen wordt je anonieme jQuery functie niet aangeroepen (hier rapporteert de 'error' parameter over: "jQueryXXX_YYY was not called") en dus ook je "success" functie wordt niet uitgevoerd.

Ik weet niet of dit een fout in de API is of bewust zo is gedaan, ik ken de beweegredenen verder niet.

Een werkend stuk code is als volgt - de opgehaalde JSON data zul je zelf moeten doorlopen:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Spotify test</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
</head>

<body>
<script type="text/javascript">
//<![CDATA[
$().ready(function() {
    // stel hier je URL samen
    var url = 'http://charts.spotify.com/api/tracks/most_streamed/global/daily/latest'; // werkt
    // var url = 'http://charts.spotify.com/api/tracks/most_viral/global/weekly/2015-01-11'; // werkt
    // var url = 'http://charts.spotify.com/api/tracks/most_viral/global/weekly/'; // werkt niet

    $.ajax({
        'url':      url + '?callback=?',
        'dataType': 'jsonp',
        'error':    function(xhr, status, error) {
            alert(error.message);
        },
        'success':  function(json) {
            alert(json); // geeft [object Object] terug, omdat het resultaat een JSON object is
            console.log(json); // als je bijvoorbeeld FireBug gebruikt, kun je in je console de structuur bekijken
        }
    }); // $.ajax
}); // ready
//]]>
</script>
</body>
</html>
Thanks! Dat van JSONP is idd nog redelijk lastig.
Nu moeten we het geheel nog even werkend krijgen. Hopelijk is dat niet al te moeilijk meer.
Dit is wat ik nu heb:


<form id="spotifyform" action="spotifylist.php" method="post">
      <select id="country" name="country">
        <option value="GB">UK</option>
        <option value="US">USA</option>
      </select>
      <select id="interval" name="interval">
        <option value="daily">Daglijst</option>
        <option value="weekly">Weeklijst</option>
      </select>
      <select id="chart" name="chart">
        <option value="most_streamed">Meest gestreamd</option>
        <option value="most_viral">Meest gedeeld</option>
      </select>
      <input type="submit" name="formSubmit" value="Submit"/>
</form>
<div id="spotifylist"></div>

<script type="text/javascript">
// deze functie wordt automatisch aangeroepen als de pagina geladen is.
window.onload = function loadJSON()
{
  // de HTML-elementen waar we een gebeurtenis van willen registreren of waaraan we iets willen wijzigen.
  country2 = document.getElementById("country");
  chart2 = document.getElementById("chart");
  interval2 = document.getElementById("interval");
  button = document.getElementById("go_button");

  // we maken een functie die aangeroepen moet worden als er op de button geklikt wordt
  $("#spotifyform").submit(function()
  {
	// we slaan de gekozen optie van de <select> country op in een variabele met de naam country2
	country = country2.options[country2.selectedIndex].value;
	chart = chart2.options[chart2.selectedIndex].value;
	interval = interval2.options[interval2.selectedIndex].value; 

	var url = "http://charts.spotify.com/api/tracks/" + chart + "/" + country + "/" + interval + "/latest";
	
	// haal de data op van de url en sla deze op in de variabele data
	$.ajax({
        'url':      url + '?callback=?',
        'dataType': 'jsonp',
        'error':    function(xhr, status, error) {
            alert(error.message);
        },
        'success':  function(json) {
            alert(json); // geeft [object Object] terug, omdat het resultaat een JSON object is
            console.log(json); // als je bijvoorbeeld FireBug gebruikt, kun je in je console de structuur bekijken
        }
    }); // $.ajax
	
	// Javascript function JSON.parse to parse JSON data
	var jsonObj = JSON.parse(json);
	
	// jsonObj variable now contains the data structure and can
	// be accessed as jsonObj.artist_name and jsonObj.track_name.

	HTML = "<table id='chart'> <thead><tr id='row2'><th id='dw'></th><th id='song'>Artiest</th><th id='song'>Titel</th></tr></thead><tbody>";
	var x=jsonObj.tracks;
	for (i=0;i<x.length;i++)
	  { 
	  HTML += "<tr id='row1'><td id='dw'>";
	  HTML += i+1;
	  HTML += "</td><td id='song'>";
	  HTML += x[ i ].artist_name;
	  HTML += "</td><td id='song'>";
	  HTML += x[ i ].track_name;
	  HTML += "</td></tr>";
	  }
	HTML += "</tbody></table>";
	 
	document.getElementById("spotifylist").innerHTML = HTML; 
	
  }); // form submit
} // window on load
</script>
Je begaat daar een vergissing in je code-structuur, om twee redenen. Hoe leg ik dit uit.

Als je jouw pagina laadt, wordt alle JavaScript uitgevoerd. Dit gebeurt vrijwel direct omdat alles lokaal in je browser wordt verwerkt. De structuur van je script hierboven is abstract gezien zoiets:

<A>
$.ajax({
    ...
    'success': function(data) {
        <B>
    }
});
<C>


Maar terwijl de AJAX-call nog bezig is, wordt tegelijkertijd (parallel) <C> al uitgevoerd, alle "lokale" JavaScript wordt immers direct uitgevoerd. De afhandeling van een AJAX-callback moet altijd (direct of indirect) plaatsvinden binnen een interne callback-functie. De code van <C> moet dus in de 'success' callback uitgevoerd worden. Je "data" is ook (nog) niet beschikbaar ten tijde van het aanroepen (de AJAX call is naar alle waarschijnlijkheid nog bezig met het wachten op een reactie), maar er is nog een andere reden:
Op elk moment in je code zullen bepaalde variabelen bekend zijn ("bestaan"). Dit domein waarbinnen variabelen geldig zijn wordt ook wel "scope" genoemd.
Als je een (callback)functie binnengaat, dan zijn de enige variabelen die "bekend" zijn binnen deze functie de parameters die je meegeeft en de variabelen die je in een hoger gelegen scope hebt gedeclareerd met var ervoor. In jouw bovenstaande code (het <C> gedeelte) bestaat de variabele "json" helemaal niet omdat je in een andere scope zit.

Wat je dus moet doen is het laatste stuk code verplaatsen naar de body van je 'success' callback functie.

EDIT: een simpelere versie van bovenstaande uitleg is wellicht: Je kunt niet direct verder gaan met de verwerking van feedback (<C>) omdat je deze niet direct hebt. Je moet wachten op de reactie van de AJAX-call. Als deze compleet is wordt 'success' pas aangeroepen. Dit kost altijd enige tijd.
Logisch.

Zo moet het dus eigenlijk:?


<A>
$.ajax({
    ...
    'success': function(data) {
        <B>
        <C>
    }
});


Dat heb ik gedaan, maar hij doet het nog steeds niet. Waarschijnlijk zie ik een foutje over het hoofd denk ik.

Reageren