I am trying to use RouteTo function through Dynamics CRM REST Builder Tool. When I am trying to execute this request, getting a data type error.
JSON Sent in request:
{"Target":{"primarykeyid":"{304CEAA4-B748-E811-A950-000D3A3606DE}","#odata.type":"Microsoft.Dynamics.CRM.systemuser"},"QueueItem":{"primarykeyid":"{976af0fa-9712-e911-a981-000d3a360e00}","#odata.type":"Microsoft.Dynamics.CRM.queueitem"}}
Error Message:
{"error":{"code":"0x0","message":"An error occurred while validating input parameters: Microsoft.OData.ODataException: Does not support untyped value in non-open type.\r\n at System.Web.OData.Formatter.Deserialization.DeserializationHelpers.ApplyProperty(ODataProperty property, IEdmStructuredTypeReference resourceType, Object resource, ODataDeserializerProvider deserializerProvider, ODataDeserializerContext readContext)\r\n at System.Web.OData.Formatter.Deserialization.ODataResourceDeserializer.ApplyStructuralProperties(Object resource, ODataResourceWrapper resourceWrapper, IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext)\r\n at Microsoft.Crm.Extensibility.CrmODataEntityDeserializer.ApplyStructuralProperties(Object resource, ODataResourceWrapper resourceWrapper, IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext)\r\n at System.Web.OData.Formatter.Deserialization.ODataResourceDeserializer.ReadResource(ODataResourceWrapper resourceWrapper, IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext)\r\n at System.Web.OData.Formatter.Deserialization.ODataResourceDeserializer.ReadResource(ODataResourceWrapper resourceWrapper, IEdmStructuredTypeReference structuredType, ODataDeserializerContext readContext)\r\n at Microsoft.Crm.Extensibility.ODataV4.CrmODataActionPayloadDeserializer.ReadEntry(ODataDeserializerContext readContext, ODataParameterReader reader, IEdmOperationParameter parameter)\r\n at Microsoft.Crm.Extensibility.ODataV4.CrmODataActionPayloadDeserializer.Read(ODataMessageReader messageReader, Type type, ODataDeserializerContext
I already tried capitalizing the schema names, adding/removing {} brackets around GUIDs.
function route() {
var parameters = {};
var target = {};
target.primarykeyid = "304CEAA4-B748-E811-A950-000D3A3606DE";
target["#odata.type"] = "Microsoft.Dynamics.CRM.systemuser";
parameters.Target = target;
var queueitem = {};
queueitem.primarykeyid = "976af0fa-9712-e911-a981-000d3a360e00";
queueitem["#odata.type"] = "Microsoft.Dynamics.CRM.queueitem";
parameters.QueueItem = queueitem;
var req = new XMLHttpRequest();
req.open("POST", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/RouteTo", false);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.onreadystatechange = function() {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 204) {
//Success - No Return Data - Do Something
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}
};
req.send(JSON.stringify(parameters));
}
This request should be executed successfully.
I believe you want your QueueItem to have a queueitemid property (not a primarykeyid).
var parameters = {};
var target = {};
target.primarykeyid = "304CEAA4-B748-E811-A950-000D3A3606DE";
target["#odata.type"] = "Microsoft.Dynamics.CRM.systemuser";
parameters.Target = target;
var queueitem = {};
queueitem.queueitemid = "976af0fa-9712-e911-a981-000d3a360e00";
queueitem["#odata.type"] = "Microsoft.Dynamics.CRM.queueitem";
parameters.QueueItem = queueitem;
var req = new XMLHttpRequest();
req.open("POST", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/RouteTo", false);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.onreadystatechange = function() {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 204) {
//Success - No Return Data - Do Something
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}
};
req.send(JSON.stringify(parameters));
Related
I have the following Microsoft Dynamics related XMLHttpRequest javascript function, and is encountering issue when attemtping to retrieve the entity attributes of the returned records.
The record managed to be created even though the conditions should have blocked it. It is likely that my following statement caused the issue:
var result1 = results.results[0];
alert("result1: " + result1.id); //Not displayed
function DisableInvalidRecordCreation(context) {
var saveEvent = context.getEventArgs();
var idNumber= Xrm.Page.getAttribute("IDNumber").getValue();
var category= Xrm.Page.getAttribute("Category").getValue();
var id = Xrm.Page.data.entity.getId();
var req = new XMLHttpRequest();
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v9.0/ccc_cases?$select=IDNumber&$filter=IDNumber eq '" + idNumber+ "' and statecode eq 0", false);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\",odata.maxpagesize=1");
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var results = JSON.parse(this.response);
if (results.value.length > 0 && id == "") {
alert(results.value.length); //Displayed as 1
var result1 = results.results[0];
alert("result1: " + result1.id); //Not displayed
for (var i = 0; i < results.entities.length; i++) {
var returned_category= results.entities[i]["Category"];
alert(returned_category); //Not displayed
if (category == 100000003 && returned_category!= 100000003)
{
alert("Invalid record");
saveEvent.preventDefault();
}
}
}
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}
};
req.send();
}
This is happening because in your get request you are selecting only IDNumber field and not the one you desire like Category
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v9.0/ccc_cases?$select=IDNumber&$filter=IDNumber eq '" + idNumber+ "' and statecode eq 0", false);
Also it should not be var result1 = results.results[0];
rather it should be var result1 =results.value[0]
sample code snippet for reference
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var results = JSON.parse(this.response);
for (var i = 0; i < results.value.length; i++) {
var abc = results.value[i]["abc"];
var xyz = results.value[i]["xyz"];
var pqr = results.value[i]["pqr"];
var pqr_formatted = results.value[i]["pqr#OData.Community.Display.V1.FormattedValue"];
}
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}
Is there any example how to call AddListMembersList? I tried with:
let data = {
"MemberIds": [500aa55d-f0c2-e711-80ca-005056bb6cde, 500aa55d-f0c2-e711-80ca-005056bb6cd, 500aa55d-f0c2-e711-80ca-005056bb6cde]
};
http.post(this.baseUrl + 'lists(770aa55d-f0c2-e711-80ca-005056bb6cde)/Microsoft.Dynamics.CRM.AddListMembersList', data);
but I get error:
"error":{
"code":"","message":"Request message has unresolved parameters.","innererror":{
"message":"Request message has unresolved parameters.","type":"Microsoft.Crm.CrmHttpException","stacktrace":"
bei
Microsoft.Crm.Extensibility.OData.CrmODataRoutingConvention.SelectAction(ODataPath
odataPath, HttpControllerContext controllerContext, ILookup`2
actionMap)\r\n bei
System.Web.OData.Routing.ODataActionSelector.SelectAction(HttpControllerContext
controllerContext)\r\n bei
System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext
controllerContext, CancellationToken cancellationToken)\r\n bei
System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()"
Any advice?
Exact answer can be found here: http://butenko.pro/2018/03/01/how-to-add-records-to-marketing-list-using-webapi/
From link adjusted to my example:
var data = {
List: {
listid: "9DEE7D76-611D-E811-A95B-000D3A1087A0",
"#odata.type": "Microsoft.Dynamics.CRM.list"
},
Members: [{
accountid: "6785A521-A2C0-E711-A950-000D3A109280",
"#odata.type": "Microsoft.Dynamics.CRM.account"
},
{
accountid: "3653A521-A2C0-E711-A950-000D3A109280",
"#odata.type": "Microsoft.Dynamics.CRM.account"
}]
};
this.http.post(this.baseUrl + 'AddListMembersList', data);
You are trying to call an action - AddListMembersList. Syntax would be like:
/api/data/v8.2/AddListMembersList
I would recommend you to use CRMRestBuilder to play around, test & understand.
var parameters = {};
var list = {};
list.listid = "00000000-0000-0000-0000-000000000000";
list["#odata.type"] = "Microsoft.Dynamics.CRM.list";
parameters.List = list;
var members1 = {};
members1.contactid = "00000000-0000-0000-0000-000000000000";
members1["#odata.type"] = "Microsoft.Dynamics.CRM.contact";
parameters.Members = [members1];
var req = new XMLHttpRequest();
req.open("POST", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/AddListMembersList", true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.onreadystatechange = function() {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 204) {
//Success - No Return Data - Do Something
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}
};
req.send(JSON.stringify(parameters));
Below is my code snippet. I am trying to fetch all the Referenced Entities from ManyToOne Relationship of Annotation entity. In the result, I am able to get the object but when I'm trying to get the length of it, its giving "undefined". Please provide your valuable suggestions on this and how can we assign the Referenced Entity to a variable from result.
OR
is there any possibility to retrieve all the entities that are associated with Annotation entity, using Web API Call ( Dynamics 365).
function fetchIt()
{
var req = new XMLHttpRequest();
var webAPICall = Xrm.Page.context.getClientUrl() + "/api/data/v8.2/EntityDefinitions(LogicalName='annotation')?$select=LogicalName&$expand=ManyToOneRelationships($select=ReferencedEntity)";
req.open("GET", webAPICall, true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function ()
{
if (this.readyState === 4)
{
req.onreadystatechange = null;
if (this.status === 200) {
var results = JSON.parse(this.response);
alert("results.valuelength: " +results.value.length);
for (var i = 0; i < results.value.length; i++)
{
var referencedEntity = results.value[i]["ReferencedEntity"];
}
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}
};
}
You have two problems
You never send your request. You're missing req.send()
It won't be results.value it will be results.ManyToOneRelationships
Then it will work
Missed a small thing there. Instead of value, to check the length, we need to use ManyToOneRelationships (results.ManyToOneRelationships.length). Thank You.
var req = new XMLHttpRequest();
var webAPICall = Xrm.Page.context.getClientUrl() + "/api/data/v8.2/EntityDefinitions(LogicalName='annotation')?$select=LogicalName&$expand=ManyToOneRelationships($select=ReferencedEntity)";
//alert(webAPICall);
req.open("GET", webAPICall, true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function ()
{
if (this.readyState === 4)
{
req.onreadystatechange = null;
if (this.status === 200)
{
var results = JSON.parse(this.response);
alert("Results length: " + results.ManyToOneRelationships.length);
for (var i = 0; i < results.ManyToOneRelationships.length; i++)
{
var referencedEntity = results.ManyToOneRelationships[i]["ReferencedEntity"];
}
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}
};
req.send();
I need to set a contract lookup field in crm based on the account selected and business unit. This is a many to many relationship in crm .The best way i can think to do this is to create 2 querys/api calls (using crm rest builder) to be able to do this based on the criteria. the first query accesses the intersect table (account-contract table)in order to return all contracts based on the account and the seconds needs to further filter the results by looping through all the results from the first query and do a count off all the contracts that match the business unit selected. the issue im having now is that, i used an array to push all the values from the first query to be able to loop in the second one. However the array i created is not reaching the for loop thus the second query is not executing
function populateContractLookup() {
var buisnessUnitId = getFieldValue("hc_businessunit");
var worksiteId = getFieldValue("hc_worksite")[0].id;
worksiteId = stripCurlies(worksiteId);
if (buisnessUnitId != null) {
buisnessUnitId = stripCurlies(buisnessUnitId[0].id);
var condition = "_hc_businessunit_value eq " + buisnessUnitId + " and";
}
else {
condition = "";
}//to be able to count the how many contracts that would hav gotten populated to contract lookup field
var validcontractid;
var contractCount = 0;
var contractArray = [];
//this query gets all contracts based on account
var req = new XMLHttpRequest();
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/hc_account_contractset?$select=contractid&$filter=accountid eq " + worksiteId, true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var results = JSON.parse(this.response);
// Xrm.Utility.alertDialog(results.value.length);
for (var i = 0; i < results.value.length; i++) {
var contractid = results.value[i]["contractid"];
// Xrm.Utility.alertDialog(contractid);
contractArray.push(contractid);
}
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}// Xrm.Utility.alertDialog(contractArray.length) prints to the screen here
};
req.send();
//query to furthr filter the above query to get all contracts based on the buisness unit
//Xrm.Utility.alertDialog(contractArray.length);//not printing to screen
for (var i = 0; i < contractArray.length; i++) {
Xrm.Utility.alertDialog("were in the loop"); //not reaching this loop
var req = new XMLHttpRequest();
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/contracts?$select=contractid&$filter=contractid eq " + contractArray[i] + " and _hc_businessunit_value eq " + buisnessUnitId, true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var results = JSON.parse(this.response);
Xrm.Utility.alertDialog(results.value.length);
for (var i = 0; i < results.value.length; i++) {
contractCount++;
}
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}
};
req.send();
}
}
.ANy ideas of why i cant access my arrary?
Your first request is asynchronous. The code after your first req.send() is executing immediately, whereas you need the result of your first request to have been returned before any other code executes.
You therefore need to wrap your second request, which is dependent on the result of the first, into a callback function. You then call this callback function in the success callback of your first request.
See this StackOverflow answer for information on callback functions.
As an aside, your second request won't work. You're trying to execute a request once per contract that was retrieved. What you want to do is build your OData filter by iterating over contractArray array and writing '(contractid eq ' + contractArray[i] + ') or' //...
As another aside, consider using a FetchXML aggregate to count records.
Your code might look something like this:
var buisnessUnitId = getFieldValue("hc_businessunit");
var worksiteId = getFieldValue("hc_worksite")[0].id;
function populateContractLookup() {
worksiteId = stripCurlies(worksiteId);
if (buisnessUnitId != null) {
buisnessUnitId = stripCurlies(buisnessUnitId[0].id);
var condition = "_hc_businessunit_value eq " + buisnessUnitId + " and";
}
else {
condition = "";
}//to be able to count the how many contracts that would hav gotten populated to contract lookup field
var validcontractid;
var contractCount = 0;
var contractArray = [];
//this query gets all contracts based on account
var req = new XMLHttpRequest();
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/hc_account_contractset?$select=contractid&$filter=accountid eq " + worksiteId, true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var results = JSON.parse(this.response);
// Xrm.Utility.alertDialog(results.value.length);
for (var i = 0; i < results.value.length; i++) {
var contractid = results.value[i]["contractid"];
// Xrm.Utility.alertDialog(contractid);
contractArray.push(contractid);
}
// Call your callback function.
countContracts(contractArray);
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}// Xrm.Utility.alertDialog(contractArray.length) prints to the screen here
};
req.send();
}
Your callback function (though as I've mentioned above this needs rewriting):
function countContracts(contractArray) {
for (var i = 0; i < contractArray.length; i++) {
Xrm.Utility.alertDialog("were in the loop"); //not reaching this loop
var req = new XMLHttpRequest();
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/contracts?$select=contractid&$filter=contractid eq " + contractArray[i] + " and _hc_businessunit_value eq " + buisnessUnitId, true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"*\"");
req.onreadystatechange = function () {
if (this.readyState === 4) {
req.onreadystatechange = null;
if (this.status === 200) {
var results = JSON.parse(this.response);
Xrm.Utility.alertDialog(results.value.length);
for (var i = 0; i < results.value.length; i++) {
contractCount++;
}
} else {
Xrm.Utility.alertDialog(this.statusText);
}
}
};
req.send();
}
}
You can try switching the first call as synchronous by changing the flag like below & the result ll be available immediately before the second call.
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.2/hc_account_contractset?$select=contractid&$filter=accountid eq " + worksiteId, false);
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 ?
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.
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
});
}
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
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}';
};
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
}
}
};
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();
},