0

In App Maker, I am displaying a table and want to replace table cell data with different text using a data lookup from another table. Assume two tables, Departments and Employees.

  • Departments is two fields, DeptID and DeptDescription.
  • Employees is multiple fields including DeptID.

In the table listing for Employees, I would like to replace the DeptID with the DeptDescription. (The page datasource is Employees. I do not want to set up a relationship between the data models.)

I am guessing I want to do some scripting in the onDataLoad event for the table cell label for DeptID. I have this much so far:

 app.datasources.Departments.query.filters.DeptID._equals = widget.datasource.item.DeptID;
 app.datasources.Departments.newQuery().run();
 widget.text = app.datasources.Departments.item.DeptDescription;

I know this is not correct, but am I close?

Nimantha
  • 6,405
  • 6
  • 28
  • 69
Jeff
  • 35
  • 8
  • 1
    Not sure why you would not want to set up a relation between the tables? It would make that so much simpler. Your current code would only run the query once against the first item in your Employees table and you are mixing client code with server code, so this would not work. I can think of a possible solution but it would take a little bit to gather my thoughts on this. – Markus Malessa Oct 31 '19 at 15:54
  • I am looking for the mechanism to accomplish this type of task because we are converting our entire system to Google Cloud. Establishing a relationship is not an option because we are using MySQL tables that are managed and maintained outside of the apps. AppMaker applies data model changes to the database. A table like Departments would potentially relate to hundreds of other tables and if you did it for everything that had a Department field then multiply that for every Cost Code, Vendor, Employee, part number, etc. that might be used for a dropdown or suggest box on hundreds of apps... – Jeff Oct 31 '19 at 17:24
  • In PHP, I would loop through the main datasource records and write my table rows. I would stop after each record read, do a quick query to another table, and write the Description to the Dept cell. I am looking for how I would accomplish this through the AppMaker interface. – Jeff Oct 31 '19 at 17:27

2 Answers2

2

1 way) Create an aggregate table that joins your tables if you need to bypass using the relations feature. This way you can use sql to join the two tables in the datasource definition

2) if you don't want to make a new table. Change the text from a value binding to "more options"

=getDescription(@datasource.item.DeptId)

and then the code you wrote in a client side script

function getDescription(id){
  google.script.run
  .withSuccessHandler(function successHandler(result){    return result;})
  .withFailureHandler( function failureHandler(e){ console.log(" Failed" +e);})
  .queryValue(id); 

}

server side script:

function queryValue(id){ 
  var query = app.models.Departments.newQuery();
  query.filters.DeptID._equals = id;
  var results = query.run();
  return results[0]["DeptDescription"];
}

that last line might be results[0].DeptDescription

J. G.
  • 1,922
  • 1
  • 11
  • 21
  • the binding for the text value does not have a client side or server side script area, only a script area. I have no way of knowing if that script area is client side or server side. Would the SCRIPTS area under PAGES in the left hand panel be a place to put one of these? – Jeff Oct 31 '19 at 17:49
  • Oh, wait, do you mean I add these two scripts to a data model client/server script area? If so, which one, Employees or Departments? – Jeff Oct 31 '19 at 19:04
  • There are two client script areas, onLoad and onItemChange. I am guessing onItemChange is the place to put it. Will that get the first record or will I need to put it in onLoad too? – Jeff Oct 31 '19 at 19:15
  • The script area over on the left hand side under where you add pages. Add one client script and one server script. – J. G. Oct 31 '19 at 22:22
  • I was finally able to get this to work with some tweaking. The server script was fine as is with results[0].DeptDescription. The client script didn't work exactly as written, but I combined it with a similar script per another answer here - [https://stackoverflow.com/questions/41645111](https://stackoverflow.com/questions/41645111/generating-an-email-list-from-appmaker-database/41646149#41646149) and put it in the onDataLoad event of the label widget. I did bind DeptID to the text of the label and let the script overwrite if the query did not fail. Thanks for all of your help. – Jeff Nov 01 '19 at 20:36
2

This answer is untested, but I wanted to present a possible solution that would not require a lot of DB calls, especially ones that make repeated calls to a server script which might consume a lot of processing time when you do line item calls.

  1. Set up a separate datasource under the Department model. Change the default 'Query Builder' to 'Query Script' and add a parameter of type 'list(number)' or 'list(string)', this should match your Primary Key field type. Uncheck the 'auto load' option.
  2. In your 'Query Script' portion enter the following code:

    query.filters.Id._in = query.parameters.YourParameter;

    return query.run();

  3. Go to your Employees datasource that is supposed to generate your table and find your 'On Load' client script section. In this section enter the following code:

    var departmentsDs = app.datasources.YourDepartmentsDs;

    departmentsDs.properties.YourParameter = datasource.items.map(function(deptIds) {return deptIds.DeptID;});

    departmentDs.load();

  4. Now go the page that contains your table. If you have not already create a label widget do so now. In this label widget for the text binding enter the following:

    @datasources.YourDepartmentsDs.loaded && (@datasources.YourDepartmentsDs.items).map(function(Id){return Id.Id}).indexOf(@widget.datasource.item.DeptID) !== -1 ? @datasources.YourDepartmentDs.items[(@datasources.YourDepartmentsDs.items).map(function(Id){return Id.Id}).indexOf(@widget.datasource.item.DeptID)].DeptDescription : 'Unable to retrieve Dept Description'

As stated this is untested and I wrote the code from memory without App Maker in front of me so it may require some additional tweaking. Going with the first option presented by J.G. would also be a very viable solution though. And I apologize but the code formatter does not seem to be working for me.

Markus Malessa
  • 1,841
  • 2
  • 9
  • 11
  • I love this! I haven't tried loading lookup tables to properties but it sounds brilliant. – J. G. Nov 01 '19 at 17:08
  • I wanted to thank you for the suggestion, but it was a little too complex for me to follow and ended up going with the answer above. – Jeff Nov 01 '19 at 20:41
  • @Jeff The alternative would be to have a datasource for Departments that has it’s limit set to 0. Load this datasource during the app start up script and then you can use the binding in step 4 still. Basically the binding in step 4 acts similar to a vLookup function in Excel/GSheets. I’m guessing that your department table probably doesn’t have hundreds of records, at which point a full load of the table might make this inefficient. As stated previously using a server function to retrieve a record on a table row when you have maybe 25 rows will consume some resources. – Markus Malessa Nov 02 '19 at 14:39
  • I would like to give this a try, but I do not understand the code in step 4. What does the "&&" do? Why are there parenthesis around some sections of the code like right before ".map"? In "function(Id){return Id.Id}", is it 'LD' or 'id' and what does it represent? What is " !== -1 ? "? Is this in a standard programming language syntax or something proprietary to App Maker? Sorry for the rudimentary questions, but I am a main frame programmer having to learn App Maker and it does not have any support, manuals, or courses. The help button in App Maker brings you here. Thanks for your patience. – Jeff Nov 05 '19 at 13:46
  • @Jeff '!== -1' represents the return value if a value was NOT found in an array of values. It would be equivalent to the 'N/A' evaluation in a Vlookup in like Excel where it represents that the value was not found in the lookup table. Also, keep in mind that step 4 is purely a binding like most other bindings work in AM. The '&&' is an 'and' evaluator just like in a standard JS. Basically step 4 is an expression using a combination of bindings and evaluations. The reason some bindings have '()' around them is because that is needed to access some JS array functions like `.map()`. – Markus Malessa Nov 05 '19 at 14:10
  • The binding expression does the following in plain words: If Departments datasource is loaded and Departments datasource contains an item with Deptid that matches Deptid from your Employees for the table row, then return the DeptDescription from the Deptpartment items at that specific index, else the label value is 'Unable to retrieve Description'. – Markus Malessa Nov 05 '19 at 14:13
  • So, this is in javascript! This makes so much more sense now. The only other piece that is a little fuzzy for me is "function(Id){return Id.Id}". Thanks. – Jeff Nov 05 '19 at 14:47
  • Yes, it is in JS and you are feeding in DB values using App Maker bindings. When using JS `.map()` function or any other array function there needs to be a function inside the '()' how to evaluate the array, so 'function' should be straight forward, '(id)' is essentially declaring a variable of 'id' which is just a representation of each item in the array (in case of AM this would be a record/item in the record set returned from the server. The 'Id' was just a place holder, in your case it should probably be 'function(id){return id.DeptID}'. – Markus Malessa Nov 05 '19 at 14:53
  • Holy crap! That worked smooth as silk. I agree with J.G., this is brilliant. It is soooo much quicker than the other method for larger data sets. Thanks so much for your help and patience. – Jeff Nov 05 '19 at 15:15
  • @Jeff Glad you figured out how to make it work in your case. I know it is a bit more difficult to construct such an expression along with data bindings because once you introduce JS array functions unfortunately the binding code auto complete stops working. But as long as there are no security issues to observe this type of approach will work faster every time. – Markus Malessa Nov 05 '19 at 15:26