Checkboxes in Table - Update Knack - Send to Webmerge

It took quite a bit of tinkering to get this code to work, so I thought I would share it with everyone now that it's done.

 

First, you should know that in my case I had a scene that had two views on it.  The first view was a details view that contained a little information about the logged in user (name email etc).  The second view was a table that showed all of the expense records that user had entered that were in and I had a filter placed on the table so that it only showed expense records for that user that had a status of "unsent".

 

My goal here was to show the user all of the expense forms they had made that had not been sent to our softball league's treasurer for repayment.  The second goal was to allow the user to select only a subset of those unsent forms and have the status in knack updated to "sent".  Finally, the third step was to have data regarding the checked off expenses sent to webmerge so that it could be incorporated into a form that we require our users to submit (hard copy with receipts) along with their expense claims.

 

I've commented the code below to try and explain what each step is doing.  Hopefully someone out there can use it, improve on it and repost it.

 

 

 

// THE CODE BELOW ADDS CHECKBOXES TO THE TABLE AND THEN UPDATES ALL THE CHECKED ROWS TO A NEW STATUS IN KNACK THEN SENDS THE CHECKED ROWS DATA TO WEBMERGE AS AN ARRAY.

// Create a function that adds checkboxes to a table - this function is a global function that can be reused and will be called on later

var addCheckboxes = function(view) {

// add the checkbox to to the header to select/unselect all
$('#' + view.key + '.kn-table thead tr').prepend('<th><input type="checkbox"></th>');

$('#' + view.key + '.kn-table thead input').change(function() {
$('.' + view.key + '.kn-table tbody tr input').each(function() {
$(this).attr('checked', $('#' + view.key + '.kn-table thead input').attr('checked') != undefined);
});
});

// add a checkbox to each row in the table body
$('#' + view.key + '.kn-table tbody tr').each(function() {
$(this).prepend('<td><input type="checkbox"></td>');
});
}

// on page load
$(document).on('knack-view-render.view_137', function(event, view) {

// Add a button
$('<button id="update"">Make PDF</button>').insertAfter('.view-header');

// Call the global function to add checkboxes to table
addCheckboxes(view);

// event for the button when clicked
$('#update').click(function() {

// setup variables to store an array of record IDs - we need two variables containing the same array of record IDs for this code to work
var record_ids = [];
var webmerge_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
});

// Copy the record IDs of the checked boxes into a new variable to be used in the second ajax call
// the .slice() is required to copy the array values at a point in time - without the .slice() the
// second copied array will mirror the original array everytime it changes and we want to maintain
// a record of the original checked rows
webmerge_ids = record_ids.slice();

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

// set data variable so that field 308 (status) will contain the new value
var data = {
field_308: 'Treasurer Waiting on Originals'
};

// set the delay to prevent hitting Knack API rate limit (milliseconds)
var myDelay = 100;

//call updateRecords function
$(function() {
updateRecords(record_ids.shift(), record_ids, data);
});


// use an ajax call to set the new value in knack for each checked record in the record ID array
var selectedRecords = record_ids.length + 1
function updateRecords(id, records, data) {
$.ajax({
url: 'https://api.knackhq.com/v1/objects/object_27/records/' + id,
type: 'PUT',
headers: {
'X-Knack-Application-ID': 'MY KNACK APP ID',
'X-Knack-REST-API-Key': 'MY KNACK API KEY'
},
data: data,
success: function(response) {
if (record_ids.length > 0) {
// every time a loop is made, the array is shifted by 1.
// if the array still has a length greater than 0, re-run another updateRecords loop
setTimeout(updateRecords(record_ids.shift(), record_ids, data), myDelay);
} else {
alert('Processing ' + selectedRecords + 'expense claims.');

// Knack records are updated now
// Starting webmerge code

// setup data variables from various views to collect values
var data_view_136 = Knack.models['view_136'].toJSON();
var data_view_137 = Knack.models['view_137'].data.toJSON();
var items = [];

// for each checked row in the table - remembered by the array that was copied above
// gather data and put the values in an array for posting to webmerge

for(x = 0; x < data_view_137.length; x++)
{
item = data_view_137[x];
if(webmerge_ids.indexOf(item.id) < 0){
continue;
}

items.push
({
formid: item.field_267_raw,
team: item.field_341_raw,
by: item.field_342_raw,
category: item.field_343_raw,
short: item.field_266_raw,
amount: item.field_269_raw,
date: item.field_264_raw.date
});
}

// use another ajax call -- this time to webmerge to send the array of data to the document
$.ajax
({
url: 'https://www.webmerge.me/merge/MY WEBMERGE ID/MY WEBMERGE DOC ID',
data:
{
user: data_view_136.field_1,
useremail: data_view_136.field_2_raw.email,
userphone: data_view_136.field_7,
items: items
},
type: 'POST',
success: function()
{
// pop up a message box if the send to webmerge was successful and hide the Knack wait spinner
alert ("e-Mailing you a PDF form to print. Please note: It is crucial that you check your e-mail inbox. You will find a PDF copy of your expense form that needs to be printed, signed and delivered to the High Park Softball Treasurer. This expense form will not be processed until the original signed copy and any applicable receipts have been recieved.");
Knack.hideSpinner();
// reload the page so that the table refreshes
window.location.reload(true);
},
error: function()
{
// pop up a message box if the send to webmerge failed and hide the Knack wait spinner
alert('There was an error creating the PDF form to print.');
Knack.hideSpinner();
}
});

}
}
})
}
})
});

 

Hi Zak

 

A late reply to this - but I see in your code you reference Xero. Have you been able to send items to Xero from Knack using a checkbox at all?

 

Thanks

Kim

I wanted to reach out on this thread and announce a new feature - Action Links - to the conversation here. While it won’t take care of all of the requirements you outline here Jeff, it can help to automate workflows like your second step of updating a status field with the click of a link.

You can read more about this feature in our “Action Links” help article.

I've adapted this code to send to a Zapier Catch Hook, allowing me to trigger Zaps from the click event as opposed to sending to WebMerge. 

The problem is that this code sends the records as a 2D array, which means I can't use Zapier's built in array support in subsequent action steps (ie. creating rows in a Google Spreadsheet). Can anyone recommend an adjustment so the payload gets sent as a simple array?

Hey Jeff - this is very impressive stuff. I'm trying to adapt your code, but to send to a Zapier Webook instead of a Webmerge. Right now, on the click event I'm stuck with a spinner and nothing happening on either end. 

// THE CODE BELOW ADDS CHECKBOXES TO THE TABLE AND THEN UPDATES ALL THE CHECKED ROWS TO A NEW STATUS IN KNACK THEN SENDS THE CHECKED ROWS DATA TO WEBMERGE AS AN ARRAY.

// Create a function that adds checkboxes to a table - this function is a global function that can be reused and will be called on later

var addCheckboxes = function(view) {

// add the checkbox to to the header to select/unselect all
$('#' + view.key + '.kn-table thead tr').prepend('<th><input type="checkbox"></th>');

$('#' + view.key + '.kn-table thead input').change(function() {
$('.' + view.key + '.kn-table tbody tr input').each(function() {
$(this).attr('checked', $('#' + view.key + '.kn-table thead input').attr('checked') != undefined);
});
});

// add a checkbox to each row in the table body
$('#' + view.key + '.kn-table tbody tr').each(function() {
$(this).prepend('<td><input type="checkbox"></td>');
});
}

// on page load
$(document).on('knack-view-render.view_28', function(event, view) {

// Add a button
$('<button id="update"">Xero</button>').insertAfter('.view-header');

// Call the global function to add checkboxes to table
addCheckboxes(view);

// event for the button when clicked
$('#update').click(function() {

// setup variables to store an array of record IDs - we need two variables containing the same array of record IDs for this code to work
var record_ids = [];
var webmerge_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
});

// Copy the record IDs of the checked boxes into a new variable to be used in the second ajax call
// the .slice() is required to copy the array values at a point in time - without the .slice() the
// second copied array will mirror the original array everytime it changes and we want to maintain
// a record of the original checked rows
webmerge_ids = record_ids.slice();

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

// set data variable so that field 308 (status) will contain the new value
var data = {
field_259: '12/12/2017'
};

// set the delay to prevent hitting Knack API rate limit (milliseconds)
var myDelay = 100;

//call updateRecords function
$(function() {
updateRecords(record_ids.shift(), record_ids, data);
});


// use an ajax call to set the new value in knack for each checked record in the record ID array
var selectedRecords = record_ids.length + 1
function updateRecords(id, records, data) {
$.ajax({
url: 'https://api.knackhq.com/v1/objects/object_3/records/' + id,
type: 'PUT',
headers: {
'X-Knack-Application-ID': APP_ID,
'X-Knack-REST-API-Key': API_KEY
},
data: data,
success: function(response) {
if (record_ids.length > 0) {
// every time a loop is made, the array is shifted by 1.
// if the array still has a length greater than 0, re-run another updateRecords loop
setTimeout(updateRecords(record_ids.shift(), record_ids, data), myDelay);
} else {
alert('Processing ' + selectedRecords + 'expense claims.');

// Knack records are updated now
// Starting webmerge code

// setup data variables from various views to collect values
var data_view_136 = Knack.models['view_28'].toJSON();
var data_view_137 = Knack.models['view_28'].data.toJSON();
var items = [];

// for each checked row in the table - remembered by the array that was copied above
// gather data and put the values in an array for posting to webmerge

for(x = 0; x < data_view_137.length; x++)
{
item = data_view_137[x];
if(webmerge_ids.indexOf(item.id) < 0){
continue;
}

items.push
({
formid: item.field_842_raw,
team: item.field_341_raw,
amount: item.field_1372_raw,
date: item.field_256_raw.date
});
}

// use another ajax call -- this time to webmerge to send the array of data to the document
$.ajax
({
url: 'https://hooks.zapier.com/hooks/catch/196874/s06oca/',
data:
{
user: data_view_136.field_842,
useremail: data_view_136.field_821_raw.email,
items: items
},
type: 'POST',
success: function()
{
// pop up a message box if the send to webmerge was successful and hide the Knack wait spinner
alert ("e-Mailing you a PDF form to print. Please note: It is crucial that you check your e-mail inbox. You will find a PDF copy of your expense form that needs to be printed, signed and delivered to the High Park Softball Treasurer. This expense form will not be processed until the original signed copy and any applicable receipts have been recieved.");
Knack.hideSpinner();
// reload the page so that the table refreshes
window.location.reload(true);
},
error: function()
{
// pop up a message box if the send to webmerge failed and hide the Knack wait spinner
alert('There was an error creating the PDF form to print.');
Knack.hideSpinner();
}
});

}
}
})
}
})
});

I reckon we're seeing that sometimes with a similar Checked Table update our end (some updates are missed) so I'll give that a try!

Thanks for sharing again Jeff.

Not if there were only a few records in the table to update, but if I put in a lot of records (50+) then yes, it wasn't catching all of the updates.  It was like java could send the updates faster than knack could take them and it caused skips.

 

Thanks for sharing Jeff, learned something with the use of a delay - were you seeing some errors in record updates without it?