Js api


(Niraj Sachania) #1

Hello!

 

Trying to create a page for a department to easily manage content for a specific metadata on a set of pages.

 

Right now, just trying to produce a listing which pulls the course name in H2 and Entry Criteria underneath. Sure it can be done via asset listing, and new value could be saved by trigger, but curious to play around with the JS API.

 

I'm supplying the root id to js_api.getChildren, which fetches attributes for all courses. I need to pick up the asset-id to then fetch the metadata – stuck on this bit.

 

Code below results in a listing of Course names. Entry criteria returns 'undefined' for all courses. However, console.log line does retrieve the content, so just need help getting this content on the page.

function printCourses(course) {
  var msg = '';
    for (var i in course) {
      msg += "<h2>" + course[i].name + "</h2>";
      js_api.getMetadata({
          "asset_id": course[i].id,
          "dataCallback": function(data) {
               console.log(data.entry_criteria);
               msg += data.entry_criteria;
          }
      });
    }
  var content = document.getElementById("asset_area");
  content.innerHTML = msg;
}

js_api.getChildren({
   “asset_id”:2231,
   “levels”:1,
   “type_codes”:[
      “page_standard”
   ],
   “link_types”:[
      “SQ_LINK_TYPE_2”
   ],
   “get_attributes”:1,
   “dataCallback”: printCourses
})


(Benjamin Pearson) #2

Could it be a timing issue? Have you tried putting the lines that set the message inside the callback?


(Chris Horikx) #3
function printCourses(course) {
  var msg = '';
    for (var i in course) {
      msg += "<h2>" + course[i].name + "</h2>";
      js_api.getMetadata({
          "asset_id": course[i].id,
          "dataCallback": function(data) {
               console.log(data.entry_criteria);
               msg += data.entry_criteria;
          }
      });
    }
  var content = document.getElementById("asset_area");
  content.innerHTML = msg;
}

Definitely a timing issue Ben.

 

Can't put it in the callback though because its a for loop. Perhaps try 

 
function printCourses(course) {
  var msg = '';
  var content = document.getElementById("asset_area");
    for (var i in course) {
      msg += "<h2>" + course[i].name + "</h2>";
      js_api.getMetadata({
          "asset_id": course[i].id,
          "dataCallback": function(data) {
               console.log(data.entry_criteria);
               msg += data.entry_criteria;
               content.innerHTML += msg;
      }
      });
    }

(Chris Horikx) #4

 

Definitely a timing issue Ben.

 

 

 

Because its an AJAX request, it will execute the callback only after the request has returned, it will execute code as beneath the callback before the request has returned


(Niraj Sachania) #5

Thanks Ben and Chris. I learnt something!

 

Rearranging the lines does bring up all the required content. However, as Chris anticipated.. all the headings get listed first, followed by the metadata content at the bottom of the page.

 

I attempted moving the headings line (msg += "<h2>" + course[i].name + "</h2>";) within the callback. That does move the metadata text under the heading, but all the headings are the same, ie the name of the last course.

 

In trying to find how to process ajax requests synchronously, I came across examples where blocks are chained. That's not applicable here as the request is within a loop, not a series of requests.

 

Anyone got an idea on how to wait for the callback before continuing the loop?


(Anthony) #6

A few thoughts... I dont think there really is a way to make AJAX synchronous, cos being asynch is really the whole point of the exercise in most cases. I suppose if you think carefully about variable scope you could possibly set a value within your callback and then outside of the callback set a timer  to periodically check whether the value has been changed to indicate the callback is done. I think it would get messy though, and probably unreliable. Interested if anyone else has differnet ideas.

 

But I'm not sure why you wouldnt want to use an asset list? One technique I have used successfully is to format an asset list to generate my data in JSON format, then nest that into my page at the appropriate place in my script. That way I get the ease of extracting the data via the listing, but the benefit of having it in a format that JS can then work with.


(Bart Banda) #7

Have you tried batching with the JS API? Your first call would still be getChildren, but then from that array result, create a bach request object using a for loop, and then after the loop, call the JS API batch request to getMetadata for all the asset ids in the json object array.

 

http://manuals.matrix.squizsuite.net/web-services/chapters/javascript-api#Batching-Requests


(Joel Porgand) #8
 

A few thoughts... I dont think there really is a way to make AJAX synchronous, cos being asynch is really the whole point of the exercise in most cases. 

 

 

Making ajax synchronous is possible (despite the name), but bad practice. To do it in the JS API you'd probably have to make some code modifications.

 

 

 

Anyone got an idea on how to wait for the callback before continuing the loop?

 

instead of using a for loop you could try using recursion - create a function & call it again from the callback of your request once it has finished

 

Both of these options are bad though as they will result in long load times as you're requesting your data one item at a time. Use the right tool for the job (an asset listing). 


(Chris Horikx) #9

For scripting sometimes it is necessary to go synchronous, especially when you are batching. I've not used the JS Api batching functionality but most of the time my jsapi calls are nested so deep, I don't know how it could work.

 

Making ajax synchronous is possible (despite the name), but bad practice. To do it in the JS API you'd probably have to make some code modifications.

 

 

instead of using a for loop you could try using recursion - create a function & call it again from the callback of your request once it has finished

 

Both of these options are bad though as they will result in long load times as you're requesting your data one item at a time. Use the right tool for the job (an asset listing). 

 

 

I've used recursion a few times, here is a quick example which I'm sure would be a great example for batching but still a simple one:

var aUnderConstructionIDs = [
    123, 456, 789
];

var popAnother = function() {
console.log("Working on: " + aUnderConstructionIDs[aUnderConstructionIDs.length - 1]);
js_api.setAssetStatus({
“asset_id”:aUnderConstructionIDs.pop(),
“status”:16,
“dataCallback”:function() {
if (aUnderConstructionIDs.length > 0) {
popAnother();
}
}
});
}

if (aUnderConstructionIDs.length > 0) {
popAnother();
}

Also, I've induced synchronous ajax by using jQuery.Deferred (or other javascript promises libraries), that gets a little more complicated. But feel free to read up on it: http://api.jquery.com/jquery.deferred/.