Select ALL or NONE in Join Page

I have a SetupReports form, with fields from the Reports table being on the General page.

My Join page is based on the ReportGroups table which has ReportGroupsKey, ReportKey and StudyGroupKey fields. First field is a generated GUID, and is (I think) immaterial to this question.

I’ve created Action Buttons on the form called AllGroups and NoGroups. Clicking either causes a client-side function to run a server-side function called JoinTableAllOrNoneServer, which is my attempt at a generic function to flip all items in the join form either on or off. My logic was to delete all records in the join table for the particular report, then if I’ve clicked “All” groups, it will add a new record in the join table for the particular report, and each group. So far I’m only testing the first part, which is removing all records in the join table where ReportKey is the particular report being modified. This should be the same as flipping all switches to “Off”, right? But it’s not working. Here is the code from the generic server function:

function JoinTableAllOrNoneServer(five, context, result)  {

    // this function is used with join tables where setup form uses a JOIN page
    // it is called from a client function so form info is available

    // context: { JoinTable: join table name, JoinField1: Field1Name, JoinField1Value: Field1Value, JoinField2: Field2Name,  
    //            RelatedTable: table name, RelatedTableKeyName: key field name in related table, IsSelected: true }

    // first, all records from the join table for context.JoinField1Value are removed
    // if context.IsSelected is true, it adds all records from JoinField2Table into the join table,
    //   using context.JoinField1Value, in essence selecting ALL

    // first clear all records from JoinTable for this Field1
    let sql = 'DELETE FROM ' + context.JoinTable + ' WHERE ' + context.JoinField1 + '=?';
    five.log(sql);
    five.log('field1: ' + context.JoinField1Value);
    let queryResults = five.executeQuery(sql, 0, context.Field1Name);
    
    // if setting all true, add all JoinField2 records to join table
    if(context.IsSelected) {
      
      // sql to cycle all records in second table, writing them into join table
      const sql1 = 'INSERT INTO ' + context.JoinTable + ' (' + context.JoinField1 + ', ' + context.JoinField2 + ')'
      const sql2 = ' SELECT \'' + context.JoinField1Value + '\' AS ' + context.JoinField1 + ', ';
      const sql3 = context.RelatedTableKeyName + ' AS ' + context.JoinField2;
      const sql4 = ' FROM ' + context.RelatedTable;
      five.log(sql1);
      five.log(sql2);
      five.log(sql3);
      five.log(sql4);
      sql = sql1 + sql2 + sql3 + sql4;
      five.log(sql);
      // queryResults = five.executeQuery(sql, 0);//, context.ReportsKey);
    }
    return five.success(result);
}

When I run the code using the inspector, I see log entries with the ReportKey GUID, and the SQL Statement, but it doesn’t do anything.

I think this messes things up, because even though it does nothing, when I flip any switches on the join page, then click Save, I get the following error:

runtime error: invalid memory address or nil pointer dereference

It also logs me off the running session.

I notice now the error happens even if I don’t use the new functionality. If I log on to the runtime, bring up the report, then flip any switches, the error occurs always when I click SAVE.

Any idea what I’ve done wrong here?

Thanks…

Hello,

Would you be able to send the fdf through so I can take a look at it,

Thanks,
Riley.

Hi Riley,

You can use the attached fdf file, or you can go directly into my beta copy on your own server. Either way, Jo should still have the control password and the admin password. If you need either password, please send me a direct email so I can reply with the passwords but not have them on the forum for everyone to see.

Just a bit of clarity: I have 2 forms, SetupReports and GenerateReports.

The Generate reports program is using a StudyGroups page which is build from a DataView. So it shows all study groups, and the ones in the join table are “switched on” in the form. If I switch any on or off, that study group gets its IsSelected field set to true or false. If I click “All Groups” or “No Groups” action buttons, every study group gets its IsSelected field set to true or false. This is working ok so far.

The SetupReports works differently, it uses a join form. Toggling a study group on or off adds or removes it from the join table for that report as expected. I haven’t worked on the “No Groups” button, but I’m working on the “All Groups” button now. My idea was to write client-side code which would call a generic server-side function. That function first removes all records in the ReportGroups join table for that particular report. Then if I’ve selected “All Groups”, it will run a query to add records in the join table for, well, all study groups. This part is commented out, until I can get the function to properly remove groups from the join table. I must have done something wrong, because now even toggling individual study groups on or off causes a run-time error when I save the form.

Please advise if bulk-removing or bulk-adding study groups to the ReportGroups join table for a particular report using SQL code will actually work.

Just to be clear: The SetupReports form is to maintain the join table. The GenerateReports form is to get a list of desired groups, set their IsSelected field to true and then run the report without permanently changing any tables.

Please advise via direct email if you need the passwords.

BrandeisConejo-20240805-190928275513752.fdf (5.0 MB)

Hello,

Please be aware that when importing the fdf I attached, it will overwrite your current version of the app as they have the same database ID. Please back your app up before importing the one I sent through.
The runtime error is an easy fix.

Go to Forms > ReportGroupsJoin > General > Fields > IsSelected > Delete

This IsSelected field is currently not referencing any field within any database so it is causing a run time error when you try to add a group using the corresponding switch.

In regard to the “All Groups” button, I have attached an fdf showing this functionality. I have made it so when “All Groups” are pressed, all current groups within the join table are deleted, and then all groups are added into it. Your function was deleting the groups from the join table correctly, however, was not adding groups in correctly. If you look at the “JoinTableAllOrNoneServer” function in my fdf, I have placed in a comparison of my code to yours. The error in your code was essentially that no primary key was being set to the record when inserting them into the ReportGroups table. To generate a GUID to be used for a primary key, you can use the function five.uuid().

Keep in mind that when you press “All Groups” button, you will need to click into a different form and then back into the “Setup Reports” form to see the changes made (that being all switches next to groups flipped to “on”). To make this a live change, you will need to make use of a Data View.

BrandeisConejo-20240808-020705508706346.fdf (5.0 MB)

Hope this helps!
Let me know if you have any more questions.

Thanks,
Riley.

Thanks Riley!

I also realized that IsSelected was a problem.

I thought a generated field the value would generate itself. Guess I was wrong.

I also tried to remove the primary key field in the ReportGroups and set a primary key on the table of ReportKey + Group key. But then I couldn’t use the Join Page because it seems to need an actual key field, not a primary key INDEX.

So I don’t understand why I can’t have the changes seen immediately but that’s what is needed. So I’ll work around by trying a data view. I’m already using one in the GenerateReport form. The Groups Page on that is NOT a join page.

Could you please answer:

Is it ok to remove a primary key field from the table as long as I create a Primary INDEX based on other fields?

If you can please reply quickly I could resume my project.

Thanks…

Ron Mittelman

Hello,

In Five, our join form does not work the way you would like. It requires a primary key in one field, and then separate fields for foreign keys.

Thanks,
Riley.

Hi Riley,

Thanks for replying so quickly to my question. I guess I didn’t state it as clearly as I should have. I thought I said that I knew it wouldn’t work when using a Join page. I had tried that and it gave me an error.

What I was referring to was the possibility of NOT using a join form, but instead using a Data View. I had already used that in my other form called GenerateReports.

The question is if I’m not trying to use a join page, is it fine to define the table with only 2 fields, then add a primary index which contains those fields?

And one final question for this issue: If I use the same “data view form” for a page on 2 different forms, is there a way to determine which of the 2 forms were active when I click a button causing a function to run? If the data view page is on the SetupReports form, I want to execute particular code. If on the GenerateReports, I want to execute some other code. Both of these sets of code are in the same function that gets run, with IF statement differentiating between which form was active, and which code to run. I would need to have some type of five variable which “knows” what form I was on. Is this possible, and what variable would I use?

Sorry if the question is complex. Please advise if you need more clarification.

Thanks…

Ron Mittelman

Actually, disregard the prior question. I figured it out myself. Now I’m going to standardize my code so it’s generic and use the already existing data view page on both forms.

Thanks so much for all of the help so far!

Hello,

That’s great that you figured it out!

Riley.

Hi Riley,
One thing figured out, another not working.

I realized by using StudyGroups.IsSelected, I’m building a single-user app. So I added a table, SelectedGroups with UserKey and StudyGroupKey as GUID fields, plus a primary index.

I created a client function called InitReportStudyGroups which gets called from GenerateReports form OnShow. This builds a context and calls InitSubformServer server-side function to load the SelectedGroups table based on data in the ReportGroups join table. So that is the part that’s not working. First it clears the table then loads it with study groups that are in the join table:

function InitSubformServer(five, context, result)  {
    
    // context: { Form: xxx, SelectTable: xxx, SelectField1: xxx, SelectVal1: guid, SelectField2: xxx, JoinTable: xxx, 
    //            JoinField1: xxx, JoinVal1: guid, JoinField2: xxx, }

    // clear Selected... table
    let sql = 'DELETE FROM ? WHERE ? = ?';
    let queryResults = five.executeQuery(sql, 0, context.SelectTable, context.SelectField1, context.SelectVal1);

    // load Selected... table with defaults from join table
    // insert into SelectedGroups (UserKey, StudyGroupKey) Select 'alfja;f' As UserKey, 
    //                                joinTable.StudyGroupKey As StudyGroupkey from joinTable where ReportKey = 'xxx'
    sql = 'INSERT INTO ? (?, ?) SELECT \'?\' AS ?, ? AS ? FROM ? WHERE ? = ?';
    
    five.log(sql);
    five.log(context.SelectTable + '(' + context.SelectField1 + ',' + context.SelectField2 + ')');
    five.log('select ' + context.SelectVal1 + ' as ' + context.SelectField1);
    five.log(context.JoinField2 + ' as ' + context.SelectField2);
    five.log('from ' + context.JoinTable);
    five.log('where ' + context.JoinField1 + '=' + context.JoinVal1);
    
    queryResults = five.executeQuery(
        sql, 0, context.SelectTable, context.SelectField1, context.SelectField2, context.SelectVal1, context.SelectField1, 
                context.JoinField2, context.SelectField2, context.JoinTable, context.JoinField1, context.JoinVal1);
   

    return five.success(result);
}

The clearing part works fine, but the loading part doesn’t seem to work. I put a commented SQL statement there just for illustration purposes. Can you see why this doesn’t seem to load the records into the SelectedGroups table?

By the way, SelVal1 in this case is the CurrentUserKey().
Thanks so much…

Progress Report:

When I use the following literal SQL it works:
insert into SelectedGroups (UserKey, StudyGroupKey) Select '10000000-0000-0000-0000-000000000001' as UserKey, StudyGroupKey From ReportGroups where ReportKey = 'e7a36693-79c5-4cb6-baa8-3ccf14e72bd2'

So I guess my fancy use of ?'s is somehow flawed.

Hi Riley,

Here is the complete function that gets run on server-side:

function InitSubformServer(five, context, result)  {
    
    // context: { SelectTable: xxx, SelectField1: xxx, SelectVal1: guid, SelectField2: xxx, JoinTable: xxx, 
    //            JoinField1: xxx, JoinVal1: guid, JoinField2: xxx, }

    // clear Selected... table
    
    // this one doesn't work
    //let sql = 'DELETE FROM ? WHERE ? = \'?\'';
    //let queryResults = five.executeQuery(sql, 0, context.SelectTable, context.SelectField1, context.SelectVal1);
    
    // this one DOES work
    let sql = 'DELETE FROM ' + context.SelectTable + ' WHERE ' + context.SelectField1 + ' = \'' + context.SelectVal1 + '\'';
    let queryResults = five.executeQuery(sql, 0)
    

    // load Selected... table with defaults from join table
    
    // this one doesn't work
    /*sql = 'INSERT INTO ? (?, ?) SELECT \'?\' AS ?, ? AS ? FROM ? WHERE ? = \'?\'';
    queryResults = five.executeQuery(
        sql, 0, context.SelectTable, context.SelectField1, context.SelectField2, context.SelectVal1, context.SelectField1, 
        context.JoinField2, context.SelectField2, context.JoinTable, context.JoinField1, context.JoinVal1);
    */
    
    // this one DOES work
    const sql1 = 'INSERT INTO ' + context.SelectTable + ' (' + context.SelectField1 + ', ' + context.SelectField2 + ') ';
    const sql2 = 'SELECT \'' + context.SelectVal1 + '\' AS ' + context.SelectField1 + ', ' + context.JoinField2 + ' AS ';
    const sql3 = context.SelectField2 + ' FROM ' + context.JoinTable + ' WHERE ' + context.JoinField1 + ' = '; 
    const sql4 = '\'' + context.JoinVal1 + '\'';
    sql = sql1+sql2+sql3+sql4;
    queryResults = five.executeQuery(sql, 0);
    five.log(sql1);
    five.log(sql2);
    five.log(sql3);
    
    return five.success(result);
}

The first SQL clears all records from the SelectedGroups table for the particular user.

The second one adds records into the SelectedGroups table for the user and all StudyGroups that are in the join table for the particular report.

Both queries don’t work unless I hard-code the various values into the SQL statement. I reserched both MySQL and JavaScript docs, and I believe they said you could use “?” for any values, not just the WHERE parameters.

Any ideas what I’m doing wrong in the statements that use all the “?” 's?
Thanks so much for your continuing help with my learning how to use five!

Hello,

Would you be able to send the fdf through when you have a chance and I will take a look at it for you,

Thanks,
Riley.

Here is my fdf file. I do have it working, but not when using ?'s. I can still continue my development, but it seems like it should work in a simpler manner, right?

Thanks…
BrandeisConejo-20240815-222716278103264.fdf (5.1 MB)