0

I tried applying this solution to my case:
Emailing SPARKLINE charts sends blank cells instead of data

But when I try to apply it to my situation an error pops up with:

TypeError: Cannot read property '0' of null

On the executions there is more information about this error:

enter image description here

My GAS code for my Email solution is able to send just the values, and it's here:

function alertDailyInfo() {

    let emailAddress = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SANDBOX").getRange("F1").getValue();
    
    let treeIconUrl = "https://d1nhio0ox7pgb.cloudfront.net/_img/g_collection_png/standard/256x256/tree.png";
    let treeIconBlob = UrlFetchApp
                       .fetch(treeIconUrl)
                       .getBlob()
                       .setName("treeIconBlob");
    
    let treeUpdate = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SANDBOX").getRange("F6").getValue();
    let waterUpdate = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SANDBOX").getRange("F11").getValue();

            if (treeUpdate > 0) {
            
                    MailApp.sendEmail({
                        to: emailAddress,
                        subject: "TREE WATER UPDATE",
                        htmlBody: "<img src='cid:treeIcon'><br>" + '<br>' + '<br>' +  
                        '<b><u>Tree average is:</u></b>'+ '<br>' + treeUpdate + '<br>' + '<br>' +
                        '<b><u>Water average is:</u></b>'+ '<br>' + waterUpdate + '<br>' + '<br>' 
                        ,
                        inlineImages:
                        {
                            treeIcon: treeIconBlob,
                        }
                    });                
                }
            }

The code from the solution presented on the link above and which I have tried to adapt to my situation (please check my file below) is here:

drawTable();

function drawTable() {

 let emailAddress1 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SANDBOX").getRange("F1").getValue();

    var ss_data = getData();
    var data = ss_data[0];
    var background = ss_data[1];
    var fontColor = ss_data[2];
    var fontStyles = ss_data[3];
    var fontWeight = ss_data[4];
    var fontSize = ss_data[5];
    var html = "<table border='1'>";
    var images = {}; // Added
    for (var i = 0; i < data.length; i++) {
        html += "<tr>"
        for (var j = 0; j < data[i].length; j++) {
            if (typeof data[i][j] == "object") { // Added
                html += "<td style='height:20px;background:" + background[i][j] + ";color:" + fontColor[i][j] + ";font-style:" + fontStyles[i][j] + ";font-weight:" + fontWeight[i][j] + ";font-size:" + (fontSize[i][j] + 6) + "px;'><img src='cid:img" + i + "'></td>"; // Added
                images["img" + i] = data[i][j]; // Added
            } else {
                html += "<td style='height:20px;background:" + background[i][j] + ";color:" + fontColor[i][j] + ";font-style:" + fontStyles[i][j] + ";font-weight:" + fontWeight[i][j] + ";font-size:" + (fontSize[i][j] + 6) + "px;'>" + data[i][j] + "</td>";
            }
        }
        html += "</tr>";
    }
    html + "</table>"
    MailApp.sendEmail({
        to: emailAddress1,
        subject: "Spreadsheet Data",
        htmlBody: html,
        inlineImages: images // Added
    })
}

function getData(){
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("SANDBOX");
  var ss = sheet.getDataRange();
  var val = ss.getDisplayValues();
  var background = ss.getBackgrounds();
  var fontColor = ss.getFontColors();
  var fontStyles = ss.getFontStyles();
  var fontWeight = ss.getFontWeights();
  var fontSize = ss.getFontSizes();
  var formulas = ss.getFormulas(); // Added
  val = val.map(function(e, i){return e.map(function(f, j){return f ? f : getSPARKLINE(sheet, formulas[i][j])})}); // Added
  return [val,background,fontColor,fontStyles,fontWeight,fontSize]; 
}

// Added
function getSPARKLINE(sheet, formula) {
  formula = formula.toUpperCase();
  if (~formula.indexOf("SPARKLINE")) {
    var chart = sheet.newChart()
      .setChartType(Charts.ChartType.SPARKLINE)
      .addRange(sheet.getRange(formula.match(/\w+:\w+/)[0]))
      .setTransposeRowsAndColumns(true)
      .setOption("showAxisLines", false)
      .setOption("showValueLabels", false)
      .setOption("width", 200)
      .setOption("height", 100)
      .setPosition(1, 1, 0, 0)
      .build();
    sheet.insertChart(chart); 
    var createdChart = sheet.getCharts()[0];
    var blob = createdChart.getAs('image/png');
    sheet.removeChart(createdChart);
    return blob;
  }
}

The code that is working just for the values, which I pasted above (1st block of code), will send me an email like this:

enter image description here

But I need to receive the email like this, with the Sparklines below the values like so:

enter image description here

The code for the Email solution, just for the values, I pasted above (1st block of code) is working. But for some reason when the code from the solution linked above (2nd block of code) is imported/saved into my Google Sheets file GAS script library and adapted to my case, everything stops working, displaying the errors mentioned above.

So basically, as you might have already understood, I need to send emails with the values from Tree Average and Water Average, and I managed to get that working. But I also need for the Sparkline graphs that you can see below, and by checking my file linked below too, to also be sent as images/blobs, just below the info, like in the screenshot above.

enter image description here

Can anyone provide any pointers on what can be missing in applying the solution above or is there a better alternative to sending a SPARKLINE graph as image/blob by email?

Here is my file:
https://docs.google.com/spreadsheets/d/1ExXtmQ8nyuV1o_UtabVJ-TifIbORItFMWjtN6ZlruWc/edit?usp=sharing

EDIT_1:
I made some edits to bring more clarity.

EDIT_2:
As requested this is the formula applied to the first Sparkline, the 2nd one is pretty much the same:

=ARRAYFORMULA( SPARKLINE(QUERY({IFERROR(DATEVALUE(SANDBOX!$A$2:$A)), SANDBOX!$B$2:$B}, 
 "select Col2 
  where Col2 is not null 
  and Col1 <= "&INT(MAX(SANDBOX!$A$2:$A))&"
  and Col1 >  "&INT(MAX(SANDBOX!$A$2:$A))-(
IFERROR(
          VLOOKUP(
           SUBSTITUTE($F$4," ",""),
            {"24HOURS",0;
            "2DAYS",1;
            "3DAYS",4; 
            "7DAYS",8; 
            "2WEEKS",16;  
            "1MONTH",30;
            "3MONTHS",90; 
            "6MONTHS",180; 
            "1YEAR",365;
            "2YEARS",730; 
            "3YEARS",1095},
           2,FALSE))
)-1, 0),
 {"charttype","column";"color","#00bb21";"empty","ignore";"nan","ignore"}))

EDIT_3:
At the advice of Rubén I have removed drawTable(); at the beggining of the code block.

I have also transfered the formula for the Sparkline to another helper sheet and link it to the main sheet. After trying it seems the error does not appear anymore. Although the email received has 2 problems:

  • I receive the whole sheet in table form, where I just wanted the Sparklines.
  • Also the Sparklines do not come as images, they do not show up at all. Also where they should appear it says undefined.

I guess the whole sheet is being set because the function getting the range getDataRange(); is getting the whole sheet range.

Here is a screenshot:

enter image description here

Verminous
  • 490
  • 3
  • 14
  • 1
    While links to other questions and to external resources might be helpful, questions should be self-contained. Considering this, please add a [mcve] – Rubén Jun 05 '22 at 15:58
  • I have provided my code, the error message, a link to a possible solution, screenshots to explain better what I need, I have also linked my file. Is there anything causing confusion to you that I might clarify? Please be more specific. Thank you. – Verminous Jun 05 '22 at 16:10
  • I run it by pressing a button with the function assigned to it on my SANDBOX file. But in my main file it will be executed by a time trigger. It gets the same error regardless of how I execute the function. I made a few edits to my question I hope it is more clear now. – Verminous Jun 05 '22 at 16:38
  • It does not because I am trying to use the code from another solution which I linked but did not paste into this question. But I will now as it seems it can bring more clarity. I will add it to the question. – Verminous Jun 05 '22 at 16:47
  • 1
    Your first suggestions worked. The error stops. If I do not use the helper sheet it appears again. I have added the formula for Sparklines. I have added a screenshot of the emails that now are being sent. – Verminous Jun 05 '22 at 17:40
  • 1
    Thanks for let me know. 1. tag spreadsheet should only be used when there is no a specific tag for spreadsheet app being used (in other words, do not use it together with [tag:excel], [tag:google-sheets]) 2. The Q/A model of this site is not good for long back-and-forth / chameleon questions, so I suggest you to post a new question including a good [mcve]. You might focus the new question on how to generalize the Tanaike's solution for email sparklines charts using an array instead of range reference. – Rubén Jun 05 '22 at 17:49
  • Can you give me an example of a good question that checks all the boxes explained in that "minimal reproducible example"? Because after reading that I think my questions is ok. But I don't know maybe I'm wrong. An example would help. Sometimes I have been criticized for something like this and I have been making an effort to provide a well explained, formated, simple to understand question with examples and I'm still getting this cirticism which is frustrating. Would help to see an example in order to finally understand what is the best way to write a question. – Verminous Jun 05 '22 at 18:09
  • I'm sorry, I can't as don't keep track of such questions but here are some questions asking for guidance on creating a mcve that might help you: https://stackoverflow.com/q/48343080/1595451, https://stackoverflow.com/q/5963269/1595451, https://stackoverflow.com/q/20109391/1595451 (taken from https://meta.stackoverflow.com/q/363046/1595451) – Rubén Jun 05 '22 at 18:45

2 Answers2

1

As the question you reference explains:

the chart created by SPARKLINE cannot be directly imported to the email.

Why isn't the script working? Because you have not made any significant modifications to it and because you are using a more complex formula than the one proposed in the other question, it is very difficult (if not impossible) to make it work without any modifications.

What are the options? In my opinion you have 3 different options.

  1. Follow the logic of the solution proposed by Tanaike in the other question and using EmbeddedChartBuilder try to shred the content of the FORMULA to achieve the same as with SPARKLINE.

  2. Use the SpreadsheetApp methods to directly get the values from the sheet and build the chart from there.Here is a small example of how you can do it using Chart Service (You could achieve exactly the same with EmbeddedChartBuilder). As you already have a Blob object, you can insert it inside an email as I do inside the Sheet.

function constCreateChart() {
  const sS = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('HELPER')
  const chart = Charts.newDataTable()
    .addColumn(Charts.ColumnType.NUMBER, '')
    .addColumn(Charts.ColumnType.NUMBER, '')

  // Modfify with your data
  // getRange('A2:A15').getValues()...
  const builder = [...Array(100).keys()].forEach(n => {
    chart.addRow([n, n * n * Math.random()])
  })
  chart.build()

  const chartShap = Charts.newColumnChart()
    .setDataTable(chart)
    .setLegendPosition(Charts.Position.NONE)
    .setOption('hAxis.ticks', [])
    .setOption('vAxis.ticks', [])
    .build()

  sS.insertImage(chartShap.getAs('image/png'), 5, 5)

}
Result

enter image description here

  1. Use this form to request Google to add the possibility to convert charts obtained using SPARKLINES to Blob objects that can be used inside an email.
Documentation
Emel
  • 2,283
  • 1
  • 7
  • 18
  • 1
    Thank you very much. This helps me understand better my situation. And you are right I did almost zero modifications. I trying to understand the basics of coding/javascript/GAS and I'm still making lots of mistakes. I will have a look at this later. Thanks again! – Verminous Jun 06 '22 at 11:38
0
  1. Remove drawTable(); as this line makes that the drawTable function be executed when any function be called.
  2. Apparently the error occurs on .addRange(sheet.getRange(formula.match(/\w+:\w+/)[0])), more specifically because formula.match(/\w+:\w+/) (this expression is intended to extract a range reference of the form A1:B10) returns null. Unfortunately the question doesn't include the formula. One possible solution might be as simple as replacing sheet.getRange(formula.match(/\w+:\w+/)[0]) by another way to set the source range for the temporary chart, but might be a more complex, i.e. adding a helper sheet to be used as the data source for the temporary chart.

NOTE: On Rev 11 one in-cell sparklines chart formula was added. As the formula is pretty complex, the simplest solution is to add a helper sheet to add the QUERY function

QUERY({IFERROR(DATEVALUE(SANDBOX!$A$2:$A)), SANDBOX!$B$2:$B}, 
 "select Col2 
  where Col2 is not null 
  and Col1 <= "&INT(MAX(SANDBOX!$A$2:$A))&"
  and Col1 >  "&INT(MAX(SANDBOX!$A$2:$A))-(
IFERROR(
          VLOOKUP(
           SUBSTITUTE($F$4," ",""),
            {"24HOURS",0;
            "2DAYS",1;
            "3DAYS",4; 
            "7DAYS",8; 
            "2WEEKS",16;  
            "1MONTH",30;
            "3MONTHS",90; 
            "6MONTHS",180; 
            "1YEAR",365;
            "2YEARS",730; 
            "3YEARS",1095},
           2,FALSE))
)-1, 0)

Then instead of sheet.getRange(formula.match(/\w+:\w+/)[0]) use helperSheet.getDataRange(). You will have to set an appropriate way to declare helperSheet.


Related to Rev. 8

The code on Tanaike's answer reads data from Sheet1 but your sheet is named SANDBOX.

Rubén
  • 34,714
  • 9
  • 70
  • 166
  • Because that is the name for my sheet in my file linked at the bottom. I pasted the code adapted to my situation not the original code. – Verminous Jun 05 '22 at 16:52
  • Until you added the code to rev. 9 there wasn't enough details in the question for us to know that you have replaced Sheet1 by SANDBOX. – Rubén Jun 05 '22 at 17:12