Built a table using java in a view that uses knack CSS then creates connected records

// Knack show and tell
// Code by Jeff Christie
// jeff.christie@shaw.ca
// August 18, 2018

// This code allows you to find records in an object and filter them down to only the ones you need.
// Then it will show the results in a table that looks exactly like a table in your knack app by
// using your existing knack style settings. The table will have checkboxes at the start of each row
// (I removed the check all box in the header but could add it back if needed) and the code will place
// a nicely formatted button (in the colors of my app, so you will need to change the color code of the button)
// at the bottom of the table which when clicked, will trigger events to insert new data back into related
// objects. In my situation, I have one scene with two views in it. The first view is a details view that
// I use to get the record ID number of the player I want to work with. The second view is a blank RTF view
// that I need to use to add the table code to it.

// My app is a sports registration app, this use case stems from a knack view that has a table of all players connected
// to the guardian that is logged in. In my app, when a guardian logs in, I show them a scene of information, including
// a Knack table with their connected players on it.
// On the table showing all the guardian's players, I used knack to add a link to view "details" (view details of the player).
// On the view Knack made I used the knack UI to make the player details view look pretty, then I added another
// blank RTF view into the scene so that I could use it to insert new HTML code into.
// The idea behind this use case is that I am going to show a list of available sports the player
// is eligble for in a table and then allow them to check off the rows of the sports they want to register in.
// Once they check off the rows and click the register button, I need to insert new records into a registration
// object back in knack. There were a lot of available sports in my app and players can only register in the ones
// that they qualify for. You can't filter rows in a knack table based on the kinds of criteria I needed, so I
// had to build a custom table, but wanted it to look exactly like the tables in the rest of the app that Knack
// made.


// PART 1 - The guardian. When the guardian logs in and is taken to view 603 automatically and the code starts.
$(document).on('knack-view-render.view_603', function(event, view) {

// Declare a variable to be used later.
var guardian_id = [];

// Create a variable that contains an array of all the data brought into the view (I now have a variable with the guardian's data).
var data_view_603 = Knack.models['view_603'].toJSON();

// This code is going to need to keep data in variables across knack views and code snips so I need to store the values of the variables
// in something I can get at later, so I use the browsers local storage to store the guardian's record ID.
localStorage.setItem("keep_guardian_id", data_view_603.id);

// Close the code that gathered information about the guardian.
});


// PART 2 - The player. When the guardian clicks on the "view details" link in the table of related players from back on view 603, knack
// pops up a players details scence that starts with detils of the player in view 695.
// When view 696 renders, start the code.
$(document).on('knack-view-render.view_695', function(event, view) {

// Declare a variable to be used later.
var player_id = [];

// Same as last time, create a variable that contains all of the data brought into the view (I now have a variable with the player's data).
var data_view_695 = Knack.models['view_695'].toJSON();

// create a few variables that can be stored over page loads for later use.
localStorage.setItem("keep_player_id", data_view_695.id);
localStorage.setItem("keep_player_age_now", data_view_695.field_475);
localStorage.setItem("keep_player_age_end", data_view_695.field_476);
localStorage.setItem("keep_player_age_min", data_view_695.field_1378);
localStorage.setItem("keep_player_age_max", data_view_695.field_1379);

// Close the code that gathered inforamtion about the player.
});

 

// In the same scene as the player's details I have added view 702 which is a blank RTF view.
// When the RTF view loads, start the code to make the table.
$(document).on('knack-view-render.view_702', function(event, view) {

// Remember those variables I had to store somewhere to use later?
// Now it's time to get the values back from memory and put them into this snippet for use.
var keep_player_id = localStorage.getItem("keep_player_id");
var keep_guardian_id = localStorage.getItem("keep_guardian_id");
var keep_player_age_now = localStorage.getItem("keep_player_age_now");
var keep_player_age_end = localStorage.getItem("keep_player_age_end");
var keep_player_age_min = localStorage.getItem("keep_player_age_min");
var keep_player_age_max = localStorage.getItem("keep_player_age_max");

// This one gets the record ID of the logged in user.
var userID = Knack.getUserAttributes().id;

// This part creates an array variable that stores the information I needed to filter out records
// of sports the player was inelegible for. For a list of which rules you can use in this kind
// of filter variable in a Knack API call go here: https://www.knack.com/developer-documentation/#filtering-by-a-field
// Remember that you are using the field numbers on the object you will be getting the data from to show in the table.
var filters = {
'match': 'and',
'rules': [
{
'field':'field_1350', //Min Player Age Allowed
'operator':'lower than', // Less than
'value': keep_player_age_min // Player Age
},
{
'field':'field_1349', //Max Player Age Allowed
'operator':'higher than', //Greater than
'value': keep_player_age_max // Player Age
},
{
'field':'field_1355', // Registration status
'operator': 'is', // is exactly
'value': 'open' // Registration status must be open to be included
}
]
};
var object60data = []; //setup success variable
var object60array = []; // setup success variable loop array
var object60error = []; // setup error variable
var i = 0; //setup loop counting variable


// Now that everything is setup and ready to go, it's time to build the table header row in the RTF document.
// Create the table in the blank RTF view
$((function createTable() {
var div = document.createElement('div'); // create a new div in the page dom to handle the table
div.setAttribute("id", "available_programs"); // set the ID of the new div to the table name
div.setAttribute("class", "kn-content"); // set the div to use the native knack style sheet
document.getElementById("view_702").appendChild(div) // insert the new div into the body of the rtf view
document.getElementById("available_programs").setAttribute("class", "kn-table-wrapper is-constrained"); //set next level of style sheet
document.getElementById("available_programs").innerHTML = "<table>" + //this starts to write the HTML code to create the table
'<thead>' + // declare the table header in the dom
'<th></th>' + // create a column for the checkbox
// below create a column for each of the items I wanted to show in the table (you can add as many as you need).
'<th>Type</th>' + '<th>Title</th> ' + '<th>Start Date</th>' + '<th># Sessions</th>' + '<th>Fee</th>' + '<th>Information</th>' +
'</thead>' + // close the table header in the dom
'<tbody id = "table_body">' + // set the ID of the table in the dom (we will look for this later)

// rows will be inserted here during API loops later

'</table>' + // close the table in the dom
'<p> </p>' + // leave a space after the table
'<button id="register"">Register in Selected Programs</button>' // Add a button below table
$("#available_programs table").addClass('kn-table kn-table-table is-bordered is-striped'); // set the style of the table
}) // close the insert HTML code
);// close function to create table

// Now I get the data I need from the Knack object using an API call
$.ajax({
// the URL of the api call tells knack wich object to look at and in this case
// which field to sort the results by and that it should only get records that match the filter critera set in the filter variable
url: 'https://api.knackhq.com/v1/objects/object_60/records/?sort_field=field_1328&sort_order=asc&filters='+ encodeURIComponent(JSON.stringify(filters)),
type: 'GET',
headers: {
'X-Knack-Application-ID': 'XXXYYYZZZ', // Don't forget your APP ID
'X-Knack-REST-API-Key': 'AAABBBCCC' // Don't forget your API key
}, // Close API headers
success: function (object60data) // if the API call was successful, put the results into a variable
{
var row = ''; // set a variable to blank for use later
for(var i = 0; i < object60data.total_records; i++) // start a loop to keep looping until all records returned have been parsed
{
var object60array = { // this variable holds data for a moment during each loop
recordid: object60data.records[i].id, //record id
season: object60data.records[i].field_1297, //season
community: object60data.records[i].field_1299, //community
communityid: object60data.records[i].field_1299_raw, //community id
gender: object60data.records[i].field_1315, //gender
type: object60data.records[i].field_1360, //type
feeraw: object60data.records[i].field_1295_raw, //feeraw
fee: object60data.records[i].field_1295, //fee
startdate: object60data.records[i].field_1328, //start date
sessions: object60data.records[i].field_1329, // number of sessions
description: object60data.records[i].field_1313, // description
minage: object60data.records[i].field_1350, // min age
maxage: object60data.records[i].field_1349, // max age
spotsavail: object60data.records[i].field_1357, // number of places available
title: object60data.records[i].field_1362, // title
status: object60data.records[i].field_1355 //status
}; // close object60array

var tablerowopen = ('<tr id = "'+ object60array.recordid +'">'); // a variable setting the ID of each row in the table to the ID number of the record
var checkboxcell = ('<td><input type="checkbox"></td>'); // a variable to store the HTML required to create a checkbox at the start of the row
var typecell = ('<td>'+ object60array.type + '</td>'); // a variable to take the data for this cell out of the array from above
var titlecell = ('<td>'+ object60array.title +'</td>');
var startdatecell = ('<td>'+ object60array.startdate +'</td>');
var sessionscell = ('<td>'+ object60array.sessions +'</td>');
var feecell = ('<td>'+ object60array.fee +'</td>');
// in my results table, I wanted to link to another knack view that contains details about the sport the row describes
// this next variable will create a link at the end of the table using the knack binoculars icon and when clicked
// will take the user to the other knack view (pop up) with information about that specific sport
// you would need to change the #view-program-details/ part of the code below to be the URL name of the view you wanted
// to link to. Knack always uses record id's to decide what data to load in the view, so I add the record id of the current row
// to the end of the link URL so that it opens the correct data in the pop-up.
var linkcell = ('<td class = "kn-table-link"><span><a href = "#view-program-details/'+object60array.recordid +'" class = "kn-link kn-link-page" address = "true"><span class = "level is-compact"><span class = "icon-is-left"><i class = "fa fa-binoculars"></i></span></a></span></td></tr>');
var tablerowclose = ('</tr>'); // close the table row.

// now I put all the HTML stored in each of the variables above together to make the HTML I need to create the entire row in the table
var row = row+tablerowopen+checkboxcell+typecell+titlecell+startdatecell+sessionscell+feecell+linkcell+tablerowclose;


$(document.getElementById("table_body").innerHTML = row); // insert the HTML that was put together in the row variable above into the dom just below the "table body"

}; //close the loop (if there are more rows to process the loop will continue until all the rows are processed)
}, // Close success function
error: function (object60error) // if the API call fails, store the message from knack in this variable
{
alert ('ajax error object60error: '+JSON.stringify(object60error)); // pop up a message box showing the error message from knack
} //close the error function
}); // Close the ajax function

//Now the table and button are built and in the dom
// PART 3 - What to do when the button is clicked.
// event for the button when clicked
$('#register').click(function() {

// turn on the Knack wait spinner
Knack.showSpinner();

// setup variable to store an array of record IDs from the checked rows
var record_ids = [];

// Populate the record IDs variable using all checked rows
$('#' + view.key + ' tbody input[type=checkbox]:checked').each(function() {
record_ids.push($(this).closest('tr').attr('id')); // record id
}); //close the get record ID function


// Now I start the process of inserting new records into a connected object.
// This takes all the information I've been gathering and places it into a registration object I have with all the connections.

alert ('Registering player in '+record_ids.length+' programs. Please wait.'); // Tell user we are registering player in thier chosen programs.

var data = []; //Setup a variable to put the data we need for the registration object.

for(var z = 0; z < record_ids.length; z++) // start a loop to keep looping until all records returned have been parsed
{ //open loop
// add data to data variable for this round of the loop
var data = {
field_1326: record_ids[z], // the id of the program from the custom table above
field_1289: keep_player_id, // the id of the player
field_1380: keep_guardian_id, // the id of the guardian
field_1292: userID // the id of the user
}; // close the data array

var registration_ids = []; // setup a variable to store the newly created record ID's

// Make an API ajax call so insert this first loop of data into the registrations object.
$.ajax({
url: 'https://api.knackhq.com/v1/objects/object_59/records/',
type: 'POST',
headers: {
'X-Knack-Application-ID': 'XXXYYYZZZ',
'X-Knack-REST-API-Key': 'AAABBBCCC'
},
data: data,
success: function(success_response)
{
registration_ids.push(success_response.id); // add this new registration record ID to the array of record ID's
localStorage.setItem("keep_registration_ids", registration_ids); //store the record ID for later use
},// close success function
error: function(error_response)
{
// let the user know of an error
alert ('Error! '+JSON.stringify(error_response));
} // close error function
}); //close the ajax api call

}; //close the loop (if there are more rows to process the loop will continue until all the rows are processed)

// turn off the knack wait spinner
Knack.hideSpinner();

var registration_ids = localStorage.getItem("keep_registration_ids"); // get back the list of record ID's from all the registration records that were made
alert ('Registration IDs are: '+ JSON.stringify(registration_ids)); // tell the user what the registration ID's are

}); // close the click function

}); // finally close the page load function
// all done

Thank you for the post.  The comments were very helpful and it worked flawlessly!

A very generous post.