Im getting the following error while exporting an html table to excel;
SCRIPT429: Automation server can't create object...
This function was working fine but when i restarted my pc and the the application it stopped working not sure whats going on.
function write_to_excel() {
str = "";
var myTable = document.getElementById('myTable');
var rows = myTable.getElementsByTagName('tr');
var rowCount = myTable.rows.length;
var colCount = myTable.getElementsByTagName("tr")[0]
.getElementsByTagName("th").length;
var ExcelApp = new ActiveXObject("Excel.Application"); //Debug shows error at this line
var ExcelWorkbook = ExcelApp.Workbooks.Add();
var ExcelSheet = ExcelWorkbook.ActiveSheet;
ExcelApp.Visible = true;
ExcelSheet.Range("A1", "Z1").Font.Bold = true;
ExcelSheet.Range("A1", "Z1").Font.ColorIndex = 23;
// Format table headers
for ( var i = 0; i < 1; i++) {
for ( var j = 0; j < colCount - 1; j++) {
str = myTable.getElementsByTagName("tr")[i]
.getElementsByTagName("th")[j].innerHTML;
ExcelSheet.Cells(i + 1, j + 1).Value = str;
}
ExcelSheet.Range("A1", "Z1").EntireColumn.AutoFit();
}
for ( var i = 1; i < rowCount; i++) {
for ( var k = 0; k < colCount - 1; k++) {
str = rows[i].getElementsByTagName('td')[k].innerHTML;
ExcelSheet.Cells(i + 1, k + 1).Value = myTable.rows[i].cells[k].innerText;
}
ExcelSheet.Range("A" + i, "Z" + i).WrapText = true;
ExcelSheet.Range("A" + 1, "Z" + i).EntireColumn.AutoFit();
}
return;
}
Automation server can't create object.
Means your ActiveX settings are too high so the code will not run.
Related
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('data');
var data = sheet.getDataRange().getValues();
var range = sheet.getRange("A1:L" + data.length);
range.sort(1);
const people = {};
for(var i = 0; i < data.length; i++) {
var name = data[i][0] + data[i][1];
console.log(i);
if (!people.name) {people.name = {rows: [i]};} else {people.name.rows.push(i)}
}
Logger.log(people);
}
What should I be doing differently? At the end, it logs {name={rows=[0.0, 1.0, 2.0, ...]}} instead of having an object for each name...?
In the sheet there's just a first name and last name on columns A and B, for around 80 rows.
Use the bracket syntax if you want to use dynamic names for properties: https://riptutorial.com/javascript/example/2321/dynamic---variable-property-names
In your case:
function myFunction() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('data');
var data = sheet.getDataRange().getValues();
var range = sheet.getRange("A1:L" + data.length);
range.sort(1);
const people = {};
for(var i = 0; i < data.length; i++) {
var name = data[i][0] + data[i][1];
console.log(i);
if (!people[name]) {people[name] = {rows: [i]};} else {people[name].rows.push(i)}
}
Logger.log(people);
}
I'm currently developing a sheet that shows results from a set of data based on some filters but the data loads to slowly when getting the results, I've tried to follow the Best Practices from Google Documentacion with no luck, how can I set an array for the data to load faster?
Below is the code commented with what I've already tried
function realizarBusqueda() {
var inicio = SpreadsheetApp.getActive().getSheetByName("INICIO");
var aux_tags = SpreadsheetApp.getActive().getSheetByName("Aux_Tags");
var data = SpreadsheetApp.getActive().getSheetByName("Data");
var data_lc = data.getLastColumn();
var data_lr = data.getLastRow();
var searchRange = data.getRange(2,1, data_lr, data_lc);
var inicio_lc = inicio.getLastColumn();
inicio.getRange("A8:L1000").clearContent();
inicio.getRange("A8:L1000").clearFormat();
var countRows = inicio.getMaxRows();
inicio.deleteRows(20, (20-countRows)*-1);
if (inicio.getRange("B4").isBlank()) {
inicio.getRange("A8:L1000").clearContent();
inicio.getRange("A8:L1000").clearFormat();
var countRows = inicio.getMaxRows();
inicio.deleteRows(20, (20-countRows)*-1);
SpreadsheetApp.flush();
}
else if ((inicio.getRange("B4").getValue() != "" &&
inicio.getRange("C4").getValue() === "")) {
//filtrado 1
var arrayDatos = searchRange.getValues();
var inicio_fr = 8;
//var row = new Array(11);
for (var j = 2; j <= data_lr; j++) {
//row[j] = new Array(data_lr);
if (aux_tags.getRange("P2").getValue() === arrayDatos[j-2][4]) {
var inicio_fc = 1;
for (var i = 0; i < arrayDatos[j-2].length; i++) {
//row[j][i] = arrayDatos[j-2][i];
var row = arrayDatos[j-2][i];
inicio.getRange(inicio_fr, inicio_fc).setValue(row);
inicio_fc++;
}
inicio_fr++;
}
//inicio.getRange("A8").setValues(row);
}
}
I expect the output to load lots faster, currently what I've tried is commented, the code as-is is working but too slow
I just wanted to update this subject because I figured out myself, see attached the new code with the use of new 2D arrays
...
//filtrado 1
var arrayDatos = searchRange.getValues();
var inicio_fr = 8;
var rows = [];
var row = [];
for (var j = 2; j <= data_lr; j++) {
if (aux_tags.getRange("P2").getValue() === arrayDatos[j-2][4]) {
var inicio_fc = 1;
for (var i = 0; i < arrayDatos[j-2].length; i++) {
row.push(arrayDatos[j-2][i]);
if (i == 11) {
rows.push(row);
row = [];
}
}
}
}
inicio.getRange(8, 1, rows.length, rows[0].length).setValues(rows);
}
Now instead of writing on row at a time, I just write the whole array at once
with this code I use to save attachment to a shared google drive folder and got notified when a change happen to it by a mail. All is fine as long as I search for pdf attachments. As I set file:xls the messages returned are far more than expected. Not only, the threads returned by the debug are correct, as well as insert the query in the search filed in Gmail, but the getmessages() returned in this case 38 messages (and attachements! And not only xls but also a pdf) of the query parameters. The conversation view is disabled in Gmail, the only match with this attachments is the from: field.
I tried to change the reference data (by adding newer_than), and nothing changed; I changed the for cycle, and nothing changed. Am I missing something really stupid or is this a kind of bug with xls?
function salvataggioTavoloTecnico(){
var folderName = 'tavoloTecnico';
var p = 0;
var f1s= DriveApp.getFoldersByName(folderName).next().getFiles();
while (f1s.hasNext()) {
var f1 = f1s.next();
p++
} Logger.log(p);
var d = new Date();
d.setDate(d.getDate()-1);
var n = d.getFullYear() + "/" + addZero(d.getMonth()+1) + "/" + addZero(d.getDate());
var query = 'in:anywhere has:attachment filename:XLS newer_than:5d (from:mariano.casillo#mit.gov.it OR from:donato.castigliego#mit.gov.it OR from:luciano.aloia#mit.gov.it)';
//var query = 'in:anywhere has:attachment filename:xls '+ 'after:'+n+' (from:mariano.casillo#mit.gov.it OR from:donato.castigliego#mit.gov.it OR from:luciano.aloia#mit.gov.it)';
var elenco = GmailApp.search(query);
var quanteMail = elenco.length; Logger.log(quanteMail);
labelName = GmailApp.createLabel('Movimenti - Tavolo tecnico');
for (var i = 0 ; i < quanteMail; i++) {
//for ( var i in elenco) {
elenco[i].addLabel(labelName);
var thr = elenco[i];
var nn = thr.getMessageCount();
Logger.log(nn);
var mesgs = thr.getMessages();
var www = mesgs.length;
for(var j = 0 ; j < www ; j ++){
var attachments = mesgs[j].getAttachments();
var rrr = attachments.length;
for(var k = 0 ; k < rrr ; k ++) {
var attachment = attachments[k];
var attachmentBlob = attachment.copyBlob();
var file = DriveApp.createFile(attachmentBlob);
DriveApp.getFoldersByName(folderName).next().addFile(file);
}
}
}
var c = 0;
var f2s = DriveApp.getFoldersByName(folderName).next().getFiles();
while (f2s.hasNext()) {
var f2 = f2s.next();
c++
}
if ( c > p) { GmailApp.sendEmail("me#me.com", " avviso "+folderName, "Un nuovo file รจ stato aggiunto")};
}
I still don't know why the search by code return more messages out of the parameters, but I workaround it by search in the "wrong" messages only those I need by parse the date, like this, and now it works:
......
var threads = GmailApp.search(query);
for ( var i in threads) {
threads[i].addLabel(labelName);
var mesgs = threads[i].getMessages();
var www = mesgs.length;
for(var j = 0 ; j < www ; j ++){
if ( mesgs[j].getDate() < d ){} else {
var attachments = mesgs[j].getAttachments();
var rrr = attachments.length;
for(var k = 0 ; k < rrr ; k ++) {
var attachment = attachments[k];
var attachmentBlob = attachment.copyBlob();
var file = DriveApp.createFile(attachmentBlob);
DriveApp.getFoldersByName(folderName).next().addFile(file);
}
}
}
}
.......
var select = [];
for (var i = 0; i < nameslots; i += 1) {
select[i] = this.value;
}
This is an extract of my code. I want to generate a list of variables (select1, select2, etc. depending on the length of nameslots in the for.
This doesn't seem to be working. How can I achieve this? If you require the full code I can post it.
EDIT: full code for this specific function.
//name and time slots
function gennametime() {
document.getElementById('slots').innerHTML = '';
var namelist = editnamebox.children, slotnameHtml = '', optionlist;
nameslots = document.getElementById('setpresentslots').value;
for (var f = 0; f < namelist.length; f += 1) {
slotnameHtml += '<option>'
+ namelist[f].children[0].value
+ '</option>';
};
var select = [];
for (var i = 0; i < nameslots; i += 1) {
var slotname = document.createElement('select'),
slottime = document.createElement('select'),
slotlist = document.createElement('li');
slotname.id = 'personname' + i;
slottime.id = 'persontime' + i;
slottime.className = 'persontime';
slotname.innerHTML = slotnameHtml;
slottime.innerHTML = '<optgroup><option value="1">00:01</option><option value="2">00:02</option><option value="3">00:03</option><option value="4">00:04</option><option value="5">00:05</option><option value="6">00:06</option><option value="7">00:07</option><option value="8">00:08</option><option value="9">00:09</option><option value="10">00:10</option><option value="15">00:15</option><option value="20">00:20</option><option value="25">00:25</option><option value="30">00:30</option><option value="35">00:35</option><option value="40">00:40</option><option value="45">00:45</option><option value="50">00:50</option><option value="55">00:55</option><option value="60">1:00</option><option value="75">1:15</option><option value="90">1:30</option><option value="105">1:45</option><option value="120">2:00</option></optgroup>';
slotlist.appendChild(slotname);
slotlist.appendChild(slottime);
document.getElementById('slots').appendChild(slotlist);
(function (slottime) {
slottime.addEventListener("change", function () {
select[i] = this.value;
});
})(slottime);
}
}
You'll have to close in the iterator as well in that IIFE
(function (slottime, j) {
slottime.addEventListener("change", function () {
select[j] = this.value;
});
})(slottime, i);
and it's only updated when the element actually change
The cool thing about JavaScript arrays is that you can add things to them after the fact.
var select = [];
for(var i = 0; i < nameSlots; i++) {
var newValue = this.value;
// Push appends the new value to the end of the array.
select.push(newValue);
}
I've Been using XSLT to display my xml page. I make use of the following to get the data from the xml file:
< xsl:value-of
select="ClinicalDocument/component/structuredBody/component[3]/section/text/table/tbody"/
>
After this, I have the following javascript to clean up the data and do the conversion:
-----------Get Content for Grids----------
//Split Content into array
var purposeArray = document.getElementById('purposeOfVisit').innerHTML.split("\n");
var activeProblemArray = document.getElementById('activeProblems').innerHTML.split("\n");
//------------ Remove All Unwanted Values-----------\\*/
var newDataString ="";
for( var k = 0; k < purposeArray.length; k++ )
{
newDataString += purposeArray[k] + "__";
}
newDataString = newDataString.replace(/ /g,"");
newDataString = newDataString.replace(/__________/g,"__-__");
var newDataArray = newDataString.split("__");
//------------- Save Values in final Array -------------\\*/
var semiFinalArray = new Array();
for( var x=0; x < newDataArray.length; x++)
{
if(newDataArray[x].length != 0)
{
semiFinalArray.push(newDataArray[x]);
}
}
var finalArray = new Array();
var counter = 0;
//------------ Find Number of Columns in row ------------\\*/
var numberOfRows = document.getElementById('numberOfRows').innerHTML;
var numberOfColumns = document.getElementById('numberOfColumns').innerHTML;
var columnsPerRow = parseInt(numberOfColumns) / parseInt(numberOfRows);
//------------------------------Testing ------------------------------//
var dataNamePre = "dataValue";
var temporaryArray = new Array();
var dataName;
//----------- Generate Grid Values -----------//
for( var b=0 ; b < semiFinalArray.length ; b = b + columnsPerRow)
{
var problemComment = "";
counter = 0;
var obj;
for( var a=0 ; a < columnsPerRow ; a++)
{
dataName = dataNamePre + counter.toString() + "";
//-------Generate Grid Titles------//
temporaryArray.push("Title " + (counter+1));
var key = "key"+a;
obj = { values : semiFinalArray[b+a] };
var problemComment = "";
finalArray.push(obj);
counter++;
}
}
//---------------------Generate GridArray---------------------------//
var gridArray = [];
var gridArrayHead = new Array();
counter = 0;
var objectValue = new Array();
for( var x = 0; x < finalArray.length; x++ )
{
objectValue = { head:temporaryArray[x], values: finalArray[x].values }
gridArray.push(objectValue);
}
var provFacilities = [];
for( var x = 0; x < finalArray.length; x++ )
{
provFacilities[x] =
{
head:temporaryArray[x], values: finalArray[x].values
}
}
//alert(gridArray);
$("#grid").kendoGrid(
{
columns:
[{
title:gridArray.head,
template:'#= values #'
}],
dataSource: {
data:finalArray,
pageSize:10
},
scrollable:false,
pageable:true
});
This may be a roundabout method, but I'm still prettry new to this method of coding.
Currently, all the data is being presented in one column, with the last value in my temporaryArray as the title for the column.
Everything works until I try to set the DataSource for the Kendo Grid. When working in the columns property in the grid, I made the following change:
title:gridArray[0].head
When this is done, the title is changed to the first value in the array.
What I want to know is how can I generate columns in the Kendo Grid According to the title? Is there a way to loop through all the values and create the objects from there, seeing that the date that is being sent to the grid are objects in an Array?
What I basically want is something to make this work, without the repitition:
var myGrid = $("#grid").kendoGrid( { columns: [ {
title: temporaryArray[0],
field: finalArray[0].values }, {
title: temporaryArray[1],
field: finalArray[1].values }, {
title: temporaryArray[2],
field: finalArray[2].values }, {
title: temporaryArray[3],
field: finalArray[3].values }, {
title: temporaryArray[4],
field: finalArray[4].values } ]
)};
Any help appreciated, thanks!
This issue has been fixed using the following coding:
var arrayData = [];
for( var x = 0; x < semiFinalArray.length; x=x+5 )
{
var tempArr = new Array();
for( var y = 0; y < 5; y++ )
{
var num = x + y;
tempArr.push(semiFinalArray[num]);
}
arrayData.push(tempArr);
}
var dataTitles = [];
for( var x = 0; x < titleArray.length; x++ )
{
var head = "";
head = titleArray[x];
head = head.replace(/ /g,"");
dataTitles.push(head);
}
var counter = 0;
var columnDefs = [];
for (var i = 0; i < columnsPerRow.length; i++)
{
if (counter == (columnsPerRow - 1))
{
counter = 0;
}
columnDefs.push({ field: dataTitles[counter], template: arrayData[i].values });
counter++;
}
// Create final version of grid array
var gridArray = [];
for (var i = 0; i < arrayData.length; i++)
{
var data = {};
for (var j = 0; j < dataTitles.length; j++)
{
data[dataTitles[j]] = arrayData[i][j];
}
gridArray.push(data);
}
// Now, create the grid using columnDefs as argument
$("#grid").kendoGrid(
{
dataSource:
{
data: gridArray,
pageSize: 10
},
columns: columnDefs,
scrollable: false,
pageable: true
}).data("kendoGrid");
With this, the data is displayed in the DataGrid.