consuming API JSon calls through TVJS-tvOS

2020-05-28 10:55发布

问题:

I am trying to play with tvOS, and I have small question regarding handling json call. I have to get some data through an API, let's say for sake of test that I am calling this link

http://query.yahooapis.com/v1/public/yql?q=select%20item%20from%20weather.forecast%20where%20location%3D%223015%22&format=json

I tried to use this function with some modification

function getDocument(url) {
  var templateXHR = new XMLHttpRequest();
  templateXHR.responseType = "json";
  templateXHR.open("GET", url, true);
  templateXHR.send();
  return templateXHR;
}

but didn't work out. Any hints or help ?

If I need to use NodeJS, how can I do that ?

回答1:

This is one that I got working. It's not ideal in many respects, but shows you something to get started with.

function jsonRequest(options) {

  var url = options.url;
  var method = options.method || 'GET';
  var headers = options.headers || {} ;
  var body = options.body || '';
  var callback = options.callback || function(err, data) {
    console.error("options.callback was missing for this request");
  };

  if (!url) {
    throw 'loadURL requires a url argument';
  }

  var xhr = new XMLHttpRequest();
  xhr.responseType = 'json';
  xhr.onreadystatechange = function() {
    try {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
          callback(null, JSON.parse(xhr.responseText));
        } else {
          callback(new Error("Error [" + xhr.status + "] making http request: " + url));
        }
      }
    } catch (err) {
      console.error('Aborting request ' + url + '. Error: ' + err);
      xhr.abort();
      callback(new Error("Error making request to: " + url + " error: " + err));
    }
  };

  xhr.open(method, url, true);

  Object.keys(headers).forEach(function(key) {
    xhr.setRequestHeader(key, headers[key]);
  });

  xhr.send();

  return xhr;
}

And you can call it with the following example:

jsonRequest({
  url: 'https://api.github.com/users/staxmanade/repos',
  callback: function(err, data) {
    console.log(JSON.stringify(data[0], null, ' '));
  }
});

Hope this helps.



回答2:

I tested this one out on the tvOS - works like a charm with jQuery's syntax (basic tests pass):

var $ = {};
$.ajax = function(options) {

  var url = options.url;
  var type = options.type || 'GET';
  var headers = options.headers || {} ;
  var body = options.data || null;
  var timeout = options.timeout || null;
  var success = options.success || function(err, data) {
    console.log("options.success was missing for this request");
  };
  var contentType = options.contentType || 'application/json';
  var error = options.error || function(err, data) {
    console.log("options.error was missing for this request");
  };

  if (!url) {
    throw 'loadURL requires a url argument';
  }

  var xhr = new XMLHttpRequest();
  xhr.responseType = 'json';
  xhr.timeout = timeout;
  xhr.onreadystatechange = function() {
    try {
      if (xhr.readyState === 4) {
        if (xhr.status === 200) {
            if (xhr.responseType === 'json') {
                success(null, xhr.response);
            } else {
                success(null, JSON.parse(xhr.responseText));
            }
        } else {
          success(new Error("Error [" + xhr.status + "] making http request: " + url));
        }
      }
    } catch (err) {
      console.error('Aborting request ' + url + '. Error: ' + err);
      xhr.abort();
      error(new Error("Error making request to: " + url + " error: " + err));
    }
  };

  xhr.open(type, url, true);

  xhr.setRequestHeader("Content-Type", contentType);
  xhr.setRequestHeader("Accept", 'application/json, text/javascript, */*');

  Object.keys(headers).forEach(function(key) {
    xhr.setRequestHeader(key, headers[key]);
  });

  if(!body) {
    xhr.send();
    } else {
        xhr.send(body);
    }

  return xhr;
}

Example queries working on Apple TV:

var testPut = function(){

    $.ajax({
        type: 'PUT',
        url: url,
        success: successFunc,
        error: errFunc,
        dataType: 'json',
        contentType: 'application/json',
        data: data2
    });
}
var testGet = function(){
    $.ajax({
        dataType: 'json',
        url: url,
        success: successFunc,
        error: errFunc,
        timeout: 2000
    });

}

var getLarge = function(){
    $.ajax({
        dataType: 'json',
        url: url,
        success: successFunc,
        error: errFunc,
        timeout: 2000
    });
}


回答3:

Did you call your function in the 'App.onLaunch'

App.onLaunch = function(options) {
  var url = 'http://query.yahooapis.com/v1/public/yql?q=select%20item%20from%20weather.forecast%20where%20location%3D%223015%22&format=json';
  var doc = getDocument(url);
  console.log(doc);
}

Might be worth looking at https://mathiasbynens.be/notes/xhr-responsetype-json



回答4:

I came across this question looking to accomplish the same thing, and was inspired by @JasonJerrett's answer, but found it a bit lacking because in my instance I am using an XML template built in Javascript like this:

// Index.xml.js
var Template = function() { 
  return `very long xml string`;
};

The issue is that you can't perform the XHR request inside the template itself, because the template string will be returned back before the XHR request actually completes (there's no way to return data from inside an asynchronous callback). My solution was to modify the resource loader and perform the XHR request there, prior to calling the template and passing the data into the template function:

ResourceLoader.prototype.loadResource = function(resource, dataEndpoint, callback) {
    var self = this;
    evaluateScripts([resource], function(success) {
        if (success) {
            // Here's the magic. Perform the API call and once it's complete,
            // call template constructor and pass in API data
            self.getJSON(dataEndpoint, function(data) {
                var resource = Template.call(self, data);
                callback.call(self, resource);
            });
        } else {
            var title = "Failed to load resources",
                description = `There was an error attempting to load the resource. \n\n Please try again later.`,
                alert = createAlert(title, description);

            Presenter.removeLoadingIndicator();

            navigationDocument.presentModal(alert);
        }
    });
}

// From: https://mathiasbynens.be/notes/xhr-responsetype-json
ResourceLoader.prototype.getJSON = function(url, successHandler, errorHandler) {
  var xhr = new XMLHttpRequest();
  xhr.open('get', url, true);
  xhr.onreadystatechange = function() {
    var status;
    var data;
    if (xhr.readyState == 4) { 
      status = xhr.status;
      if (status == 200) {
        data = JSON.parse(xhr.responseText);
        successHandler && successHandler(data);
      } else {
        errorHandler && errorHandler(status);
      }
    }
  };
  xhr.send();
};

Then the template function needs to be modified to accept the incoming API data as a parameter:

// Index.xml.js
var Template = function(data) { 
    return 'really long xml string with injected ${data}';
};


回答5:

You need to implement the onreadystatechange event on the XHR object to handle the response:

templateXHR.onreadystatechange = function() {
  var status;
  var data;
  if (templateXHR.readyState == 4) { //request finished and response is ready
    status = templateXHR.status;
    if (status == 200) {
      data = JSON.parse(templateXHR.responseText);
      // pass the data to a handler
    } else {
      // handle the error
    }
  }
};


回答6:

If you want to call the request on app launch, just add in application.js:

App.onLaunch = function(options) {
  var javascriptFiles = [
    `${options.BASEURL}js/resourceLoader.js`, 
    `${options.BASEURL}js/presenter.js`
  ];

evaluateScripts(javascriptFiles, function(success) {
 if(success) {
  resourceLoader = new ResourceLoader(options.BASEURL);
  var index = resourceLoader.loadResource(`${options.BASEURL}templates/weatherTemplate.xml.js`, function(resource) {

    var doc = Presenter.makeDocument(resource);

    doc.addEventListener("select", Presenter.load.bind(Presenter));

    doc.addEventListener('load', Presenter.request);

    navigationDocument.pushDocument(doc);

  });
} else {
  var errorDoc = createAlert("Evaluate Scripts Error", "Error attempting to evaluate external JavaScript files.");
  navigationDocument.presentModal(errorDoc);
}

}); }

In presenter.js add a method:

request: function() {

  var xmlhttp = new XMLHttpRequest() , method = 'GET' , url = 'your Api url';
  xmlhttp.open( method , url , true );
  xmlhttp.onreadystatechange = function () {
    var status;
    var data;
    if (xmlhttp.readyState == 4) {
      status = xmlhttp.status;
      if (status == 200) {
        data = JSON.parse(xmlhttp.responseText);
        console.log(data);
      } else {
        var errorDoc = createAlert("Evaluate Scripts Error", "Error attempting to evaluate external JavaScript files.");
        navigationDocument.presentModal(errorDoc);
      }
    }
  };
  xmlhttp.send();
},