Grouping the tables with dropdown box - html

I am a beginner in google app script. So right now I am doing a project where users can sign in and can view their payment history. So for now it is just showing from 2020 until 2021. So I want your guys help on creating a dropdown box which states (eg : 2020 , 2021 ) so maybe if the user clicks 2020 then they can see the payment history of 2020 only. I really need your guys help in this thing. I have attached the link to my google app script and a image to explain myself better. Thank you guys.
https://script.google.com/d/1DdRKqUX__-ZITUgTZanQ_A7hUL1kcc0TZOeFmn58wYsX_o_7cqNExnYo/edit?usp=sharing - Link to my appscript
First image
Second Image

Here is a sample code you can refer with:
WebAppLogin.html (modifications)
<script>
function GetRecords() {
var spin = "<span class=\"spinner-border spinner-border-sm\" role=\"status\" aria-hidden=\"true\"></span>";
spin += " Loading...";
document.getElementById("LoginButton").innerHTML = spin;
var username = document.getElementById("username").value;
var password = document.getElementById("password").value;
google.script.run.withSuccessHandler(function(output) {
console.log(output);
var username = output[1];
var name = output[2];
if(output[0] == 'TRUE') {
document.getElementById("errorMessage").innerHTML = "";
document.getElementById("currentUser").value = username;
google.script.run.withSuccessHandler(displayTable).GetRecords(username,"None");
} else if(output[0] == 'FALSE') {
document.getElementById("firstLastName").innerHTML = "";
document.getElementById("currentUser").value = "";
document.getElementById("myFilter").innerHTML = "";
document.getElementById("errorMessage").innerHTML = "Failed to Login";
document.getElementById("LoginButton").innerHTML = "Login";
}
}).checkLogin(username, password);
}
function filter(){
var filterStr = document.getElementById("filterYear").value;
var user = document.getElementById("currentUser").value;
google.script.run.withSuccessHandler(displayTable).GetRecords(user,filterStr);
}
function displayTable(result) {
var ar = result.data;
var filterString = result.filter;
var username = document.getElementById("currentUser").value;
if(ar.length > 0) {
var displayTable = '<table class=\"table\" id=\"mainTable\" >';
displayTable += "<tr>";
displayTable += "<th>Month</th>";
displayTable += "<th>House Number</th>";
displayTable += "<th>Street</th>";
displayTable += "<th>Payment Status</th>";
displayTable += "</tr>";
ar.forEach(function(item, index) {
displayTable += "<tr>";
displayTable += "<td>"+item[0]+"</td>";
displayTable += "<td>"+item[1]+"</td>";
displayTable += "<td>"+item[2]+"</td>";
displayTable += "<td>"+item[3]+"</td>";
displayTable += "</tr>";
});
displayTable += "</table>";
} else {
var displayTable = "<span style=\"font-weight: bold\" >No Records Found</span>";
}
var filter = '';
if(filterString.length > 0) {
filter += '<label for="years" style="font-size: 20px">Years</label><br><select class="form-control form-control-sm" id="filterYear" name="years" required><option value="" selected>Choose...</option>';
filterString.forEach(str => {
filter += '<option value="'+str+'">'+str+'</option>';
});
filter += '</select><button class="btn btn-primary" type="button" id="FilterButton" onclick="filter()" >Submit</button>';
}
//var filter = '<label for="years" style="font-size: 20px">Years</label><br><select class="form-control form-control-sm" id="filterYear" name="years" required><option value="" selected>Choose...</option><option value="2020">2020</option><option value="2021">2021</option></select><button class="btn btn-primary" type="button" id="FilterButton" onclick="filter()" >Submit</button>';
document.getElementById("digitalgoods-030521182921-1").style.display = "block";
document.getElementById("displayRecords").innerHTML = displayTable;
document.getElementById("firstLastName").innerHTML = "USER: " + name;
document.getElementById("myFilter").innerHTML = filter;
document.getElementById("LoginButton").innerHTML = "Login";
document.getElementById("username").value = '';
document.getElementById("password").value = '';
}
</script>
<div>
<h2 id="firstLastName">
</h2>
</div>
<input type="hidden" id="currentUser" value=""/>
<div id ="myFilter" class="form-group">
</div>
</div>
<div id="displayRecords" style="padding: 10px;" >
</div>
Modifications done:
Include empty form-group class
Include hidden input to hold current logged-in user
Create a reusable function displayTable()
Create an html content for the drop-down filter. See variable filter.
Include another argument when calling GetRecords(username, filter)
Create a new function filter()
During initial log-in, filter will be set to "None". filter will be set depending on the option selected
Code.gs (modifications)
function GetRecords(username,filter) {
var filteredDataRangeValues = GetUsernameAssociatedProperties(username);
var resultArray = GetPaymentRecords(filteredDataRangeValues,filter);
var resultFilter = getYears();
result = {
data: resultArray,
filter: resultFilter
};
return result;
}
function getYears() {
var ss= SpreadsheetApp.openByUrl(url);
var yearSheet = ss.getSheetByName("Configuration");
var getLastRow = yearSheet.getLastRow();
var return_array = [];
for(var i = 2; i <= getLastRow; i++)
{
if(return_array.indexOf(yearSheet.getRange(i, 2).getDisplayValue()) === -1) {
return_array.push(yearSheet.getRange(i, 2).getDisplayValue());
}
}
return return_array;
}
function GetPaymentRecords(userProperties,filter) {
var transpose = m => m[0].map((_, i) => m.map(x => x[i]));
var resultArray = [];
var ss = SpreadsheetApp.openByUrl(url);
var displaySheet = ss.getSheetByName(streetSheetName);
var addressValues = displaySheet.getRange("B:C").getValues();
var paidMonthValues = displaySheet.getRange("G:AD").getValues();
//Logger.log(addressValues);
//Logger.log(transpose(paidMonthValues));
userProperties.forEach((v, i) => {
var userHouseNumber = v[1];
var userStreet = v[2];
var column = addressValues.reduce(function callbackFn(accumulator, currentValue, index, array) {
if (currentValue[0] == userHouseNumber && currentValue[1] == userStreet) {
return index
} else {
return accumulator
}
}, '');
//Logger.log(column);
Logger.log(filter)
Logger.log(paidMonthValues);
if(filter=="None"){
var result = transpose(paidMonthValues).map(function callbackFn(element, index, array) {
return [element[0], userHouseNumber, userStreet, element[column] || '']
});
}else{
var result = transpose(paidMonthValues).map(function callbackFn(element, index, array) {
if(element[0].includes(filter))return [element[0], userHouseNumber, userStreet, element[column] || '']
});
}
resultArray = resultArray.concat(result);
//Logger.log(resultArray);
})
//Remove null elements
resultArray = resultArray.filter(element=>{
Logger.log(element!=null)
return element != null;
});
return resultArray;
}
Modifications done:
Modified GetRecords() and GetPaymentRecords() to include filter option
Add removal of null elements in the resultArray. (Null elements may exist when filter option was used due to the map() used)
Output:
(After user logged-in)
(After user selects a filter)
(UPDATE):
The following modifications where done to create a drop-box based on the list of years available in the configuration sheet.
WebAppLogin.html
displayTable() was modified that will accept an object as its parameter which contains an array data and an array of filter strings.
displayTable() was modified to update the drop-down options based on the filter strings available
Code.gs
getYears() was added that will read the sheet "Configuration" to get the filter string values
GetRecords() was modified to return an object which contains an array of record data and an array of filter strings.

Related

Get Google sheets to updatable JSON script to only need Header row

I have inherited a Google sheet script that generates a Live updatable JSON link from sheets chosen from an HTML form in a sidebar
At the start of a project, the only data that might be present is the Headers on both the Element and Connection sheets (and they are always present) and as the project matures rows of data are added to one or both sheets
My issue is that the script to generate the json requires that at least two rows of data exist (on both sheets) the Header row and a data row
When you run the script you get a pop up with a link then you click that link and a new browser tab opens with the JSON data. It is at this step I get the error
Line 161 in the gs modual is properties.setProperty("settings", newProperties); from
function setExportProperties(newProperties){
var properties = PropertiesService.getDocumentProperties();
var prop = properties.getProperty("settings");
properties.setProperty("settings", newProperties);
}
But this is set on line 292 from the HTML module
function setProperties(){
return google.script.run.setExportProperties(generateProperties());
}
I have tried for hours to figure out how to modify the script so only the header row is necessary but I have not achieved this
There is a lot of code here and it seems to me too much to post it but it is well documented in the code and I think someone who knows what they are doing can figure out pretty easily what needs to be done
From the added menu click Open Sidebar to get to the form. I have set it up in advance
Thank you in advance for your help with this
gs code
//===================================================================================================================
//ExportJson Code
//===================================================================================================================
//Displays an HTML-service dialog in Google Sheets that contains client-side JavaScript code for the Google Picker API.
function showForm() {
var html = HtmlService.createHtmlOutputFromFile('Sidebar.html')
.setTitle('Export Sheet As JSON Data')
.setWidth(300)
.setHeight(480)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showSidebar(html);
}
//function showData(elements,connections,headerrow,datarow,prefix,separator){
function showData(sheets,settings){
//Sheets Array
var elements=sheets[0];
var connections=sheets[1];
//Settings Array
var emptycells=settings[0];
var headerrow=settings[1];
var datarow=settings[2];
var prefix=settings[3];
var separator=settings[4];
var keepignore=settings[5];
var cellarrays=settings[6];
var separator2=settings[7];
var columnignore=settings[8];
var colprefix=settings[9];
var sheetid=SpreadsheetApp.getActiveSpreadsheet().getId();
if(elements=='' && connections==''){
Browser.msgBox('Select any one or both sheets and run again!');
return;
}
if(headerrow=='' || headerrow<=0){
Browser.msgBox('Please enter row number of your data header, it should be an integer value greater than 0');
return;
}
if(datarow=='' || datarow<=0){
Browser.msgBox('Please enter row number where data starts, it should be an integer value greater than 0');
return;
}
if(keepignore==true){
if(prefix=='' && separator!=''){
Browser.msgBox('Please Column Prefix And Array Separator');
return;
}
}
if(columnignore==true){
if(colprefix=='' || colprefix==false){
Browser.msgBox('Please Enter Column Prefix');
return;
}
}
if(cellarrays==true){
if(separator2=='' || separator2==false){
Browser.msgBox('Please Enter Array Separator');
return;
}
}
if(elements=='' && connections==''){
Browser.msgBox('Select sheet(s)!');
return;
}
var webappurl='https://script.google.com/macros/s/AKfycbwn.../exec';
webappurl=webappurl + '?id=' + sheetid;
if(elements!='')
webappurl=webappurl + '&sheet1=' + elements;
else
webappurl=webappurl + '&sheet1=' + '';
if(connections!='')
webappurl=webappurl + '&sheet2=' + connections;
else
webappurl=webappurl + '&sheet2=' + '';
webappurl=webappurl + '&header=' + headerrow;
webappurl=webappurl + '&startrow=' + datarow;
if(keepignore==true){
if(prefix=='' || separator==''){
webappurl=webappurl + '&prefix=' + 'false';
webappurl=webappurl + '&separator=' + 'false';
}
else{
webappurl=webappurl + '&prefix=' + prefix;
webappurl=webappurl + '&separator=' + separator;
}
}
else{
webappurl=webappurl + '&prefix=' + 'false';
webappurl=webappurl + '&separator=' + 'false';
}
if(emptycells==true)
webappurl=webappurl + '&emptycells=' + 'yes';
else
webappurl=webappurl + '&emptycells=' + 'no';
if(cellarrays==true){
webappurl=webappurl + '&cellarrays=' + 'yes';
webappurl=webappurl + '&separator2=' + separator2;
}
else{
webappurl=webappurl + '&cellarrays=' + 'no';
webappurl=webappurl + '&separator2=' + 'no';
}
if(columnignore==true){
webappurl=webappurl + '&columnignore=' + 'yes';
webappurl=webappurl + '&colprefix=' + colprefix;
}
else{
webappurl=webappurl + '&columnignore=' + 'no';
webappurl=webappurl + '&colprefix=' + 'no';
}
//Workflows.setCurrentMapSource("");
var kumuurl='Workflows.setCurrentMapSource("'+ webappurl +'")';
openUrl(webappurl,kumuurl);
}
//===================================================================================================================
function openUrl( url,kumuurl ){
var html = HtmlService.createHtmlOutput('<html>'
//+'<script>'
//+'window.close = function(){window.setTimeout(function(){google.script.host.close()},9)};'
//+'var a = document.createElement("a"); a.href="'+url+'"; a.target="_blank";'
//+'if(document.createEvent){'
//+' var event=document.createEvent("MouseEvents");'
//+' if(navigator.userAgent.toLowerCase().indexOf("firefox")>-1){window.document.body.append(a)}'
//+' event.initEvent("click",true,true); a.dispatchEvent(event);'
//+'}else{ a.click() }'
//+'close();'
//+'</script>'
// Offer URL as clickable link in case above code fails.
+'<body style="word-break:break-word;font-family:sans-serif;">' + kumuurl + '</body>'
+'<script>google.script.host.setHeight(150);google.script.host.setWidth(600)</script>'
+'</html>')
.setWidth(600).setHeight(1);
SpreadsheetApp.getUi().showModalDialog( html, "Click the Link below to get the LIVE Link" );
}
//===================================================================================================================
function setExportProperties(newProperties){
var properties = PropertiesService.getDocumentProperties();
var prop = properties.getProperty("settings");
properties.setProperty("settings", newProperties);
}
//===================================================================================================================
function getExportProperties(){
var props = PropertiesService.getDocumentProperties();
var prop = props.getProperty("settings");
//Logger.log(prop);
return prop;
}
//===================================================================================================================
function getSheetList(){
var ss=SpreadsheetApp.getActiveSpreadsheet();
var sheets=ss.getSheets();
var sheetnames=[];
sheetnames[0]="|"
for(var i=0;i<sheets.length;i++){
var sh=sheets[i];
sheetnames[i+1]=sh.getName()+"|"+sh.getName();
}
return sheetnames;
}
//===================================================================================================================
//LiveJson Code
//===================================================================================================================
function doGet(request) {
// Get request params.
var sheetKey = request.parameters.id;
var sheetName1 = request.parameters.sheet1;
var sheetName2 = request.parameters.sheet2;
var callback = request.parameters.callback;
var headerRow = request.parameters.header;
var startRow = request.parameters.startrow;
var prefix = request.parameters.prefix;
var separator = request.parameters.separator;
var emptycells = request.parameters.emptycells;
var cellarrays = request.parameters.cellarrays;
var separator2 = request.parameters.separator2;
var columnignore = request.parameters.columnignore;
var colprefix = request.parameters.colprefix;
// Parse the spreadsheet.
var spreadsheet = SpreadsheetApp.openById(sheetKey); //.getActiveSpreadsheet();
if(sheetName1!=''){
var keys = getHeaderRowKeys_(spreadsheet, sheetName1, headerRow, columnignore, colprefix);
var data1 = readData_(spreadsheet, sheetName1, headerRow, keys, startRow,prefix,separator,emptycells,cellarrays,separator2,columnignore, colprefix);
}
if(sheetName2!=''){
var keys = getHeaderRowKeys_(spreadsheet, sheetName2, headerRow, columnignore, colprefix);
var data2 = readData_(spreadsheet, sheetName2,headerRow, keys, startRow,prefix,separator,emptycells,cellarrays,separator2,columnignore, colprefix);
}
// Write and return the response.
if(sheetName1!='' && sheetName2!=''){
var response = JSON.stringify({ elements: data1,connections: data2 },null,'\t');
}
else if(sheetName1!='' && sheetName2==''){
var response = JSON.stringify({ elements: data1 },null,'\t');
}
else if(sheetName1=='' && sheetName2!=''){
var response = JSON.stringify({ connections: data2 },null,'\t');
}
var output = ContentService.createTextOutput();
if(callback == undefined){
// Serve as JSON
output.setContent(response).setMimeType(ContentService.MimeType.JSON);
}
else{
// Serve as JSONP
output.setContent(callback + "(" + response + ")").setMimeType(ContentService.MimeType.JAVASCRIPT);
}
//output.setContent(callback + "(" + response + ")").setMimeType(ContentService.MimeType.JAVASCRIPT);
return output;
}
//===================================================================================================================
function readData_(spreadsheet, sheetName, headerRow, properties, startRowNum, prefix,separator,emptycells,cellarrays,separator2,columnignore, colprefix) {
if (typeof properties == "undefined") {
properties = getHeaderRowKeys_(spreadsheet, sheetName, headerRow, columnignore, colprefix);
}
var rows = getDataRows_(spreadsheet, sheetName,headerRow, startRowNum,columnignore, colprefix);
var data = [];
if(emptycells=='yes'){//Ignore Empty Cells
for (var i = 0; i<rows.length; i++) {
var row = rows[i];
var record = {};
for (var p in properties) {
if(row[p]!=''){
var keys=properties
record=updateRecord(prefix,keys,p,row,record,prefix,separator,cellarrays,separator2)
//properties[p]=properties[p].replace(prefix,"")
}
}
data.push(record);
}
}
else{//Inclue Empty Cells
for (var i = 0; i<rows.length; i++) {
var row = rows[i];
var record = {};
for (var p in properties) {
var keys=properties
record=updateRecord(prefix,keys,p,row,record,prefix,separator,cellarrays,separator2)
}
data.push(record);
}
}
return data;
}
//===================================================================================================================
function updateRecord(prefix,properties,p,row,record,prefix,separator,cellarrays,separator2){
if(prefix=='false'){
if(cellarrays=='yes'){//Split Cell Values as arrays if contains comma
if(row[p].toString().indexOf(separator2)>=0){//Convert Cell As Array Only If Contains Comma
record[properties[p]]=row[p].toString().split(separator2);
}
else{
record[properties[p]]=row[p];
}
}
else{
record[properties[p]]=row[p];
}
}
else{//Split Entire Column as array if start with a prefix
var key=properties[p];
if(key.indexOf(prefix)==0){
key=key.replace(prefix,"")
if(row[p].toString().indexOf(separator)>=0)
record[key]=row[p].toString().split(separator);
else if(row[p].toString().indexOf(separator2)>=0)
if(cellarrays='yes')
record[key]=row[p].toString().split(separator2);
else
record[key]=row[p];
else
record[key]=row[p];
}
else{
if(cellarrays=='yes'){//Split Cell Values as arrays if contains separator2
if(row[p].toString().indexOf(separator2)>=0){//Convert Cell As Array Only If Contains separator2
record[key]=row[p].toString().split(separator2);
}
else{
record[key]=row[p];
}
}
else{
record[key]=row[p];
}
}
}
return record;
}
//===================================================================================================================
function getDataRows_(spreadsheet, sheetName, headerRow, startRowNum, columnignore, colprefix) {
var properties = getHeaderRow_2(spreadsheet, sheetName, headerRow, columnignore, colprefix);
if (typeof startRowNum == "undefined") startRowNum = 2;
var sheet = spreadsheet.getSheetByName(sheetName);
var values=sheet.getRange(startRowNum, 1, sheet.getLastRow()-1, sheet.getLastColumn()).getValues();
if(columnignore=='yes'){
var data=[];
for(var i=0;i<values.length;i++){
data[i]=[];
var k=0;
for(var j=0;j<properties.length;j++){
if(properties[j].toString().indexOf(colprefix)!=0){
data[i][k]=values[i][j];
k++;
}
}
}
return data;
}
else
return values;
}
//======================================================================================================================================================================================
function getHeaderRowKeys_(spreadsheet, sheetName, rowNum, columnignore, colprefix) {
if (typeof rowNum == "undefined") rowNum = 1;
var header=getHeaderRow_(spreadsheet, sheetName, rowNum, columnignore, colprefix);
return header;
}
//===================================================================================================================
function getHeaderRow_(spreadsheet, sheetName, rowNum, columnignore, colprefix) {
var sheet = spreadsheet.getSheetByName(sheetName);
var values=sheet.getRange(rowNum, 1, 1, sheet.getLastColumn()).getValues();
if(columnignore=='yes'){
var data=[];
for(var i=0;i<values.length;i++){
data[i]=[];
var k=0;
for(var j=0;j<values[0].length;j++){
if(values[i][j].toString().indexOf(colprefix)!=0){
data[i][k]=values[i][j];
k++;
}
}
}
return data[0];
}
else
return values[0];
}
//===================================================================================================================
function getHeaderRow_2(spreadsheet, sheetName, rowNum, columnignore, colprefix) {
var sheet = spreadsheet.getSheetByName(sheetName);
var values=sheet.getRange(rowNum, 1, 1, sheet.getLastColumn()).getValues();
return values[0];
}
HTML code (some CSS remove so as not to exceed char limit)
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons.css">
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<base target="_top">
</head>
<body>
<body onload="populate()"></body>
<div class="main">
<h2 align="center" style="font-family:Open+Sans;"> Select Sheets</h2>
<div class="section">
<div class="block form-group">
<br>
<center> <label for="elmn">Select Elements Sheet</label>
<select id="elmn" name="elmn" style="width: 180px;" ></select>
<br>
<label for="conn">Select Connections Sheet</label>
<select id="conn" name="conn" style="width: 180px;" ></select></center>
</div>
</div>
<h2 align="center" style="font-family:Open+Sans;">General Data</h2>
<div class="section">
<div id="new-div">
<center>Header Row:<br>
<input type="text" name="Header Row" value="1" id="headerrow">
<br><br>
Start Data Row:<br>
<input type="text" name="Data Row" value="2" id="datarow"> </center>
</div>
</div>
<h2 align="center" style="font-family:Open+Sans;">JSON Settings</h2>
<div class="buttonDiv" >
<!--<div class="newButtonDiv"></div>-->
<button class="action" value="Run" onclick="get_form_values()">View</button>
<input type="reset" value="Reset" onclick="enableTextBox()">
</div>
</div>
<script>
var elements = "";
var connections = "";
var emptycells = "";
var headerrow = "";
var datarow = "";
var prefix = "";
var separator = "";
var keepignore = "";
var cellarrays = "";
var separator2 = "";
var columnignore="";
var colprefix="";
var elementsIndex = "";
var elementsValue = "";
var connectionsIndex = "";
var connectionsValue = "";
function populate(){
var elementlist=document.getElementById('elmn');
var connectlist=document.getElementById('conn');
//document.getElementById("elmn").innerHTML = "";
//document.getElementById("conn").innerHTML = "";
function onSuccess(optionarray) {
for(var option in optionarray){
var pair=optionarray[option].split("|")
var newoption=document.createElement("Option");
newoption.value=pair[0];
newoption.innerHTML=pair[1];
elementlist.options.add(newoption);
var newoption=document.createElement("Option");
newoption.value=pair[0];
newoption.innerHTML=pair[1];
connectlist.options.add(newoption);
}
elementlist.selectedIndex=elementsIndex;
connectlist.selectedIndex=connectionsIndex;
}
//console.log(document.getElementById('elmn').options);
google.script.run.withSuccessHandler(onSuccess).getSheetList();
enableTextBox();
}
function get_form_values(){
//...
var e = document.getElementById('elmn');
elementsValue = e.options[e.selectedIndex].value;
elements = e.options[e.selectedIndex].text;
elementsIndex = e.options[e.selectedIndex].index;
//...
//...
var c = document.getElementById('conn');
connectionsValue = c.options[c.selectedIndex].value;
connections = c.options[c.selectedIndex].text;
connectionsIndex = c.options[c.selectedIndex].index;
//...
emptycells=document.getElementById('emptycells').checked;
headerrow=document.getElementById('headerrow').value;
datarow=document.getElementById('datarow').value;
prefix=document.getElementById('prefix').value;
separator=document.getElementById('separator').value;
keepignore=document.getElementById('keepignore').checked;
cellarrays=document.getElementById('cellarrays').checked;
separator2=document.getElementById('separator2').value;
columnignore=document.getElementById('columnignore').checked;
colprefix=document.getElementById('colprefix').value;
var sheets=[];
sheets[0]=elements;
sheets[1]=connections;
var settings=[];
settings[0]=emptycells;
settings[1]=headerrow;
settings[2]=datarow;
settings[3]=prefix;
settings[4]=separator;
settings[5]=keepignore;
settings[6]=cellarrays;
settings[7]=separator2;
settings[8]=columnignore;
settings[9]=colprefix;
setProperties();
google.script.run.showData(sheets,settings);
}
function setProperties(){
return google.script.run.setExportProperties(generateProperties());
}
function generateProperties(){
var properties = {
//General settings
"emptycells" : emptycells,
"headerrow" : headerrow,
"datarow" : datarow,
"prefix" : prefix,
"separator" : separator,
"keepignore" : keepignore,
"cellarrays" : cellarrays,
"columnignore" : columnignore,
"colprefix" : colprefix,
"separator2" : separator2,
"elements" : elements,
"connections" : connections,
"elementsIndex" : elementsIndex,
"elementsValue": elementsValue,
"connectionsIndex" : connectionsIndex,
"connectionsValue": connectionsValue,
};
return JSON.stringify(properties);
}
//Actually sets the local properties based on stored settings.
function updateProperties(properties){
var settings;
if(properties == null || properties === ""){
settings = {};
}
else{
settings = JSON.parse(properties);
}
//General
//connections = settings["connections"] != null ? settings["connections"] : connections;
elements = settings["elements"] != null ? (settings["elements"] === "" ? elements : settings["elements"]) : elements
elementsIndex = settings["elementsIndex"] != null ? settings["elementsIndex"] : elementsIndex;
elementsValue = settings["elementsValue"] != null ? settings["elementsValue"] : elementsValue;
connections = settings["connections"] != null ? (settings["connections"] === "" ? connections : settings["connections"]) : connections
connectionsIndex = settings["connectionsIndex"] != null ? settings["connectionsIndex"] : connectionsIndex;
connectionsValue = settings["connectionsValue"] != null ? settings["connectionsValue"] : connectionsValue;
emptycells = settings["emptycells"] != null ? settings["emptycells"] : emptycells;
headerrow = settings["headerrow"] != null ? settings["headerrow"] : headerrow;
datarow = settings["datarow"] != null ? settings["datarow"] : datarow;
prefix = settings["prefix"] != null ? settings["prefix"] : prefix;
separator = settings["separator"] != null ? settings["separator"] : separator;
keepignore = settings["keepignore"] != null ? settings["keepignore"] : keepignore;
cellarrays = settings["cellarrays"] != null ? settings["cellarrays"] : cellarrays;
separator2 = settings["separator2"] != null ? settings["separator2"] : separator2;
columnignore = settings["columnignore"] != null ? settings["columnignore"] : columnignore;
colprefix = settings["colprefix"] != null ? settings["colprefix"] : colprefix;
refreshSideBar();
//populate(elementsValue, connectionsValue);
}
//Function to rebuild the sidebar after loading export parameters
function refreshSideBar(){
document.getElementById('emptycells').checked=emptycells;
if(headerrow=="")
document.getElementById('headerrow').value="1";
else
document.getElementById('headerrow').value=headerrow;
if(datarow=="")
document.getElementById('datarow').value="2";
else
document.getElementById('datarow').value=datarow;
if(prefix=="")
document.getElementById('prefix').value="ja_";
else
document.getElementById('prefix').value=prefix;
if(separator=="")
document.getElementById('separator').value="|";
else
document.getElementById('separator').value=separator;
document.getElementById('keepignore').checked=keepignore;
document.getElementById('cellarrays').checked=cellarrays;
document.getElementById('columnignore').checked=columnignore;
if(colprefix=="")
document.getElementById('colprefix').value="ig_";
else
document.getElementById('colprefix').value=colprefix;
if(separator2=="")
document.getElementById('separator2').value="|";
else
document.getElementById('separator2').value=separator2;
EnableTextbox1('keepignore', 'prefix' ,'separator')
EnableTextbox('columnignore', 'colprefix')
EnableTextbox('cellarrays', 'separator2')
}
function getProperties(onSuccess){
return google.script.run.withSuccessHandler(onSuccess).getExportProperties();
}
//enable disable
function EnableTextbox(CheckboxId,TextboxId1){
if(document.getElementById(CheckboxId).checked){
document.getElementById(TextboxId1).style.backgroundColor = "#fff"
document.getElementById(TextboxId1).disabled=false;
}
else{
document.getElementById(TextboxId1).style.backgroundColor = "#5bb800"
document.getElementById(TextboxId1).disabled=true;
}
}
function EnableTextbox1(CheckboxId,TextboxId1, TextboxId2){
if(document.getElementById(CheckboxId).checked){
document.getElementById(TextboxId1).style.backgroundColor = "#fff";
document.getElementById(TextboxId2).style.backgroundColor = "#fff";
document.getElementById(TextboxId1).disabled=false;
document.getElementById(TextboxId2).disabled=false;
}
else{
document.getElementById(TextboxId1).style.backgroundColor = "#5bb800";
document.getElementById(TextboxId2).style.backgroundColor = "#5bb800";
document.getElementById(TextboxId1).disabled=true;
document.getElementById(TextboxId2).disabled=true;
}
}
function enableTextBox(){
document.getElementById("elmn").selectedIndex = 0;
document.getElementById("conn").selectedIndex = 0;
document.getElementById("headerrow").value = "1";
document.getElementById("datarow").value = "2";
document.getElementById("emptycells").checked = false;
//========================================================
document.getElementById("keepignore").checked = false;
document.getElementById("prefix").value = "ja_";
document.getElementById("separator").value = "|";
document.getElementById("columnignore").checked = false;
document.getElementById("colprefix").value = "ig_";
document.getElementById("cellarrays").checked = false;
document.getElementById("separator2").value = "|";
document.getElementById("prefix").style.backgroundColor = "#5bb800";
document.getElementById("separator").style.backgroundColor = "#5bb800";
document.getElementById("colprefix").style.backgroundColor = "#5bb800";
document.getElementById("separator2").style.backgroundColor = "#5bb800";
document.getElementById("prefix").disabled=true;
document.getElementById("separator").disabled=true;
document.getElementById("colprefix").disabled=true;
document.getElementById("separator2").disabled=true;
}
//end
getProperties(updateProperties);
</script>
</body>
</html>
I believe your goal is as follows.
You want to remove the following error. (The following image is from your question.)
When I saw your script and your sample Spreadsheet, I noticed that your sheet of "Connections" has only the header row. In this case, an error occurs at var values=sheet.getRange(startRowNum, 1, sheet.getLastRow()-1, sheet.getLastColumn()).getValues(); in your script. Because sheet.getLastRow()-1 is 0. So, in your situation, how about the following modification?
From:
var values=sheet.getRange(startRowNum, 1, sheet.getLastRow()-1, sheet.getLastColumn()).getValues();
To:
var rows = sheet.getLastRow() - 1;
var values = rows > 0 ? sheet.getRange(startRowNum, 1, rows, sheet.getLastColumn()).getValues() : [];
By this modification, when "Connections" sheet has only the header, [] is used as values.

How can I parse html table inside a script respecting merged cells

This header is contained in a js file https://www.portaldefinancas.com/js-tx-ctb/th-cdib.js
document.write(""),document.write('</p></caption><thead><tr><th rowspan="4">Mês de<br>Referência</th><th colspan="7">Taxas - %</th></tr><tr> <th rowspan="3">Mensal</th><th colspan="4">Anualizada</th><th colspan="2">Acumulada</th></tr><tr> <th colspan="2">Ano de<br>252 dias<br> úteis</th><th colspan="2">Ano de<br>365/366 dias<br>corridos</th><th rowspan="2">No ano</th><th rowspan="2">Em <br>12 meses</th></tr><tr><th>Dias</th><th> Taxa</th><th>Dias</th><th> Taxa</th></tr></thead><tbody>');
How can I parse the headers respecting the merged rows and merged columns. The script I use today is
function getHeaders(url) {
var source = UrlFetchApp.fetch(url).getContentText()
source = source.split('document')[2]
var table = '<table><tr><th ' + source.match(/(?<=<th ).*(?=th>)/g) + 'th></tr></table>'
table=table.replace(/ê/g,'ê').replace(/ú/g,'ú').replace(/<br>/g,'\n')
var doc = XmlService.parse(table);
var rows = doc.getDescendants().filter(function(c) {
var element = c.asElement();
return element && element.getName() == "tr";
});
var data = rows.slice(0).map(function(row) {
return row.getChildren("th").map(function(cell) {
return cell.getValue();
});
});
return data;
}
but it doesn't respect merged areas. Thanks for any help !
Since intellectual exercises is my my drug of choice... I can't help it. Here is the possible solution. It works to a degree but it shows little the traits of lofty style of coding:
function main() {
var sheet = SpreadsheetApp.getActiveSheet();
var data = getHeaders();
data = handle_rowspans(data);
sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
}
function getHeaders(url) {
// var source = UrlFetchApp.fetch(url).getContentText()
// source = source.split('document')[2]
var source = `<thead><tr><th rowspan="4">Mês de<br>Referência</th><th colspan="7">Taxas - %</th></tr><tr> <th rowspan="3">Mensal</th><th colspan="4">Anualizada</th><th colspan="2">Acumulada</th></tr><tr> <th colspan="2">Ano de<br>252 dias<br> úteis</th><th colspan="2">Ano de<br>365/366 dias<br>corridos</th><th rowspan="2">No ano</th><th rowspan="2">Em <br>12 meses</th></tr><tr><th>Dias</th><th> Taxa</th><th>Dias</th><th> Taxa</th></tr></thead><tbody>`;
source = handle_colspans(source);
table = '<table><tr><th ' + source.match(/(?<=<th ).*(?=th>)/g) + 'th></tr></table>';
table = table.replace(/ê/g, 'ê').replace(/ú/g, 'ú').replace(/<br>/g, '\n');
var doc = XmlService.parse(table);
var rows = doc.getDescendants().filter(function (c) {
var element = c.asElement();
return element && element.getName() == "tr";
});
var data = rows.slice(0).map(function (row) {
return row.getChildren("th").map(function (cell) {
return cell.getValue();
});
});
return data;
}
function handle_colspans(table) {
return table.split('</tr>').map(r => add_cells_in_row(r)).join('</tr>');
function add_cells_in_row(row) {
var cells = row.split('</th>');
for (var i in cells) {
if (/colspan/.test(cells[i])) {
var colspan = cells[i].replace(/.*colspan="(\d+).*/, '$1');
cells[i] += '{col' + colspan + '}';
cells[i] = [cells[i], ...(new Array(+colspan - 1).fill('<th>'))];
}
if (/rowspan/.test(cells[i])) {
var rowspan = cells[i].replace(/.*rowspan="(\d+).*/, '$1');
cells[i] += '{row' + rowspan + '}';
}
}
return cells.flat().join('</th>')
}
}
function handle_rowspans(array) {
for (var row in array) {
for (var col in array[row]) {
if (/\{row/.test(array[row][col])) {
var rowspan = array[row][col].replace(/.*\{row(\d+).*/s, '$1');
for (var r = 1; r < rowspan; r++) array[+row + r].splice(col, 0, '')
}
}
}
return array;
}
It will get you the table like this:
Whrere {row#} and {col#} means how many cells or rows to the left or to the bottom you need to join to the current cell to recreate the original design. It could be the next dose of the intellectual exercises. :)
A solution, example with url = https://www.portaldefinancas.com/js-tx-ctb/th-cdib.js
function getHeaders(url) {
var source = UrlFetchApp.fetch(url).getContentText()
source = source.split('document')[2]
var table = '<table><tr><th ' + source.match(/(?<=<th ).*(?=th>)/g) + 'th></tr></table>'
table=table.replace(/ê/g,'ê').replace(/ú/g,'ú').replace(/<br>/g,'\n')
var doc = XmlService.parse(table);
var rows = doc.getDescendants().filter(function(c) {
var element = c.asElement();
return element && element.getName() == "tr";
});
var n=0
var data=[]
rows.slice(0).map(function(row) {data[n++]=[]})
n=0
rows.slice(0).map(function(row) {
row.getChildren("th").map(function(cell) {
try{nbcols = cell.getAttribute('colspan').getValue()}catch(e){nbcols = 1}
try{nbrows = cell.getAttribute('rowspan').getValue()}catch(e){nbrows = 1}
var value = cell.getValue()
r=0
var free=0
while(r<nbrows*1){
c=0
while(c<nbcols*1){
while(data[n+r][free]!=null){free++}
data[n+r][free]=(value)
value=''
c++
}
r++
}
});
n++
});
return (data);
}

Google maps not working IE11 (sharepoint 2010)

I have a code to show Google Maps in my list on Sharepoint 2010. Code plot list items in the map. Work great with Edge and Chrome, but on IE not.
In IE console error on this line (object doesn't support this action (error 445))
var latlng = new google.maps.LatLng(defaultLatitude,defaultLongitude);
//build map
$('#'+idOfMapDiv).css({"width":mapWidth, "height":mapHeight});
var latlng = new google.maps.LatLng(defaultLatitude,defaultLongitude);
var myOptions = {
zoom: 6,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var gmMap = new google.maps.Map(document.getElementById(idOfMapDiv), myOptions);
var gmBounds = new google.maps.LatLngBounds();
var gmGeocoder = new google.maps.Geocoder();
var jQuerySelect_GetListRowByAttributeCTXName;
if(isSharePoint2010)
{
jQuerySelect_GetListRowByAttributeCTXName = jQuerySelect_2010_GetListRowByAttributeCTXName;
}
else
{
jQuerySelect_GetListRowByAttributeCTXName = jQuerySelect_2007_GetListRowByAttributeCTXName;
}
Full code on Sharepoint:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js" type="text/javascript"></script><script src="http://maps.google.com/maps/api/js?key=MY KEY" type="text/javascript"></script><script type="text/javascript">
/* *****************************************************
AND NOW SOME VARIABLES YOU MAY WANT TO CHANGE.
***************************************************** */
var mapWidth = "1725px"; //the width of the map
var mapHeight = "400px"; //the height of the map
//if true the coordinates (latitude, longitude) will be used
//if false the addresses (street, zip, city, country) will be used
var useCoordinates = true;
//if true the map will not be shown by default
//if false the map will be shown by default
var hideMapUntilClick = false;
//if 0 absolutely no warning- and error-alerts will be written to a log-div
//if 1 the warning- and error-alerts will be written to a log-div
//if 2 the warning- and error-alerts will be alerted via javascript-alert
var showWarningAndErrorAlerts = 1;
//the internal-names of list-columns (not the display-name!)
var colLinkTitleInternalName = "ows_Nome_x0020_Instala_x00e7__x00e3_"; //will be used as the title of the geo-markers
var colLongitudeInternalName = "ows_Longitude"; //useCoordinates == true
var colLatitudeInternalName = "ows_Latitude"; //useCoordinates == true
var colStreetInternalName = "WorkAddress"; //useCoordinates == false
var colStreetDisplayName = "Endereço"; //useCoordinates == false
var colZipInternalName = "WorkZip"; //useCoordinates == false
var colCityInternalName = "WorkCity"; //useCoordinates == false
var colCountryInternalName = "WorkCountry"; //useCoordinates == false
var defaultCountryValue = "Brazil"; //the default country which will be used if no country-name will be found in the list-column
//default position (Germany: 51.165691,10.451526) > used if no markers will be set to the map
var defaultLatitude = -15.77972;
var defaultLongitude = -47.92972;
//some language-specific messages
var resxGoogleMapsLink = "Google-Map"; //the name of new menu-point in the menu-toolbar of the sharepoint-list
var resxGoogleMapsLinkTitle = "Menu: Show or hide Google-Map"; //the title which will be visible while hovering
var resxAlertsMessageText = resxGoogleMapsLink+": There are # hints!"; //the hint which will be visible if configuration warnings or errors occured.
var resxAlertsMessageTextTitle = "Click here to show or hide the hints!"; //the title which will be visible while hovering
/* *******************************************************************
NOW DO NOT CHANGE ANYTHING IF YOU ARE NOT FAMILIAR WITH JAVASCRIPT!
******************************************************************* */
var isSharePoint2010 = true;
var hasMapBeenLoadedInitially = false;
var idOfMapDiv = "divGoogleMapForSharePointList";
var idOfCustomLogDiv = "divCustomLog";
var idOfCustomLogOverview = "divCustomLogOverview";
var noOfCustomLogEntries = 0;
var noOfMaxGeocodingRequest = 10;
//the attribute-name of the column "id" > will be used for a) finding the id of a certain row and b) for building the ajax-request-url
var colID = "ID";
//now some templates for jquery-selects
var jQuerySelect_2007_GetListRowByAttributeCTXName = "table[CTXName]";
var jQuerySelect_2010_GetListRowByAttributeCTXName = "div[CTXName]";
function InitializeGoogleMapForSharePointList()
{
BuildGoogleMapCustomLogForSharePointList();
if(!DoPreCheckForInitializationOfGooglemapsForSharePointList())
{
//pre-check not successfully done > abort now!
customAlert("The Pre-Check has not been successfully! > Abort now.");
return;
}
DoSchemaCheckAndBuildGoogleMapIconForSharePointList();
}
function DoSchemaCheckAndBuildGoogleMapIconForSharePointList()
{
//get the schema-data for the list
$.get(BuildAjaxRequestUrlForSharePointListSchemaOnly(), {}, function (xml)
{
//find all necessary internal-field-names
var arrNeccessaryFields = new Array();
arrNeccessaryFields.push(colLinkTitleInternalName);
if(useCoordinates)
{
arrNeccessaryFields.push(colLatitudeInternalName);
arrNeccessaryFields.push(colLongitudeInternalName);
}
else
{
arrNeccessaryFields.push(colStreetInternalName);
arrNeccessaryFields.push(colZipInternalName);
arrNeccessaryFields.push(colCityInternalName);
arrNeccessaryFields.push(colCountryInternalName);
}
//check all neccessary internal-field-names
var foundAllNeccessaryFields = true;
for(i=0;i<arrNeccessaryFields.length;i++)
{
//getting <xml><s:Schema><s:ElementType><s:AttributeType name="[internal-field-name]">
var xmlQuery = 'xml > *:first > *:first > *[name='+arrNeccessaryFields[i]+']';
if($(xmlQuery, xml).length<1)
{
foundAllNeccessaryFields = false;
customAlert("Schema-Check failed for internal-field-name '"+arrNeccessaryFields[i]+"'. The field is not available in this list.");
}
}
//check if the neccessary fields have been found
if(foundAllNeccessaryFields)
{
BuildGoogleMapIconForSharePointList();
}
else
{
var ajaxRequestLinkTag = 'ajax-request for list-schema';
customAlert("Hint: You can get the internal names of the columns by calling the "+ajaxRequestLinkTag+" for the current sharepoint-list manually.");
customAlert("Schema-Check failed. Abort now!");
}
});
}
function DoPreCheckForInitializationOfGooglemapsForSharePointList()
{
//check if this is SharePoint2010 or not
//if it is not 2010 it is assumed that it is 2007
if(typeof(_fV4UI)!='undefined')
{
//the checked javascript-variable exists only in 2010
isSharePoint2010 = true;
}
else
{
isSharePoint2010 = false;
}
//for the first shot: support only one table
//for further version we could support more tables (table[class=ms-listviewtable].length>1)
var noOfListViews = $("table[class=ms-listviewtable]").length;
if(noOfListViews==0)
{
//no list-view exists > there is no need to show google-maps
customAlert("There is no list-view available on this site. > No need to show google-maps. > Abort now.");
return false;
}
else if(noOfListViews>1)
{
//there are more than one list-view > this is not supported at the moment
customAlert("There are more than one list-views on the site. This is not supported at the moment. > Abort now!");
return false;
}
//check if multi-lookup exists
if($("table[FieldType=LookupMulti]").length>0)
{
//If there are columns in the list-view which are of type multi-lookup the ajax-call (via owssrv.dll) will return zero results.
var multiMsg = "Multi-lookup exists! Please remove the mulit-lookup-column or use another view (otherwise the ajax-request will receive an empty result). > Abort now!";
multiMsg += "\n\nColumns which are of type multi-lookup are (the display-name will be shown):";
$("table[FieldType=LookupMulti]").each(function(){
var displayName = $(this).attr("displayName");
multiMsg += "\n- "+displayName;
});
customAlert(multiMsg);
return false;
}
//check if javascript-variable exists > we need ctx to get the id of the sharepoint-list
if(ctx==null)
{
//this javascript-variable is essential for getting the list-id and the list-view-id.
customAlert("The javascript-variable 'ctx' does not exist within the html-dom. > Abort now!");
return false;
}
//all checks passed - return true
return true;
}
function BuildGoogleMapCustomLogForSharePointList()
{
if(showWarningAndErrorAlerts!=1)
{
return;
}
var divCustomLogOverview = '<div title="'+resxAlertsMessageTextTitle+'" onclick="ToggleCustomLog();" id="'+idOfCustomLogOverview+'" style="margin: 10px; cursor: pointer; color: red; display: none;"></div>';
$("table.ms-menutoolbar").parent().append(divCustomLogOverview);
var divCustomLog = '<div id="'+idOfCustomLogDiv+'" style="margin: 10px; display: none;"></div>';
$("table.ms-menutoolbar").parent().append(divCustomLog);
}
function ToggleCustomLog()
{
//show or hide
$("#"+idOfCustomLogDiv).toggle();
}
function ToggleGoogleMapDiv()
{
//check if the map will be called for the first time
if(!hasMapBeenLoadedInitially)
{
ShowGoogleMapForSharePointList();
}
//show or hide
$("#"+idOfMapDiv).toggle();
}
function BuildGoogleMapIconForSharePointList()
{
//searching for the correct position in the menu-toolbar (ms-menutoolbar)
$("td.ms-toolbar").each(function(j){
if($(this).attr("width")=="99%")
{
//insert a new menu-item before the found placeholder
//var newMenuItem = '</internal-name-of-column><td class="ms-separator">';
var newMenuItem = '<td class="ms-separator">';
newMenuItem += '<img src="/_layouts/images/blank.gif" alt=""/>';
newMenuItem += '</td>';
newMenuItem += '<td nowrap="true" class="ms-toolbar">';
newMenuItem += '<span title="'+resxGoogleMapsLinkTitle+'">';
newMenuItem += '<div nowrap="nowrap" hoverinactive="ms-menubuttoninactivehover" hoveractive="ms-menubuttonactivehover" onmouseover="MMU_PopMenuIfShowing(this);MMU_EcbTableMouseOverOut(this, true)" class="ms-menubuttoninactivehover">';
newMenuItem += '<a onclick="javascript:ToggleGoogleMapDiv();return false;" href="#" style="cursor: pointer; white-space: nowrap;">'+resxGoogleMapsLink+'</a>';
newMenuItem += '</div>';
newMenuItem += '</span>';
newMenuItem += '</td>';
$(this).before(newMenuItem);
}
});
//adding map-canvas as div-tag to the dom
var divMapCanvas = '<div id="'+idOfMapDiv+'" style="margin: 10px; display: none;"></div>';
$("table.ms-menutoolbar").parent().before(divMapCanvas);
//check if the map should be shown as soon as possible or if it should be hidden until the user clicked the new menu-point
if(!hideMapUntilClick)
{
ToggleGoogleMapDiv();
}
}
//gets the complete list-schema and one row with all its values (not filtered by the current used view)
function BuildAjaxRequestUrlForSharePointListByID_Template()
{
if(ctx!=null)
{
//build the url of the ajax-request
return ctx.HttpRoot+'/_vti_bin/owssvr.dll?XMLDATA=1&List=' + ctx.listName + '&Query=*&FilterField1='+colID+'&FilterValue1=';
}
}
//gets the list-schema and no rows
function BuildAjaxRequestUrlForSharePointListSchemaOnly()
{
//build the url of the ajax-request
return BuildAjaxRequestUrlForSharePointListByID_Template()+'-1';
}
function ShowGoogleMapForSharePointList()
{
//build the url of the ajax-request
var urlTemplate = BuildAjaxRequestUrlForSharePointListByID_Template();
//build map
$('#'+idOfMapDiv).css({"width":mapWidth, "height":mapHeight});
var latlng = new google.maps.LatLng(defaultLatitude,defaultLongitude);
var myOptions = {
zoom: 6,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var gmMap = new google.maps.Map(document.getElementById(idOfMapDiv), myOptions);
var gmBounds = new google.maps.LatLngBounds();
var gmGeocoder = new google.maps.Geocoder();
var jQuerySelect_GetListRowByAttributeCTXName;
if(isSharePoint2010)
{
jQuerySelect_GetListRowByAttributeCTXName = jQuerySelect_2010_GetListRowByAttributeCTXName;
}
else
{
jQuerySelect_GetListRowByAttributeCTXName = jQuerySelect_2007_GetListRowByAttributeCTXName;
}
//check if the number of geocodings will exceed the max-number
if(!useCoordinates && $(jQuerySelect_GetListRowByAttributeCTXName).length > noOfMaxGeocodingRequest)
{
var linkToStatusCodes = '<a title="Statuscodes of geocoding-responses from Google-Maps" target="_blank" href="http://code.google.com/intl/de-DE/apis/maps/documentation/javascript/services.html#GeocodingStatusCodes">OVER_QUERY_LIMIT-Status</a>';
var tooManyMsg = "Hint: In the current view of the SharePoint-List there are more than "+noOfMaxGeocodingRequest+" list-entries. ";
tooManyMsg += "This will result in an "+linkToStatusCodes+" by Google-Maps (and not all markers will be shown on the map). > You have 2 options: ";
tooManyMsg += "a) Change your view to get no more than "+noOfMaxGeocodingRequest+" list-entries or ";
tooManyMsg += "b) use the coordinates (longitude, latitude) of the addresses (they will be shown on the map).";
customAlert(tooManyMsg);
}
//get each row from list-view which is shown at the moment
$(jQuerySelect_GetListRowByAttributeCTXName).each(function(j)
{
var lat, lng, gmLatLng, gmMarker, title, customUrl, street, city, country;
var idOfListItem = $(this).attr(colID);
if(isSharePoint2010)
{
var linkToListItem = '<table height="auto" width="calcWidthpx"><tr class="ms-alternating ms-itmhover"><td height="100%" class="ms-vb-title" onmouseover="OnChildItem(this)">';
linkToListItem += $(this).parent().html();
linkToListItem += '</td></tr></table><span style="font-size:72pt;"></br></span>';
}
else
{
linkToListItem = $(this).parent().html();
linkToListItem = linkToListItem.replace(/100%/g, "auto"); //exchange tag-attributes for width and height
}
//build url for the ajax-request which reads all data for a certain row (for the current list-view)
customUrl = urlTemplate+idOfListItem;
//get the data for the row
$.get(customUrl, {}, function (xml)
{
$('xml > *:last > *', xml).each(function (i)
{
//get some data from the xml-response
title = $(this).attr(colLinkTitleInternalName);
if(isSharePoint2010)
{
var titleLength= title.length+185;
linkToListItem = linkToListItem.replace('calcWidth', titleLength);
}
if(useCoordinates)
{
//getting coordinates
lat = $(this).attr(colLatitudeInternalName);
lng = $(this).attr(colLongitudeInternalName);
if(typeof(lat)!='undefined' && typeof(lng)!='undefined')
{
gmLatLng = new google.maps.LatLng(lat, lng);
msgForInfoWindow = linkToListItem; //you may add more information-text here
SetMarkerForGoogleMapForSharePointList(gmLatLng, gmMap, gmBounds, title, msgForInfoWindow);
}
else
{
customAlert(title +" has undefined lat+lng. > Do not add marker on map.");
}
}
else
{
//getting address
street = $(this).attr(colStreetInternalName);
zip = $(this).attr(colZipInternalName);
city = $(this).attr(colCityInternalName);
country = $(this).attr(colCountryInternalName);
//checking received values
if(typeof(street)=='undefined') street = ""; //optional
if(typeof(zip)=='undefined') zip = ""; //optional
if(typeof(city)=='undefined')
{
customAlert("The ajax-response got no city for '"+title+"'. > Do not add marker on map.");
return;
}
if(typeof(country)=='undefined') country = defaultCountryValue;
address = street+","+zip+","+city+","+country;
//getting coordinates
gmGeocoder.geocode( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK)
{
if(results.length==0)
{
customAlert("Geocoding: There are no results for address '"+results[0].formatted_address+"'! Expected exactly one result. > Do not show any marker on map for this address.");
}
else if(results.length>1)
{
var msg = "Geocoding: There are too many ("+results.length+") results for given address! Expected exactly one result. > Do not show any marker on map for this address.\n\nFound addresses:\n";
for(i=0;i<results.length;i++)
{
var c = i+1;
msg += "\n"+c+": "+results[i].formatted_address;
}
customAlert(msg);
}
else
{
gmLatLng = results[0].geometry.location;
var msgForInfoWindow = linkToListItem+"<br>";
msgForInfoWindow += "<span style='font-size:0.8em;'>Koordinaten (Lat, Lon): "+gmLatLng+"<br>Adresse: "+results[0].formatted_address+"</span>";
SetMarkerForGoogleMapForSharePointList(gmLatLng, gmMap, gmBounds, title, msgForInfoWindow);
}
}
else
{
customAlert("Geocode for address '"+address+"' was not successful for the following reason: " + status);
}
});
}
});
});
});
hasMapBeenLoadedInitially = true;
}
function SetMarkerForGoogleMapForSharePointList(gmLatLng, gmMap, gmBounds, title, contentForInfoWindow)
{
var gmMarker = new google.maps.Marker({
position: gmLatLng,
map: gmMap,
title: title,
zIndex: 0
});
gmBounds.extend(gmLatLng);
gmMap.setCenter(gmBounds.getCenter());
gmMap.fitBounds(gmBounds);
if(contentForInfoWindow!=null && contentForInfoWindow!="")
{
var gmInfowindow = new google.maps.InfoWindow({
content: contentForInfoWindow
});
google.maps.event.addListener(gmMarker, 'click', function() {
gmInfowindow.open(gmMap,gmMarker);
});
}
}
function customAlert(msg)
{
if(msg==null || msg=="")
{
return;
}
else
{
var now = new Date();
msg = now.getHours()+":"+now.getMinutes()+":"+now.getSeconds()+": "+msg;
}
if(showWarningAndErrorAlerts==0)
{
//do nothing
}
else if(showWarningAndErrorAlerts==1)
{
//do log in log-div
msg = msg.replace(/\n/g, "<br/>");
msg += "<br/><br/>";
$("#"+idOfCustomLogDiv).append(msg);
noOfCustomLogEntries++;
$("#"+idOfCustomLogOverview).show();
var overviewText = resxAlertsMessageText.replace(/#/g, noOfCustomLogEntries);
$("#"+idOfCustomLogOverview).text(overviewText);
}
else if(showWarningAndErrorAlerts==2)
{
//do alert via javascript-alert
alert(msg);
}
else
{
//unsupported
}
}
//call initialization after the dom has been loaded completely > so it does not matter where this piece of javascript will be inserted in the dom
$(document).ready(InitializeGoogleMapForSharePointList);</script>
Result in Chrome e Edge:
Screenshot
Just to put an answer to this question, adding the meta tag of <meta http-equiv="X-UA-Compatible" content="IE=8"> would solve the issue.

Add search filter inside the select dropdown in AngularJS

I want to add a search filter inside a select dropdown in angularJS.
I have used ng-options to list down the options and used filter to filter out the data in the search box , but the problem is that the search box is not coming inside(or under) select dropdown. (When I click the select dropdown, it shows a search filter and below it has all the options)
Below is the code for your reference :
<div class="rowMargin">
<label class="control-label" for="entitySel">Entity:</label>
<div class="controls">
<select id="entityId" class="input-medium" type="text" name="entityId" ng-model="payment.entityId" ng-options="entityOpt for entityOpt in paymentEntityOptions">
<option value="">Select</option>
</select>
<span ng-show=" submitted && addPayment.entityId.$error.required">
<label class="error">Please provide entity Id </label>
</span>
<div ng-show="payment.entityId == \'Individual\'">
<span>
<select ng-model="payment.entity.individual" ng-options = "individual for individual in individualEntities | filter : filterEntity">
<option value="">Select Individual Entity</option>
<option>
<input type="search" placeholder="Search" ng-model="filterEntity"></input>
</option>
</select>
</span>
</div>
<div ng-show="payment.entityId == \'Group\'">
<span>
<select ng-model="payment.entity.group" ng-options = "group for group in groupEntities | filter : filterEntity">
<option value="">Select Group Entity</option>
<input type="search" placeholder="Search" ng-model="filterEntity"></input>
</select>
</span>
</div>
</div>
I have used the bootstrap button with class 'dropdown-toggle' and on click of the button I have appended an input search box as following :
<div class="dropdown pull-right makePaymentDropdownMainDiv" auto-close="outsideClick">
<button class="btn btn-default dropdown-toggle makePaymentDropdownBtn" type="button" id="individualDrop" data-toggle="dropdown">{{payment.entity}}<span class="caret pull-right"></span></button>
<span ng-show="submitted"><label class="error">Select an Individual</label></span>
<ul class="dropdown-menu makePaymentDropdownUlStyle" role="menu" aria-labelledby="individualDrop">
<input disable-auto-close type="search" ng-model="serchFilter" class="makePaymentDropdownSearchBox" placeholder="Search"></input>
<li role="presentation" ng-repeat="indi in individuals | filter: serchFilter"><a role="menuitem" ng-click="selectEntity(indi)">{{indi}}</a></li>
</ul>
</div>
Showing the 'li' using ng-repeat.
Remember to add auto-close="outsideClick" to your dropdown so that it doesn't close on filtering attempt.
Sorry, I'm rather late to the party, but to me it sounds like you need acute-select, an open source extension (MIT license) to Angular that does exactly this, without further dependencies.
They also have a demo page, which shows what it can do nicely.
you can use easy and best way to search filter inside the select dropdown in AngularJS
Working Demo : http://plnkr.co/edit/o767Mg6fQoyc7jKq77If?p=preview
(function (angular, undefined) {
'use strict';
// TODO: Move to polyfill?
if (!String.prototype.trim) {
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g, '');
};
}
/**
* A replacement utility for internationalization very similar to sprintf.
*
* #param replace {mixed} The tokens to replace depends on type
* string: all instances of $0 will be replaced
* array: each instance of $0, $1, $2 etc. will be placed with each array item in corresponding order
* object: all attributes will be iterated through, with :key being replaced with its corresponding value
* #return string
*
* #example: 'Hello :name, how are you :day'.format({ name:'John', day:'Today' })
* #example: 'Records $0 to $1 out of $2 total'.format(['10', '20', '3000'])
* #example: '$0 agrees to all mentions $0 makes in the event that $0 hits a tree while $0 is driving drunk'.format('Bob')
*/
function format(value, replace) {
if (!value) {
return value;
}
var target = value.toString();
if (replace === undefined) {
return target;
}
if (!angular.isArray(replace) && !angular.isObject(replace)) {
return target.split('$0').join(replace);
}
var token = angular.isArray(replace) && '$' || ':';
angular.forEach(replace, function (value, key) {
target = target.split(token + key).join(value);
});
return target;
}
var module = angular.module('AxelSoft', []);
module.value('customSelectDefaults', {
displayText: 'Select...',
emptyListText: 'There are no items to display',
emptySearchResultText: 'No results match "$0"',
addText: 'Add',
searchDelay: 300
});
module.directive('customSelect', ['$parse', '$compile', '$timeout', '$q', 'customSelectDefaults', function ($parse, $compile, $timeout, $q, baseOptions) {
var CS_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elem, attrs, controller) {
var customSelect = attrs.customSelect;
if (!customSelect) {
throw new Error('Expected custom-select attribute value.');
}
var match = customSelect.match(CS_OPTIONS_REGEXP);
if (!match) {
throw new Error("Expected expression in form of " +
"'_select_ (as _label_)? for _value_ in _collection_[ track by _id_]'" +
" but got '" + customSelect + "'.");
}
elem.addClass('dropdown custom-select');
// Ng-Options break down
var displayFn = $parse(match[2] || match[1]),
valueName = match[3],
valueFn = $parse(match[2] ? match[1] : valueName),
values = match[4],
valuesFn = $parse(values),
track = match[5],
trackByExpr = track ? " track by " + track : "",
dependsOn = attrs.csDependsOn;
var options = getOptions(),
timeoutHandle,
lastSearch = '',
focusedIndex = -1,
matchMap = {};
var itemTemplate = elem.html().trim() || '{{' + (match[2] || match[1]) + '}}',
dropdownTemplate =
'<a class="dropdown-toggle" data-toggle="dropdown" href ng-class="{ disabled: disabled }">' +
'<span>{{displayText}}</span>' +
'<b></b>' +
'</a>' +
'<div class="dropdown-menu">' +
'<div stop-propagation="click" class="custom-select-search">' +
'<input class="' + attrs.selectClass + '" type="text" autocomplete="off" ng-model="searchTerm" />' +
'</div>' +
'<ul role="menu">' +
'<li role="presentation" ng-repeat="' + valueName + ' in matches' + trackByExpr + '">' +
'<a role="menuitem" tabindex="-1" href ng-click="select(' + valueName + ')">' +
itemTemplate +
'</a>' +
'</li>' +
'<li ng-hide="matches.length" class="empty-result" stop-propagation="click">' +
'<em class="muted">' +
'<span ng-hide="searchTerm">{{emptyListText}}</span>' +
'<span class="word-break" ng-show="searchTerm">{{ format(emptySearchResultText, searchTerm) }}</span>' +
'</em>' +
'</li>' +
'</ul>' +
'<div class="custom-select-action">' +
(typeof options.onAdd === "function" ?
'<button type="button" class="btn btn-primary btn-block add-button" ng-click="add()">{{addText}}</button>' : '') +
'</div>' +
'</div>';
// Clear element contents
elem.empty();
// Create dropdown element
var dropdownElement = angular.element(dropdownTemplate),
anchorElement = dropdownElement.eq(0).dropdown(),
inputElement = dropdownElement.eq(1).find(':text'),
ulElement = dropdownElement.eq(1).find('ul');
// Create child scope for input and dropdown
var childScope = scope.$new(true);
configChildScope();
// Click event handler to set initial values and focus when the dropdown is shown
anchorElement.on('click', function (event) {
if (childScope.disabled) {
return;
}
childScope.$apply(function () {
lastSearch = '';
childScope.searchTerm = '';
});
focusedIndex = -1;
inputElement.focus();
// If filter is not async, perform search in case model changed
if (!options.async) {
getMatches('');
}
});
if (dependsOn) {
scope.$watch(dependsOn, function (newVal, oldVal) {
if (newVal !== oldVal) {
childScope.matches = [];
childScope.select(undefined);
}
});
}
// Event handler for key press (when the user types a character while focus is on the anchor element)
anchorElement.on('keypress', function (event) {
if (!(event.altKey || event.ctrlKey)) {
anchorElement.click();
}
});
// Event handler for Esc, Enter, Tab and Down keys on input search
inputElement.on('keydown', function (event) {
if (!/(13|27|40|^9$)/.test(event.keyCode)) return;
event.preventDefault();
event.stopPropagation();
switch (event.keyCode) {
case 27: // Esc
anchorElement.dropdown('toggle');
break;
case 13: // Enter
selectFromInput();
break;
case 40: // Down
focusFirst();
break;
case 9:// Tab
anchorElement.dropdown('toggle');
break;
}
});
// Event handler for Up and Down keys on dropdown menu
ulElement.on('keydown', function (event) {
if (!/(38|40)/.test(event.keyCode)) return;
event.preventDefault();
event.stopPropagation();
var items = ulElement.find('li > a');
if (!items.length) return;
if (event.keyCode == 38) focusedIndex--; // up
if (event.keyCode == 40 && focusedIndex < items.length - 1) focusedIndex++; // down
//if (!~focusedIndex) focusedIndex = 0;
if (focusedIndex >= 0) {
items.eq(focusedIndex)
.focus();
} else {
focusedIndex = -1;
inputElement.focus();
}
});
resetMatches();
// Compile template against child scope
$compile(dropdownElement)(childScope);
elem.append(dropdownElement);
// When model changes outside of the control, update the display text
controller.$render = function () {
setDisplayText();
};
// Watch for changes in the default display text
childScope.$watch(getDisplayText, setDisplayText);
childScope.$watch(function () { return elem.attr('disabled'); }, function (value) {
childScope.disabled = value;
});
childScope.$watch('searchTerm', function (newValue) {
if (timeoutHandle) {
$timeout.cancel(timeoutHandle);
}
var term = (newValue || '').trim();
timeoutHandle = $timeout(function () {
getMatches(term);
},
// If empty string, do not delay
(term && options.searchDelay) || 0);
});
// Support for autofocus
if ('autofocus' in attrs) {
anchorElement.focus();
}
var needsDisplayText;
function setDisplayText() {
var locals = { };
locals[valueName] = controller.$modelValue;
var text = displayFn(scope, locals);
if (text === undefined) {
var map = matchMap[hashKey(controller.$modelValue)];
if (map) {
text = map.label;
}
}
needsDisplayText = !text;
childScope.displayText = text || options.displayText;
}
function getOptions() {
return angular.extend({}, baseOptions, scope.$eval(attrs.customSelectOptions));
}
function getDisplayText() {
options = getOptions();
return options.displayText;
}
function focusFirst() {
var opts = ulElement.find('li > a');
if (opts.length > 0) {
focusedIndex = 0;
opts.eq(0).focus();
}
}
// Selects the first element on the list when the user presses Enter inside the search input
function selectFromInput() {
var opts = ulElement.find('li > a');
if (opts.length > 0) {
var ngRepeatItem = opts.eq(0).scope();
var item = ngRepeatItem[valueName];
childScope.$apply(function () {
childScope.select(item);
});
anchorElement.dropdown('toggle');
}
}
function getMatches(searchTerm) {
var locals = { $searchTerm: searchTerm }
$q.when(valuesFn(scope, locals)).then(function (matches) {
if (!matches) return;
if (searchTerm === inputElement.val().trim()/* && hasFocus*/) {
matchMap = {};
childScope.matches.length = 0;
for (var i = 0; i < matches.length; i++) {
locals[valueName] = matches[i];
var value = valueFn(scope, locals),
label = displayFn(scope, locals);
matchMap[hashKey(value)] = {
value: value,
label: label/*,
model: matches[i]*/
};
childScope.matches.push(matches[i]);
}
//childScope.matches = matches;
}
if (needsDisplayText) setDisplayText();
}, function() {
resetMatches();
});
}
function resetMatches() {
childScope.matches = [];
focusedIndex = -1;
};
function configChildScope() {
childScope.addText = options.addText;
childScope.emptySearchResultText = options.emptySearchResultText;
childScope.emptyListText = options.emptyListText;
childScope.select = function (item) {
var locals = {};
locals[valueName] = item;
var value = valueFn(childScope, locals);
//setDisplayText(displayFn(scope, locals));
childScope.displayText = displayFn(childScope, locals) || options.displayText;
controller.$setViewValue(value);
anchorElement.focus();
typeof options.onSelect === "function" && options.onSelect(item);
};
childScope.add = function () {
$q.when(options.onAdd(), function (item) {
if (!item) return;
var locals = {};
locals[valueName] = item;
var value = valueFn(scope, locals),
label = displayFn(scope, locals);
matchMap[hashKey(value)] = {
value: value,
label: label/*,
model: matches[i]*/
};
childScope.matches.push(item);
childScope.select(item);
});
};
childScope.format = format;
setDisplayText();
}
var current = 0;
function hashKey(obj) {
if (obj === undefined) return 'undefined';
var objType = typeof obj,
key;
if (objType == 'object' && obj !== null) {
if (typeof (key = obj.$$hashKey) == 'function') {
// must invoke on object to keep the right this
key = obj.$$hashKey();
} else if (key === undefined) {
key = obj.$$hashKey = 'cs-' + (current++);
}
} else {
key = obj;
}
return objType + ':' + key;
}
}
};
}]);
module.directive('stopPropagation', function () {
return {
restrict: 'A',
link: function (scope, elem, attrs, ctrl) {
var events = attrs['stopPropagation'];
elem.bind(events, function (event) {
event.stopPropagation();
});
}
};
});
})(angular);
<body ng-app="Demo">
<div class="container" ng-controller="DemoController">
<label>Level 1</label>
<div custom-select="g for g in nestedItemsLevel1 | filter: $searchTerm" custom-select-options="level1Options" ng-model="level1"></div>
<label>Level 2</label>
<div custom-select="g for g in nestedItemsLevel2 | filter: $searchTerm" ng-model="level2" cs-depends-on="level1"></div>
</div>
<!-- basic scripts -->
<!--[if !IE]> -->
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<!-- <![endif]-->
<!--[if IE]>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<![endif]-->
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="js/customSelect.js"></script>
<script>
(function () {
var app = angular.module('Demo', ['AxelSoft']);
app.controller('DemoController', ['$scope', '$timeout', '$q', function ($scope, $timeout, $q) {
$scope.searchAsync = function (term) {
// No search term: return initial items
if (!term) {
return ['Item 1', 'Item 2', 'Item 3'];
}
var deferred = $q.defer();
$timeout(function () {
var result = [];
for (var i = 1; i <= 3; i++)
{
result.push(term + ' ' + i);
}
deferred.resolve(result);
}, 300);
return deferred.promise;
};
$scope.nestedItemsLevel1 = ['Item 1', 'Item 2', 'Item 3'];
$scope.level1 = $scope.nestedItemsLevel1[0];
$scope.level1Options = {
onSelect: function (item) {
var items = [];
for (var i = 1; i <= 5; i++) {
items.push(item + ': ' + 'Nested ' + i);
}
$scope.nestedItemsLevel2 = items;
}
};
$scope.nestedItemsLevel2 = [];
$scope.level1Options.onSelect($scope.nestedItemsLevel1[0]);
}]);
})();
</script>
</body>
https://docs.angularjs.org/api/ng/directive/select
There can be only one hard coded in a ngOption.

getelementbyid issue with radio button

I'm trying to make an alert with the value of the selected radio button, but I allways get the first of them, regardless the one I choose...(Acompanhado);
html:
<form/>
<input type="radio" class="simple_form" name="grupo_1" value="Acompanhado" id="saida"/>
<span class="texto">Acompanhado</span>
<input type="radio" class="simple_form" name="grupo_1" value="Individual" id="saida"/>
<span class="texto">Individual</span>
</form>
js:
function save() {
var saida_js = document.getElementById('saida').value;
alert("Tipo de saida: " + saida_js);
}
Any idea ?
#Quentin: I have alot of alerts, cause Im trying to get all data from a form. I used your code, and I get no alert at all.
function save() {
var morada_js = document.getElementById('morada').value;
var data_js = document.getElementById('data').value;
var hora_js = document.getElementById('hora').value;
var radio_saida = document.getElementsByName('name_saida');
var notas_js = document.getElementById('notas').value;
var condicoes_atm_js = document.getElementById('condicoes_atm').value;
alert("Morada: " + morada_js);
alert("Data: " + data_js);
alert("Hora: " + hora_js);
function get_checked_radio(radio_saida) {
for (var i = 0; i < radio_saida.length; i++) {
var current = radio_saida[i];
if (current.checked) {
return current;
}
}
}
alert(get_checked_radio(radio_saida).value);
alert("Notas: " + notas_js);
}
An id must be unique in a document.
To find the value of a selected radio button in a group, get the group by its name…
var radios = document.getElementsByName('radio_name'); // or
var radios = document.forms.formId.elements.radio_name;
Then loop over them until you find the one with the true checked property.
function get_checked_radio(radios) {
for (var i = 0; i < radios.length; i++) {
var current = radios[i];
if (current.checked) {
return current;
}
}
}
alert(get_checked_radio(radios).value);
Makes sense because you've got two input tags with the same id saida