Using image onload field in a report leads to the repeated execution of the javascript and thereby leads to incorrect output

  • 0
  • 2
  • Question
  • Updated 1 year ago
  • In Progress
Using image onload field in a report leads to the repeated execution of the javascript and thereby leads to incorrect output
Photo of Sauda Furkhana

Sauda Furkhana

  • 224 Points 100 badge 2x thumb

Posted 1 year ago

  • 0
  • 2
Photo of Matthew Neil

Matthew Neil

  • 31,438 Points 20k badge 2x thumb
Yes, that is a situation where you need to account for that when you are writing the script, and more importantly the placement of the field that causes the execution of the code.
Photo of Sauda Furkhana

Sauda Furkhana

  • 224 Points 100 badge 2x thumb
Photo of Sauda Furkhana

Sauda Furkhana

  • 224 Points 100 badge 2x thumb
So, how should this be rectified? I just need the output highlighted in blue in each row (screenshot attached above)
Photo of Sauda Furkhana

Sauda Furkhana

  • 224 Points 100 badge 2x thumb
Hi Gaurav! the exampleurl in Dan's Pasties app does have the image onload field on the table home page and it seems to be working fine! so there definitely is a way to eliminate the redundant/incorrect data, and I think probably the piece of code that is joining all the record ids in the query needs to be fixed somehow so that it queries one record at a time rather than joining all of them with a "OR".
Photo of Ⲇanom the ultimate (Dan Diebolt)

Ⲇanom the ultimate (Dan Diebolt), Champion

  • 26,572 Points 20k badge 2x thumb
>Using image onload field in a report leads to the repeated execution of the javascript and thereby leads to incorrect output

The formula for the image onload field is specifically designed to prevent multiple loads of the script file by testing the existence of the global QBU variable. This feature has always been included in the image onload field definition so I suspect you have implemented the IOL incorrectly. To debug post your formula and the shortest code the produces the same behavior.
Photo of Sauda Furkhana

Sauda Furkhana

  • 224 Points 100 badge 2x thumb
Hi Dan! Thank you for the response. Here are the details.

1) My App variables and Formula Field in parent table:
a) iol => <img qbu='module' src='/i/clear2x2.gif' onload="javascript:if(typeof QBU=='undefined'){QBU={};$.getScript(gReqAppDBID+'?a=dbpage&pagename=
b) /iol => &rand='+new Date().getTime())};">
c) Childs2:
[iol] & "module.js" & [/iol]
&
"<div class=QBU_Childs data-rid=" & [Tracking ID#] & "></div>"


2) HTML source of the tag that is getting rendered in the parent report
<tr>
<td  class='FirstColumn'><img qbu='module' src='/i/clear2x2.gif' onload="javascript:if(typeof QBU=='undefined'){QBU={};$.getScript(gReqAppDBID+'?a=dbpage&pagename=module.js&rand='+new Date().getTime())};"><div class=QBU_Childs data-rid=444></div></td>
<td  align=right class='NoWrap'>444</td>
</tr>
<tr>
<td  class='FirstColumn'><img qbu='module' src='/i/clear2x2.gif' onload="javascript:if(typeof QBU=='undefined'){QBU={};$.getScript(gReqAppDBID+'?a=dbpage&pagename=module.js&rand='+new Date().getTime())};"><div class=QBU_Childs data-rid=445></div></td>
<td  align=right class='NoWrap'>445</td>
</tr>

3) Data in my parent/child tables:

Parent Record #444 has 3 children - child1 value: ghi, child2 value: def, child3 value: abc)
Parent Record #445 has 3 children - child1 value: 123, child2 value: 456, child3 value: 789)

Expected data output is to have parent record and reports in the parent table to display:
444 - ghi, def, abc
445 - 123, 456, 789


4) module.js  is same as in https://haversineconsulting.quickbase.com/db/bgcwm2m4g?a=dr&rid=464&_ga=2.235786049.17404487.... Here are the details of the code changes that I tried:

a) I changed dbid, dbidParents,dbidChilds, apptoken, relatedParentFid, relatedParentLabel, clist
- clist is fetching two fields: 1) child unique record id # and 2) data-value that I want to concatenate and display in parent table

b) I modified the following two lines in js file. Please see the screenshot for the output related to Code2 and Code3
    
>> Code1: as it is in Pasties example

var markup = Mustache.render(template, item);
    $("div." + qbuClass + "[data-rid=" + rid + "]").html(markup);

Output: no result within in the parent record and parent table report

>> Code2: using console.log, I saw that rid contained the child record value that I need to display in parent record

var markup = Mustache.render(template,  {comment: rid});
    $("div." + qbuClass).html(markup);

Output:
- always lists the last child value in the individual parent record 
- parent table report has the last value in the loop for both the records (I think this is because I eliminated unique identifier for the div?)

>> Code3: I changed .html to .append in code2

var markup = Mustache.render(template,  {comment: rid});
    $("div." + qbuClass).append(markup);

Output:
- lists the concatenated child values within an individual record
- but I think due to .append(), all the records in the report have the concatenated list of child values for "all" records (I think this is because I eliminated unique identifier for the div?) 


5)   Dan, should we change something in this function as well?

function ridsToQuery(rids, fid) {
    fid = fid || "3";
    return "{" + fid + ".EX." + rids.join("}OR{" + fid + ".EX.") + "}";
}
Photo of Sauda Furkhana

Sauda Furkhana

  • 224 Points 100 badge 2x thumb
Hi Dan! Is the above information sufficient for you to debug the code?
Photo of Sauda Furkhana

Sauda Furkhana

  • 224 Points 100 badge 2x thumb
Hi Dan,

So, I was able to resolve the issue. The report and the individual records in the parent table display the concatenated text correctly.

I have few other issues/questions though:
1) On saving the report, instead of the concatenated text, the report displays the formula code. How can we get the report to save the "concatenated text"?
2) The iol code does not execute when dynamic filters are applied to the report.

Any guidance/input is highly appreciated! Awaiting your response.

Thanks,
Sauda
Photo of Ⲇanom the ultimate (Dan Diebolt)

Ⲇanom the ultimate (Dan Diebolt), Champion

  • 26,572 Points 20k badge 2x thumb
>Awaiting your response.

What a great pun - sir you are a master punmaster. I created a new demo using the new Async / Await feature in JavaScript that concatenates child cast members into the parent movie records.

Movies and Actors
https://haversineconsulting.quickbase.com/db/bm3wfa894?a=td

I will probably create a top level post of this under another title. See here:

Where Can I See More Async / Await?
https://community.quickbase.com/quickbase/topics/where-can-i-see-more-async-await
(Edited)
Photo of Sauda Furkhana

Sauda Furkhana

  • 224 Points 100 badge 2x thumb
Hi Dan,

Thank you for the direction.

1) I looked at the example and it seems that the await/async technique resolve the issues I mentioned.
- I looked at the new code and could not evaluate the piece of code that would query for the specific parent record or set of records in a report? (I was trying to locate something on the lines of ridstoquery() in the original code.)
- I tried to adapt the code and using the word "await" before Promise.resolve does not execute the JS code. Are there any prerequisites (browser related maybe?) to be able to use "await"? (I'm using Google Chrome)

2) I added a new Actor (to the Movie record Dr.No) in your example database and the concatenated text did not get updated. 
- Did I add the Actor record incorrectly?

3) Would it be possible for you to add the brief comments to the different portions of the Pasties code to explain action/input/result for that specific section? 

Thanks,
Sauda
Photo of Ⲇanom the ultimate (Dan Diebolt)

Ⲇanom the ultimate (Dan Diebolt), Champion

  • 26,572 Points 20k badge 2x thumb
1) "I looked at the new code and could not evaluate the piece of code that would query for the specific parent record or set of records in a report?"

My code queried all child records using {qid: 6} for simplicity of the demo. Again, you will have to modify the code to provide the correct context of those child records you want to concatenate to the parent.

2) "I added a new Actor (to the Movie record Dr.No) in your example database and the concatenated text did not get updated."

It shows up when I run the script anew:



3) "Would it be possible for you to add the brief comments ...?"

Sure I was just wallowing away my time watching CNN coverage of IRMA consisting of a bunch of man-in-the-street reporters ignoring their own advice to take cover.

More Notes:

(1) this piece of code simply protects all code within from creating conflicts with QuickBase's global variables:
(function(){
  // rest of script
})();
(2) this piece of code defines a bunch of parameters that would need to be modified if you moved the code to another application
  var dbid = "bm3wfa883";
  var dbidMovies = "bm3wfa894";
  var dbidActors = "bm3wfa9a2";
  var apptoken = "cwk6g85n9e79c5kask3dyd9wi2";
  $.ajaxSetup({data: {apptoken: apptoken}});
(3) this piece of code defines an async function named process() and then calls that function:
 async function process() {
  // definition of async function
  }
  process();

(4) this piece of code wraps a jQuery promise in a native JavaScript promise so it can be awaited:

    var xml1 = await Promise.resolve(
      $.get(dbidActors, {
        act: "API_DoQuery",
        qid: "6"
      })
    );

The await keyword can only be used within an async function. The await keyword causes the function to suspend executing temporarily until the promise returns its result (xml1).

(5) this piece of code (with some console.log{}'s added for debugging) converts the XML response into an array of objects containing the same information:
    var data = $("record", xml1).map(function(index) {
      var charactor = $("character", this).text();
      var actor = $("actor", this).text();
      var relatedMovie = $("related_movie", this).text();
      return {charactor, actor, relatedMovie};
    }).get();
    console.log("\ndata:");
    console.log(JSON.stringify(data2, null, "  "));
(6) this piece of code randomly generates a new delimiter for the concatenated child records to provide a visual indication that the operation was successful:
    var delims = ",./|&#!~"; 
    var delim =  " " + delims[Math.floor(Math.random() * delims.length)] + " ";

(7) this piece of code uses the underscorejs library to reformat that data by (a) grouping child records together, (b) mapping the child records into a concatenated string, and (c) returning an array of objects:
    var data2 = _.chain(data)
      .groupBy("relatedMovie")
      .tap(function(x) {
        console.log("\nafter grouping:");
        console.log(JSON.stringify(x, null, "  "));
       })
      .map(function(data, key) {
        var cast = _.pluck(data, "actor").join(delim);
        return {rid:key, cast: cast};
      })
      .tap(function(x) {
        console.log("\nafter mapping:");
        console.log(JSON.stringify(x, null, "  "));
      })
      .value();
The code above is modified with tap() methods to provide console debugging of each step.

(8) this piece of code logs the final processed data which will be iterated over in the next step:
    console.log("\ndata2:");
    console.log(JSON.stringify(data2, null, "  "));
(9) this piece of code iterates over each element in the data array and performs a API_EditRecord on each element of the array:
    data2.forEach(async function(item) {
      var xml2 = await Promise.resolve(
        $.post(dbidMovies, {
          act: "API_EditRecord",
          rid: item.rid,
          _fid_12: item.cast
        })
      );
We could easily modify the script to concatenate all child records to the parent with one call to API_ImportFromCSV but the whole point of this demo (from my perspective) is to introduce you to Async /Await and to reinforce the idea that this technique allows you to make an arbitrary amount of asynchronous network requests written as if they were synchronous operations.

(10)
this piece of code does a final reload of the page when all the async operations are completed
document.location.reload(true);
(Edited)
Photo of Sauda Furkhana

Sauda Furkhana

  • 224 Points 100 badge 2x thumb
Hi Dan,

Thank you for the inputs. I got the new code working too and have few questions and need your guidance.

1) "My code queried all child records using {qid: 6} for simplicity of the demo." 

Oh, my mistake! It was my oversight to read "qid" as "query". 

My report in child table has "ask user" field to specify the parent record id.  I hard coded the parent record id to test the code.

      $.get(dbidActors, {
        act: "API_DoQuery",
        qid:"1000084",
        nv:"1",
        v0:"7524"
      })

>>>> The concatenated text is appearing in the parent record. However, the code is looping and the execution does not break!

Meaning, in the every iteration, the output is calculated twice for each delimiter. And
This looping behavior is seen in both the "view mode" of the record#7524 and on the parent table report that contains #7524. Why is the execution not stopping when the concatenated text is displayed for #7524 the first time?

>>>>  I stopped the execution after 12 iterations, please see the log file. For 12 iterations, we have output text calculated 24 times?

Please see the txt file the log output



Here is the screenshot of the output displayed in parent record and parent table report.


2) My parent and child tables have 10,000+ records each so I think I will have to execute the code for specific parent record ids ONLY and I'm not sure how to pass the parent record id or ids dynamically in the JS doquery code that uses qid (that is, a report). Can you give me some input on this?

Should I use the following code to extract the list of parent record identifiers (rids) and loop (using "for" or "._chain") through it using v0:rids[i] in DoQuery?
  var rids = $("div.QBU_Childs").map(function() {
    return this.dataset.rid;
  }).get();

 
3) "It shows up when I run the script anew" 
- Can you please clarify what you mean by "run the script anew"? Movies table home page only has the concatenated text field (_fid_12). I don't see the formula text field that invokes the script. How did you run the script for all the records in parent table for the home page to reflect the new concatenated text?


Thanks
Sauda
Photo of Ⲇanom the ultimate (Dan Diebolt)

Ⲇanom the ultimate (Dan Diebolt), Champion

  • 26,272 Points 20k badge 2x thumb
1) "My code queried all child records using {qid: 6} for simplicity of the demo." 
 ... My report in child table has "ask user" ... the code is looping and the execution does not break!

I can't debug this without seeing your exact code. Also, please don't run your "development code" against my demo as I don't want to have to come back and fix it or restore it to the original state. Some of my demos allow a user to add, edit or even delete records to show off the intended functionality and I don't mind small edits such as creating the "Test" actors. But you should create your own application mimicking mine and run your development code and modifications in your own application. It is a challenge creating public demos that use script when there are modifications to an application or deep workarounds.

2)  I'm not sure how to pass the parent record id or ids dynamically in the JS doquery code that uses qid (that is, a report). Can you give me some input on this?

You can pass any number of parameters to process() to generalize what the async function does:
 async function process(qid) {
  // definition of async function
  }
  var qid=7;
  process(qid);
3) "It shows up when I run the script anew"  Can you please clarify what you mean by "run the script anew"? Movies table home page only has the concatenated text field (_fid_12). I don't see the formula text field that invokes the script. 

QuickBase does not have a "concatenate to parent" summary field so this thread (and the prior one) offered workarounds using script. The script has to be run immediately prior to viewing the report as child records may have changed since last viewing the report. So you have to get the scrip to run before viewing the report. This means you probably have to bind the script to a button but I have no idea where you want to place that button as that is a workflow issue unique to how you want to perform your task. Without any mention of your intended workflow I simply started the process from the console by pasting the script.

Undoubtedly you have to place the button (1) on the dashboard, {2) on some fake parent record (3) on a Tasks table record, (4) on some other parent record or perhaps even (5) on a code page.

How to configure a button to run script is a separate consideration - ask about it in a new question. The forum is a better resource for everyone if you ask simple questions that stand on their own, eliminate unnecessary details, and avoid long wandering threads.


(Edited)
Photo of Sauda Furkhana

Sauda Furkhana

  • 224 Points 100 badge 2x thumb
Hi Dan,

Good Morning. Thank you for all the responses! 

1) Please be assured that I'm not executing my test code against your demo tables. I just added two child records to test the functionality. All the changes/r&d is in my copy app. Should I send my code to your email address?

2) I think I'm not asking the question correctly. Let me investigate more on this.
(I need the v0 parameter value to change dynamically to single parent record id or list of parent record ids in the parent table report. I currently hard-coded it to one of the parent table record numbers.)

3) Very clear explanation, thank you! Let me investigate this on my own and I will come back to you if I have questions.

Thanks,
Sauda
Photo of Ⲇanom the ultimate (Dan Diebolt)

Ⲇanom the ultimate (Dan Diebolt), Champion

  • 26,572 Points 20k badge 2x thumb
My script uses API methods and some advanced Async/Await JavaScript capabilities built into modern version of browsers - there is no GUI or page loads other than the final document.location.reload(true).

Using the <ask the user> feature and the associated URL parameters &nv and &v0 is not using script or API methods but rather loading new pages and using redirection features built into early versions of QuickBase during the Jurassic Epoch (ie dinosaurs). Continuing to use these archaic methods will hold you and everyone else back. The same goes for using &rdr in formulas URL.

The reason is simple: Using redirection methods to (1) chain together formula calculations or (2) step through workflow disturbs the entire application because the page reloads.

If you need to solicit user input prior to running a script use a dialog. as demonstrated here:


https://haversineconsulting.quickbase.com/db/bkw2ff3e3?a=q&qid=1


What is the Hylo Technique?
https://community.quickbase.com/quickbase/topics/what-is-the-hylo-technique

THIS IS NOT A TRIVIAL OR A PEDANTIC MATTER ON MY PART. THE USE OF REDIRECTION METHODS TO CHAIN TOGETHER URL FORMULAS OR TO FORCE WORKFLOW STEP EVALUATIONS WILL CONTINUE TO HOLD BACK THE EVOLUTION OF QUICKBASE AND IS PROBABLY ONE OF THE TECHNICAL REASONS QUICKBASE DID A REBOOT OF THEIR PRODUCT DEVELOPMENT STRATEGY.

STOP USING &rdr, &nexturl, &RedirectURL, &rl PARAMETERS AND 302 REDIRECTS
(Edited)