Show hidden columns in Kendo grid excel export - excel

I have a kendo grid and I can export its data into excel file without any problem. In my grid, some columns may be hidden because they do not have any value. However, I want even these hidden columns (I mean their header) be in my exported excel file.
Here is a piece of code showing the excel config in my Kendo grid configuration.
excel: {
fileName: new Date().toString() + ".xlsx",
allPages: true,
},
Any help would be appreciated.

You can have columns in an array which defines hidden: true and then simply traverse through columns array and show/hide columns just before export as following:
function excelExport(e) {
if (!exportFlag) {
for(var i=0; i < columns.length; i++) {
if(columns[i].hidden)
e.sender.showColumn(i);
}
e.preventDefault();
exportFlag = true;
setTimeout(function () {
e.sender.saveAsExcel();
});
} else {
for(var i=0; i < columns.length; i++) {
if(columns[i].hidden)
e.sender.hideColumn(i);
}
exportFlag = false;
}
}

You can add some javascript to control this.
var exportFlag = true;
$("#gridName").data("kendoGrid").bind("excelExport", function (e) {
if (exportFlag) {
e.sender.showColumn("hiddenColumnName");
e.preventDefault();
exportFlag = false;
e.sender.saveAsExcel();
} else {
e.sender.hideColumn("hiddenColumnName");
exportFlag = true;
}
});
Basically this catches the excelExport event when you click the Export button and shows the hidden column in your grid before it fires the saveAsExcel() function which saves your document. Then it hides the column again.
Here is an Example you can test with.

I was looking to achieve a similar thing and used the answer provided by #Ankur with slight modification as I needed to hide the columns again after the export.
Code as follows:
excelExport(e) {
Spa.startLoading(); // loading overlay to hide the columns showing then hiding again
var columns = e.sender.columns;
var hiddenColumnNumbers = [];
if (!exportFlag) {
for (let i = 0; i < columns.length; i++) {
if (columns[i].hidden) {
e.sender.showColumn(i);
hiddenColumnNumbers.push(i);
}
}
e.preventDefault();
exportFlag = true;
setTimeout(() => {
e.sender.saveAsExcel();
for (let j = 0; j < columns.length; j++) {
if (hiddenColumnNumbers.indexOf(j) > -1) {
e.sender.hideColumn(j);
}
}
Spa.stopLoading(); // hide loading overlay
});
} else {
for (let k = 0; k < columns.length; k++) {
if (columns[k].hidden)
e.sender.hideColumn(k);
}
exportFlag = false;
Spa.stopLoading(); // hide loading overlay
}
},

Related

Unable to delete Mutiple Comp from listbox with Extendscript

I can't perform multiple delette from my listbox although I made the listbox mutiselect Why ??
I need your help . You can see the full Script down there.
(function(){
$.win = new Window("palette");
var win = $.win;
win.orientation = "column";
win.alignChildren = ["center", "top"];
win.spacing = 10;
win.margins = 16;
var listbox1 = win.add("listbox", undefined, undefined, { name: "listbox1", multiselect: true, columnTitles: "Max", showHeaders: true });
listbox1.preferredSize.width = 136;
listbox1.preferredSize.height = 208;
var button1 = win.add("button", undefined, undefined, { name: "button1" });
button1.text = "Search";
var button2 = win.add("button", undefined, undefined, { name: "button2" });
button2.text = "Delete";
win.show();
var myNewArray = [];
button1.onClick = function Search() {
var compsArray = new Array();
var myProj = app.project;
myNewArray = [];
listbox1.removeAll();
for (var i = 1; i <= myProj.numItems; i++) {
if (myProj.item(i) instanceof CompItem) {
myNewArray = compsArray[compsArray.length] = myProj.item(i);
listbox1.add("item", myNewArray.name);
}
}
}
button2.onClick = function deletecomps() {
for (var s = 1; s <= app.project.numItems; s ++) {
if ((app.project.item(s) instanceof CompItem) && (app.project.item(s).name.match(listbox1.selection))) {
myComp = app.project.item(s);
break;
}
}
app.project.item(s).remove ();
}
})();
You can see an image to clarify the script in AE
Your problem is that listbox1.selection in line 34
if ((app.project.item(s) instanceof CompItem) && (app.project.item(s).name.match(listbox1.selection))) {
is an array, and you're trying to match it to a string returned by app.project.item(s).name which is never going to match.
Also, what are you trying to achieve with the lines
myComp = app.project.item(s);
break;
Here's the onClick function, but it works. It loops through the selection, and looks for a matching project item, based on the text of the listbox matching the comp's name. This is dangerous, because identical comp names would create false positives. I strongly suggest you don't use this technique in production code, because it will definitely cause problems for your users.
Also I'd turn the part wherre you populate the list into a separate function, and call it after you click delete, so that the list is refreshed, because at the moment the list stays the same, even after the comp is deleted.
button2.onClick = function deletecomps() {
for (var b= 0; b < listbox1.selection.length; b++){
for (var s = 1; s <= app.project.numItems; s ++) {
if ((app.project.item(s) instanceof CompItem) && (app.project.item(s).name.match(listbox1.selection[b].text))) {
app.project.item(s).remove ();
}
}
}
}

Why break statement does not work in else loop, is there any alternative to this?

Inside nested for loop I am using if/else if else condition. When if and else if condition are false, final else condition is running as expected but as it is in for loop instead of just running once it is running multiple times. What changes do i need to make to make else condition work only once?
Here is my code
productCodes: string[] = [],
vehicleType: string[] = [],
for (var i = 0; i < urls.length; i++) {
return https.get(urls[i], res => {
res.on('data', function(chunk) {
json += chunk;
});
res.on('end', function() {
var result = JSON.parse(json);
for (var i = 0; i < result.length; i++) {
for (var j = 0; j < result[i].products.length; j++) {
if (productCodes.length !== 0 && productCodes !== undefined) {
for (var k = 0; k < productCodes.length; k++) {
if (result[i].products[j].productCode == productCodes[k]) {
console.log(
'Product Codes: ' +
result[i].products[j].productCode
);
}
}
} else if (
vehicleType.length !== 0 &&
vehicleType !== undefined
) {
for (var k = 0; k < vehicleType.length; k++) {
if (result[i].products[j].productType == vehicleType[k]) {
console.log(
'Product Codes: ' +
result[i].products[j].productCode
);
}
}
} else {
console.log('No data');
break; ------------------------> HERE
}
}
}
});
});
}
```
If the loops are to cease after you encounter your last "else" statement I would recommend breaking them, if necessary using labels.
Here is a related question! It's for java, but the syntax is similar.
If it is just about only displaying the message once, consider just setting a boolean, then asking for it after the loops have concluded, and if true log the message.
EDIT: To expand my answer for your edit, I am not sure what exactly your desired behaviour would be, but chances are you want to break a loop deeper down than the topmost one. break; will break the topmost loop, in your case I believe that would be
for (var j = 0; j < result[i].products.length; j++)
But not the loop directly above that, iterating over this loop all over again.
Try assigning a label to the loop you want to break further down, and then break that one specifically.
EDIT 2:
I've modified your code to include the example of labels. That way, you can break whichever loop you actually want to end. Just comment the appropriate break back in. Hope this is helpful!
productCodes: string[] = [],
vehicleType: string[] = [],
outer1:
for (var i = 0; i < urls.length; i++)
{
return https.get(urls[i], res =>
{
res.on('data', function(chunk)
{
json += chunk;
});
res.on('end', function()
{
var result = JSON.parse(json);
middle1:
for (var i = 0; i < result.length; i++)
{
for (var j = 0; j < result[i].products.length; j++)
{
if (productCodes.length !== 0 && productCodes !== undefined)
{
for (var k = 0; k < productCodes.length; k++)
{
if (result[i].products[j].productCode == productCodes[k])
{
console.log(
'Product Codes: ' +
result[i].products[j].productCode
);
}
}
}
else if (
vehicleType.length !== 0 &&
vehicleType !== undefined
)
{
for (var k = 0; k < vehicleType.length; k++)
{
if (result[i].products[j].productType == vehicleType[k])
{
console.log(
'Product Codes: ' +
result[i].products[j].productCode
);
}
}
}
else
{
console.log('No data');
//break outer1;
//break middle1;
//break;
-- -- -- -- -- -- -- -- -- -- -- -- > HERE
}
}
}
});
});
}
As an additional note, nested labled loops or blocks like these are fairly uncommon due to the tendency to be able to resolve loops things in their own individual functions and then simply call those. I'd advise you to take a look at the java-based answer I linked above if you wanted to look into that.

Correct way to export data to excel?

I am currently trying to export some data from my application in either Excel or CSV. What is the best way to accomplish this? Should I export from the backend, or export once I have the data client side using a library within Angular 2? My Web API 2 controller currently produces a list and then sends it as JSON to the front end.
That all works, I am just struggling with exporting the list.
Here is a sample of what I am doing
[HttpGet]
[Route("/api/preview/{item}")]
public IActionResult Preview(string item)
{
if (item!= null)
{
var preview = _context.dbPreview.FromSql("Exec sampleStoredProcedure {0}, 1", item).ToList();
return Ok(preview);
}
}
That is how I am generating my data that is sent to Angular 2.
I can provide any Angular 2 code if it is necessary but it is just a normal service. Was not sure if there was some library that worked well with Angular 2 to do an export. I've seen some things for javascript but alaSQL but it does not seem like it would work with Angular 2.
Any ideas?
I've looked at the source code from PrimeNG DataTable and I think you can use the exportCSV code for exporting a csv of your data.
The "trick" is to generate a string starting with data:text/csv;charset=utf-8 and make this downloadable by the user.
Something like the following code should work for you (maybe you need to modify it a bit so it fits to your data).
Most of the code is copied from PrimeNG except the download method. That method is copied from a SO answer.
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app works!';
csvSeparator = ';';
value = [
{ name: 'A3', year: 2013, brand: 'Audi' },
{ name: 'Z3', year: 2015, brand: 'BMW' }
];
columns = [
{ field: 'name', header: 'Name' },
{ field: 'year', header: 'Production data' },
{ field: 'brand', header: 'Brand' },
];
constructor() {
console.log(this.value);
this.exportCSV('cars.csv'); // just for show casing --> later triggered by a click on a button
}
download(text, filename) {
let element = document.createElement('a');
element.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
exportCSV(filename) {
let data = this.value, csv = '';
// csv = "data:text/csv;charset=utf-8,";
//headers
for (let i = 0; i < this.columns.length; i++) {
if (this.columns[i].field) {
csv += this.columns[i].field;
if (i < (this.columns.length - 1)) {
csv += this.csvSeparator;
}
}
}
//body
this.value.forEach((record, j) => {
csv += '\n';
for (let i = 0; i < this.columns.length; i++) {
if (this.columns[i].field) {
console.log(record[this.columns[i].field]);
// resolveFieldData seems to check if field is nested e.g. data.something --> probably not needed
csv += record[this.columns[i].field]; //this.resolveFieldData(record, this.columns[i].field);
if (i < (this.columns.length - 1)) {
csv += this.csvSeparator;
}
}
}
});
// console.log(csv);
// window.open(encodeURI(csv)); // doesn't display a filename!
this.download(csv, filename);
}
// resolveFieldData(data: any, field: string): any {
// if(data && field) {
// if(field.indexOf('.') == -1) {
// return data[field];
// }
// else {
// let fields: string[] = field.split('.');
// let value = data;
// for(var i = 0, len = fields.length; i < len; ++i) {
// value = value[fields[i]];
// }
// return value;
// }
// }
// else {
// return null;
// }
// }
}
AWolfs answer got me on the right track but I did some tweaking to get it working with Internet Explorer.
This function converts my array to my string for my csv file. (I had to create a new object that was my column headers). I then just pass the data that is generated by my service to the function and it does the parsing for me. For more complex data I believe you would need to do some additional logic but I have basic text so it all worked out for me.
exportCSV(filename, CsvData) {
let data = CsvData, csv = '';
console.log(data);
//headers
for (let i = 0; i < this.columns.length; i++) {
if (this.columns[i].field) {
csv += this.columns[i].field;
if (i < (this.columns.length - 1)) {
csv += this.csvSeparator;
}
}
}
//body
CsvData.forEach((record, j) => {
csv += '\n';
for (let i = 0; i < this.columns.length; i++) {
if (this.columns[i].field) {
console.log(record[this.columns[i].field]);
csv += record[this.columns[i].field];
if (i < (this.columns.length - 1)) {
csv += this.csvSeparator;
}
}
}
});
this.DownloadFile(csv, filename);
}
That was pretty much the same as AWolfs answer but I had to make some modifications to the DownloadFile function to get it to work with additional browsers. This function just accepts the huge string that makes up your .CSV file and the filename.
DownloadFile(text, filename) {
console.log(text);
var blob = new Blob([text], { type: 'text/csv;charset=utf-8;' });
if (navigator.msSaveBlob) { // IE 10+
navigator.msSaveBlob(blob, filename);
}
else //create a link and click it
{
var link = document.createElement("a");
if (link.download !== undefined) // feature detection
{
// Browsers that support HTML5 download attribute
var url = URL.createObjectURL(blob);
link.setAttribute("href", url);
link.setAttribute("download", filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
}
This code needs cleaned up but I wanted to update my question with an answer for anyone who was struggling with the same thing. This should at least get you started. This works in both Chrome and IE.
Thanks.

Creating a attachment from Google sheet in ms Excel format via Google script

Is it possible to create an Excel format file from a Google sheet using Google script, so that it can be added as an attachment to an email?
I've got a code that takes columns with certain names (e.g. A, C, F) and turns them into a new sheet (on createCustomStatusTable() function).
https://docs.google.com/spreadsheets/d/1fZ0JMYjoIrfPIxFBVgDNU0x5X0ll201ZCU-lcaTwwcI/edit?usp=sharing
var expected = ['A','C','F'];
var newSpreadSheet = SpreadsheetApp.getActiveSheet();
var tableLastRow = newSpreadSheet.getLastRow();
var tablelastColumn = newSpreadSheet.getLastColumn();
var values = newSpreadSheet.getRange(1, 1, tableLastRow, tablelastColumn).getValues();
var rangeToCopy = [];
function in_array(value, array)
{
for(var i = 0; i < array.length; i++)
{
if(array[i] == value) return true;
}
return false;;
};
function columnsCount() {
var count = 1;
for (var i = 0; i < SpreadsheetApp.getActiveSheet().getLastColumn(); i++) {
if (in_array(values[0][i],expected))
count++;
}
return count;
};
function returnRange() {
for (var i = 1; i < SpreadsheetApp.getActiveSheet().getLastColumn()+1; i++) {
if (in_array(values[0][i-1],expected)) {
rangeToCopy.push(newSpreadSheet.getRange(1, i, newSpreadSheet.getMaxRows()));
};
};
return rangeToCopy;
};
function createCustomStatusTable() {
var targetSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Target');
for (var i = 1; i < columnsCount(); i++) {
returnRange()[i-1].copyTo(targetSheet.getRange(1,i));
};
};
Thank you in advance for any help
You can create an EXCEL type file with DriveApp:
The problem is, that the content must be a string. And I haven't tested for a way to make that work.
I know this doesn't answer your question. Hopefully someone knows for sure how to create an EXCEL file from a Google Sheet.

Dropdown field - first item should be blank - For more than one field (Sharepoint)

I was looking for a solution to the problem of getting a blank default when using a lookup in a field in Sharepoint. Kit Menke's solution to "Dropdown field - first item should be blank" question works perfectly for my first field with a lookup. But I can't make it work if have more that one field in the same list where I need to insert a blank for each lookup field (works only for the first field). I tried adding a new "Web Part" and applying the same code to the second field, but doesn't work. Any ideas? Thanks in advance
Follow-up to my answer here: Dropdown field - first item should be blank
Version 2.0 allows you to add the names of your dropdowns to dropdownNames in the MyCustomExecuteFunction function. As with the first one, this will work only with required single select lookup fields. Also, in order to edit the page again and update your Content Editor Web Part you may have to choose a value for your dropdowns otherwise you get the dreaded An unexpected error has occurred.. Good luck! :D
<script type="text/javascript">
function GetDropdownByTitle(title) {
var dropdowns = document.getElementsByTagName('select');
for (var i = 0; i < dropdowns.length; i++) {
if (dropdowns[i].title === title) {
return dropdowns[i];
}
}
return null;
}
function GetOKButtons() {
var inputs = document.getElementsByTagName('input');
var len = inputs.length;
var okButtons = [];
for (var i = 0; i < len; i++) {
if (inputs[i].type && inputs[i].type.toLowerCase() === 'button' &&
inputs[i].id && inputs[i].id.indexOf('diidIOSaveItem') >= 0) {
okButtons.push(inputs[i]);
}
}
return okButtons;
}
function AddValueToDropdown(oDropdown, text, value, optionnumber){
var options = oDropdown.options;
var option = document.createElement('OPTION');
option.appendChild(document.createTextNode(text));
option.setAttribute('value',value);
if (typeof(optionnumber) == 'number' && options[optionnumber]) {
oDropdown.insertBefore(option,options[optionnumber]);
}
else {
oDropdown.appendChild(option);
}
oDropdown.options.selectedIndex = 0;
}
function WrapClickEvent(element, newFunction) {
var clickFunc = element.onclick;
element.onclick = function(event){
if (newFunction()) {
clickFunc();
}
};
}
function MyCustomExecuteFunction() {
// **** ADD YOUR REQUIRED SINGLE SELECT FIELDS HERE ***
var dropdownNames = [
'Large Lookup Field',
'My Dropdown Field'
];
var dropdownElements = [];
for (var d = 0; d < dropdownNames.length; d++) {
// find the dropdown
var dropdown = GetDropdownByTitle(dropdownNames[d]);
// comment this IF block out if you don't want an error displayed
// when the dropdown can't be found
if (null === dropdown) {
alert('Unable to get dropdown named ' + dropdownNames[d]);
continue;
}
AddValueToDropdown(dropdown, '', '', 0);
// collect all of our dropdowns
dropdownElements.push(dropdown);
}
// add a custom validate function to the page
var funcValidate = function() {
var isValid = true;
var message = "";
for (var d = 0; d < dropdownElements.length; d++) {
if (0 === dropdownElements[d].selectedIndex) {
// require a selection other than the first item (our blank value)
if (isValid) {
isValid = false;
} else {
message += "\n"; // already had one error so we need another line
}
message += "Please choose a value for " + dropdownNames[d] + ".";
}
}
if (!isValid) {
alert(message);
}
return isValid;
};
var okButtons = GetOKButtons();
for (var b = 0; b < okButtons.length; b++) {
WrapClickEvent(okButtons[b], funcValidate);
}
}
_spBodyOnLoadFunctionNames.push("MyCustomExecuteFunction");
</script>
How about prepending a null option to the select menu of sharepoint.Like,
$('#idOfSelectMenu').prepend('<option value="" selected>(None)</option>');
I used this approach and append this code only in the NewForm.aspx because in EditForm.aspx it will override the selected option.

Resources