Hello there,
We want to create a custom widget that helps export specific data from a table and the corresponding data from a card. For example, if we want to download a specific data row and its card fields as a CSV or xlsx file, we can select them and download them. Also, similar data can be imported from an external file. It’s pretty much an export/import widget.
Is it doable? Any suggestions would be much appreciated!
Best regards!
I did it to export a contact in vcard format. It was a button which wrote a text file, I think it would work for your csv export. If you are interested I can post the code (ugly but works).
1 Like
Please share. If it works, the look doesn’t matter.! I hope it will work for us as well. Thank you for your reply and help!
script.js:
var globalData="";
var bcolor="";
var bcolorh="";
$(document).ready(function() {
var pif=$(window.parent.document).find('iframe').filter(function(){
return this.contentWindow === window
}).parent();
var obj=pif.closest('.view_data_pane_container');
pif.closest('.view_data_pane_container').css('height','50px');
});
function GoogleSearch(text) {
var search=getStringInBetween(text,"\nN:",";\nFN:").replaceAll(";"," ");
search=search.replaceAll(" ","+");
link="https://www.google.com/search?q="+search;
window.open(link,"_blank");
}
function getStringInBetween(str, start, end) {
const res = str.match(new RegExp(start + "(.*)" + end));
return res[1];
}
function textToClipboard (text) {
var dummy = document.createElement("textarea");
document.body.appendChild(dummy);
dummy.value = text;
dummy.select();
document.execCommand("copy");
document.body.removeChild(dummy);
// Create element with <a> tag
const link = document.createElement("a");
// Create a blob object with the file content which you want to add to the file
const file = new Blob([text], { type: 'text/plain' });
// Add file content in the object URL
link.href = URL.createObjectURL(file);
// Add file name
var fnam= getStringInBetween(text,"\nN:",";\nFN:").replaceAll(";","_").concat(".vcf");
link.download = fnam;
// Add click event to <a> tag to save file.
link.click();
URL.revokeObjectURL(link.href);
}
function search(){
GoogleSearch(globalData);
}
function copyText() {
textToClipboard(globalData);
document.getElementById('copy').innerHTML = "vCard salvata";
}
function showError(msg) {
var el = document.getElementById('error')
if (!msg) {
el.style.display = 'none';
} else {
el.innerHTML = msg;
el.style.display = 'block';
}
}
// Returns first column from a record or undefined.
function firstColumn(record) {
return record ? record[Object.keys(record).filter(k => k !== 'id')[0]] : undefined;
}
grist.ready({
columns: ["Column"]
});
grist.on('message', (e) => {
if (e.tableId) { tableId = e.tableId; }
});
grist.onRecord(function(record) {
const mapped = grist.mapColumnNames(record);
// We will fallback to reading a value from a first column to
// support old way of mapping (exposing only a single column).
// New widgets should only check if mapped object is truthy.
const valu`Preformatted text`e = mapped?.Column ?? firstColumn(record);
if (value === undefined) {
showError("Select a column to copy using the Creator Panel.");
} else {
globalData=value;
document.getElementById('copy').innerHTML = "Salva vCard";
}
});
1 Like
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Save vCard to file</title>
<script src="https://docs.getgrist.com/grist-plugin-api.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script src="script.js"></script>
<!--
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.3.1/dist/css/bootstrap.mi>
-->
<link rel="stylesheet" type="text/css" href="inline1.css">
<link rel="stylesheet" type="text/css" href="inline2.css">
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
<div class="content buttonbar">
<div class="_grain675_-top-space _grain675_ flexvbox">
<div class="layout_box layout_hbox">
<button class="_grain12_ _grain12_-primary" id="copy" onclick="copyText()">Salva vCard</button>
<button class="_grain12_ _grain12_-primary" id="search" onclick="search()">Cerca</button>
</div>
</div>
<div id="error"></div>
</div>
</body>
</html>
1 Like
I redefine some css so that the buttonbar can be shrunk nicely, but if you do not care about formatting you don’t need them. Some comments are in italian, if unclear I can translate them.
If you also want the css, just let me know.
It is just a quick and dirty solution I am sure you will do much better.
1 Like
Thank you very much for sharing the code. I will try to implement it and will let you know. You don’t need to translate and the CSS is not needed. I really appreciate your time and help! 