Changing File Hyperlink Text

Just noticed it not working on mobile. Does anyone have a fix?

Years later – and this is still SO useful. (It’s revolutionized my little knack world today, in fact.) Thank you Stephen!!!

Ahhh! So good to hear @AmyRobb02531! I appreciate that.

1 Like

This is fantastic. Thank you! They should implement this as an opt out for the file previewer. Everything loads way faster.

@StephenChapman It seems this code breaks down if a search is made on a table or you increase the # of records shown from default. For example if I open a table and there is no text after the “####.knack.com/yourapp#tablepage/” everything works as intended. But if you search something in that table and it adds “####.knack.com/yourapp#tablepage/?view_100_page=1&view_100_search=yourseach” to the end of the url, it goes back to the knack file preview. Any idea why this would be?

Sorry I meant to send the below reply to this specific comment. @StephenChapman It seems this code breaks down if a search is made on a table or you increase the # of records shown from default. For example if I open a table and there is no text after the “####.knack.com/yourapp#tablepage/” everything works as intended. But if you search something in that table and it adds “####.knack.com/yourapp#tablepage/?view_100_page=1&view_100_search=yourseach” to the end of the url, it goes back to the knack file preview. Any idea why this would be?

@Eddie good catch!
The issue was my original code was triggered from a scene-render (I was less experienced!). When you search, it actually re-renders the view, and hence the code doesn’t re-run to overwrite the click handler for any links.
The solution is to change it to a view-render.
I have also changed the newURL variable so that it dynamically finds the Knack application ID without needing to update it manually.

$(document).on('knack-view-render.any', function (event, view, data) {  
  $(".kn-view-asset").on("click", function(e) {
    e.preventDefault(); //Prevent normal file window opening
    e.stopPropagation();
    var url = $(this).attr('href'); // Get the URL of the clicked link
    var pathArray = url.split('/'); // Split the URL string into substrings between '/' characters
    var filePath = pathArray[pathArray.length-1]; // Get the file name in the last substring (e.g. file.pdf)
    var assetPath = pathArray[pathArray.length-2]; // Get the asset path in the second-last substring (e.g. 6-13-77-5d2ffb2f47b3c70010ffd98d)
    pathArray = assetPath.split('-'); // Split the assetPath into substrings between '-' characters
    assetPath = pathArray[pathArray.length-1]; // Get the actual asset path in the last substring (e.g. 5d2ffb2f47b3c70010ffd98d)
	var newURL = `https://api.knack.com/v1/applications/${Knack.application_id}/download/asset/${assetPath}/${filePath}` // Concatenate the download URL
    window.open(newURL, '_blank'); // Open the new URL in a new tab
  });
});

This code should now be plug-and-play, thanks for highlighting that issue!

@StephenChapman This does seem to fix that with a small edit to the URL line

var newURL = “https://api.knack.com/v1/applications/Knack Application ID/download/asset/” + assetPath + “/” + filePath // Concatenate the download URL

However, it seems that on mobile it opens a new tab for each view on a page. So if a page has 6 views on it, it will open six tabs of a file when the file is clicked.

@StephenChapman The original code did work for the URL. The problem with the multiple views opening multiple tabs on mobile does seem to persist though. Really appreciate the help!

@StephenChapman

I was able to fix this mobile issue by adding a debounce function. See final code below:

// Function to debounce a function execution
function debounce(func, delay) {
  let timeoutId;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      func.apply(context, args);
    }, delay);
  };
}

// Debounced click event handler
const debouncedClickHandler = debounce(function(e) {
  e.preventDefault(); //Prevent normal file window opening
  e.stopPropagation();
  var url = $(this).attr('href'); // Get the URL of the clicked link
  var pathArray = url.split('/'); // Split the URL string into substrings between '/' characters
  var filePath = pathArray[pathArray.length - 1]; // Get the file name in the last substring (e.g. file.pdf)
  var assetPath = pathArray[pathArray.length - 2]; // Get the asset path in the second-last substring (e.g. 6-13-77-5d2ffb2f47b3c70010ffd98d)
  pathArray = assetPath.split('-'); // Split the assetPath into substrings between '-' characters
  assetPath = pathArray[pathArray.length - 1]; // Get the actual asset path in the last substring (e.g. 5d2ffb2f47b3c70010ffd98d)
  var newURL = `https://api.knack.com/v1/applications/${Knack.application_id}/download/asset/${assetPath}/${filePath}` // Concatenate the download URL
  window.open(newURL, '_blank'); // Open the new URL in a new tab
}, 300); // Adjust the delay as needed (e.g., 300 milliseconds)

// Attach debounced click event handler to elements with class .kn-view-asset
$(document).on('knack-view-render.any', function(event, view, data) {
  $(".kn-view-asset").on("click", debouncedClickHandler);
});

The code above wasn’t blocking the default viewer on the other page. See final altered code below.

// Function to debounce a function execution
function debounce(func, delay) {
  let timeoutId;
  return function() {
    const context = this;
    const args = arguments;
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      func.apply(context, args);
    }, delay);
  };
}

// Debounced click event handler
const debouncedClickHandler = debounce(function(e) {
  var url = $(this).attr('href'); // Get the URL of the clicked link
  var pathArray = url.split('/'); // Split the URL string into substrings between '/' characters
  var filePath = pathArray[pathArray.length - 1]; // Get the file name in the last substring (e.g. file.pdf)
  var assetPath = pathArray[pathArray.length - 2]; // Get the asset path in the second-last substring (e.g. 6-13-77-5d2ffb2f47b3c70010ffd98d)
  pathArray = assetPath.split('-'); // Split the assetPath into substrings between '-' characters
  assetPath = pathArray[pathArray.length - 1]; // Get the actual asset path in the last substring (e.g. 5d2ffb2f47b3c70010ffd98d)
  var newURL = `https://api.knack.com/v1/applications/${Knack.application_id}/download/asset/${assetPath}/${filePath}` // Concatenate the download URL
  window.open(newURL, '_blank'); // Open the new URL in a new tab
}, 300); // Adjust the delay as needed (e.g., 300 milliseconds)

// Attach debounced click event handler to elements with class .kn-view-asset
$(document).on('knack-view-render.any', function(event, view, data) {
  $(".kn-view-asset").on("click", function(e) {
    e.preventDefault(); // Prevent the default behavior (e.g., opening a file window)
    e.stopPropagation();
    debouncedClickHandler.call(this, e); // Call the debounced click handler function
  });
});

Haha, love this code jam we have going on!
Another oversight on my part: my click handler for the link was missing reference to the view, which means that it would run this code on each link found multiplied by the number of views on the page.
Instead of:

$('.kn-view-asset').on("click", function(e) {

It needs to be this:

$(`#${view.key} .kn-view-asset`).on("click", function(e) {

This means you shouldn’t need that debounce function.

Full code again below:

$(document).on('knack-view-render.any', function (event, view, data) {  
  $(`#${view.key} .kn-view-asset`).on("click", function(e) {
    e.preventDefault(); //Prevent normal file window opening
    e.stopPropagation();
    var url = $(this).attr('href'); // Get the URL of the clicked link
    var pathArray = url.split('/'); // Split the URL string into substrings between '/' characters
    var filePath = pathArray[pathArray.length-1]; // Get the file name in the last substring (e.g. file.pdf)
    var assetPath = pathArray[pathArray.length-2]; // Get the asset path in the second-last substring (e.g. 6-13-77-5d2ffb2f47b3c70010ffd98d)
    pathArray = assetPath.split('-'); // Split the assetPath into substrings between '-' characters
    assetPath = pathArray[pathArray.length-1]; // Get the actual asset path in the last substring (e.g. 5d2ffb2f47b3c70010ffd98d)
	var newURL = `https://api.knack.com/v1/applications/${Knack.application_id}/download/asset/${assetPath}/${filePath}` // Concatenate the download URL
    window.open(newURL, '_blank'); // Open the new URL in a new tab
  });
});

:crossed_fingers:

Hi Stephen

Just revisiting this - I was loath to embed files in our database due to the fear of outgrowing our space allowance but some things I have now decided we can do.

But rather than a filename as a hihghlighted link I would much rather show a PDF icon.

This is for both grid views and detail/edit views as well.

Any hints clues ? I found some old code but it only referred to replacing the text with more text when its too long.

Cheers

Hi Ray

Just tested this


PDF Icon

which works but maybe not the best looking icon.

.

Another option is using Font Awesome Icons:

You can use:

(Ensure Font Awesome is loaded in your Knack app.)

Where to Get PDF Icons
Flaticon: https://www.flaticon.com/free-icons/pdf
Font Awesome: File Pdf Icon | Font Awesome

Dean

1 Like

Thanks for that - only problem is I am a javascript idiot.
Where does this get specified.
Not sure what the latest code snippet is within the chain (hanging head low in embarrassment)

Hi Ray

Sorry, about that.

I put that in my Rich Text field - click on the <> and paste/enter the values.

I’m sorry but I don’t know to paste this without it running the HTML so I’ve tried removing a few < and > and .

a href=“YOUR_PDF_URL” target=“_blank”
img src=“https://cdn-icons-png.flaticon.com/512/337/337946.png” alt=“PDF Icon” width=“30”

image

Dean

Thanks that works - was hoping for a global parameters as there is some 1500 POW’s that we are keeping the red cross records for.

Never mind - its kinda fun chasing little mods and things but when you have to play with knack and javascript and css and your a novice at all becomes a challenge.

I’ve achieved some pretty good things (IMO) overall in the past few years but still not that conversant with some languages.

Cheers

PS Got font awesome loaded and running - lot more icons but nothing really standout wiithout paying up for pro etc.

Hi Ray

I’m no coder so here’s what AI suggests:

document.addEventListener("knack-view-render.any", function() {
  // Find all links ending with ".pdf" and replace them with an icon
  const pdfLinks = document.querySelectorAll('a[href$=".pdf"]');
  
  pdfLinks.forEach(link => {
    link.innerHTML = `
      <img 
        src="https://cdn-icons-png.flaticon.com/512/337/337946.png" 
        alt="PDF" 
        style="width: 20px; vertical-align: middle; margin-right: 5px;"
      >
      ${link.textContent}
    `;
  });
});

This should automatically apply to all PDF links in your app.

You’ll need to adjust the src, width, and styling as needed.

Dean

1 Like

Cheers Dean.