Handy Wrapper to deal with page load / Load order issues

Page load timing: helpful  wrapper

This is a handy script for when you have trouble getting your code to execute after a view/ views have rendered - maybe you're retrieving values from 2 or more tables but it's firing before the one  can finish loading /This works most times that something's not loaded in time to retrieve a value you need from it. This wrapper let's you specify the views to wait for and only executes at that point. If you're having timing issues with a plugin or jquery you may be able to just replace the view names and paste your code in the middle.

 


waitForAllEvents([
'knack-view-render.view_xxx', //change as necessary
'knack-view-render.view_yyy //change as necessary
], function(){

//YOUR CODE HERE


})

function waitForAllEvents(listOfEvents, cb){ //cb is a callback
var triggeredEvents = []; //create an array called triggered events
listOfEvents.forEach(function(eventKey){ //Looping through list of events
var listener = function(event, view, record){ //creating event listener

// $(document).off(eventKey, listener);
// removing that listener from the document
// this is for Safety / optimization (in case the page loads twice)


triggeredEvents.push(eventKey); //add event to list of already triggered events

if(triggeredEvents.length === listOfEvents.length){   //have all the events finished? 
cb() //run the callback
}
}

$(document).on(eventKey, listener);
})
}

 

Hello I have a page that has a lot of display rules and the user is starting to click before all the rules have finished (even though the spinner is still spinning). So, can I use the code that has been given here to not show the view until it finishes the display rules?

Justin, your code just saved my bacon. Great post, thank you so much.

Keith, I haven't tried you approach yet - but I may next time.

Great work, guys.

Mike

Thanks, Brad. I figured it out with a little help from stack overflow.

Here's the code in case anyone is interested. In the future, I plan on adding this to every app I build and to create a field to record the record.id for each object. This way if I want to send a link to a specific record in a Knack generated email it can be generated via text formula in the background and included in the notification (even if there is a parent's record.id in the url string.)

//Record a record.id for every record created

//create an array of objects
bindListToInputs([
{
//object is client
page_name: "page_14",
field_name: "field_317", //field to capture record ID
view_name: "view_15",
},
//repeat as necessary
{
//Another view where a different object record is created
page_name: "page_186",
field_name: "field_317",
view_name: "view_308",
}
]);

function bindListToInputs(list){
list.forEach(function(inputs){
bindToInputs(inputs);
});
}

function bindToInputs(inputs){

var page_name = inputs.page_name,
view_name = inputs.view_name,
field_name = inputs.field_name;

$(document).on("knack-record-create." + view_name, function(event, view, record) {

console.log('record created');
var recordOne = record.id
console.log(view_name, record.id);

var url = "https://api.knack.com/v1/pages/" + page_name + "/views/" + view_name + "/records/" + record.id;

//console.log (url, field_name);

var user = Knack.getUserToken();
var headers = { "Authorization": user, "X-Knack-Application-ID": app_id, "Content-Type":"application/json"}
var fieldnamed = field_name
var data = {};
data[fieldnamed] = recordOne


//console.log('data is : ', data);
//console.log (url, field_name);

Knack.showSpinner();
// Make the AJAX call
$.ajax({
url: url,
type: 'PUT',
headers: headers,
//data: data,
data: JSON.stringify(data),
}).done(function(responseData) {
console.log('Record updated!');
Knack.hideSpinner();
});
});

}

Hi Justin,

I'm no JavaScript or JSON expert, but it seems to me the problem is using a variable as the property name in building your JSON data object.  I'm pretty crunched for time at the moment but it wouldn't surprise me if a bright spark on Stackoverflow has already answered this problem already:

"How to build a JSON object using a variable as the property name..."

Anyone else?

Thanks Brad -I've learned a lot by piggybacking on the code you have shared here and appreciate the feedback.

If you have a minute maybe you can help me with this code. ?  I would really appreciate it.

I want to create an easy way to record the record.id for every record created in my database.

The idea is a create an array of objects  that stores what views/scenes are associated with which fields to record a record.id. Here's the code. As new  forms are added to the app  -you could just add a new entry to the array and automatically be saving the record.id for each new record.

It works when the payload field value is represented as text ( field_317) but I cannot get it to work when it is represented as a var  (field_name) (even though I'm able to console.log that value in the same function.)

You can see below that I'm able to log the value on the lines directly preceding and following the declaration of the payload.  I think if I can get that last part working this will be a very useful script for the community. Allowing the easy transfer of connection fields between objects and easily parsing urls for custom view links to your records.

 


//Record all record ids for every record created
//create an array of objects representing which view creation scenex/views correspond to which record.id fields
bindListToInputs([
{
//object is client
page_name: "page_14",
field_name: "field_317", //field to capture record ID
view_name: "view_15",
},

//repeat as necessary

{
//a different record create screen
page_name: "page_186",
field_name: "field_317",
view_name: "view_308",

}
]);

function bindListToInputs(list){
list.forEach(function(inputs){
bindToInputs(inputs);
});
}

function bindToInputs(inputs){

var page_name = inputs.page_name,
view_name = inputs.view_name,
field_name = inputs.field_name;

$(document).on("knack-record-create." + view_name, function(event, view, record) {

console.log('record created');
console.log(view_name, record.id);

var url = "https://api.knack.com/v1/pages/" + page_name + "/views/" + view_name + "/records/" + record.id;

console.log (url, field_name);  //correctly logs 'field name ' here


var user = Knack.getUserToken();
var headers = { "Authorization": user, "X-Knack-Application-ID": app_id, "Content-Type":"application/json"}

//var data = { field_name: record.id };    //this does not work
var data = { field_317: record.id };         //this does, why?
console.log (url, field_name);  //correctly logs 'field_name' here

Knack.showSpinner();
// Make the AJAX call
$.ajax({
url: url,
type: 'PUT',
headers: headers,
data: JSON.stringify(data),
}).done(function(responseData) {
console.log('Record updated!');
Knack.hideSpinner();
});
});

}

Justin & Keith - really appreciate you sharing this with the community.

Great work.

I didn't know that about page render_ Nice to know. I'll try this out next time I run into this problem

 

Here's another approach to the same problem that I think might be simpler.  Page render always fires after all views have rendered, so use that event to trigger a jquery deferred in the view event, like this... 

var pageRendered = $.Deferred();

// FIRST VIEW EVENT
$(document).on('knack-view-render.view_1', function(viewEvent, view, data) {
  // Wait for all views to render
pageRendered.then(function(pageEvent, page) {
// Do stuff here
});
// Other code that doesn't wait for page-render here
});

// ANOTHER VIEW EVENT
$(document).on('knack-view-render.view_2', function(viewEvent, view, data) {
  // Wait for all views to render
  pageRendered.then(function(pageEvent, page) {
    // Do stuff here
  });
// Other code that doesn't wait for page-render here
});

$(document).on('knack-page-render.page_1', function(event, page) {
pageRendered.resolve(event, page);

});

 

This keeps the normal flow and I think is easier to read.  It also lets you use the page event's parameters (pageEvent and page) inside the view event.