Kyle_H15
Kyle_H15

Reputation: 1

Javascript & HTML - Passing dropdown index value as object property returns undefined

I'm brand new to this language and I've hit a road block. I've searched for a few hours about this specific problem but can't seem to figure out why my situation is so hard to fix. I'm sure I just need a set of eyes that knows what they're looking at.

I'm trying to pass the index of a dropdown select into my Code.gs function so that I can use it elsewhere to select a certain row of a data in a spreadsheet. I set up the object driverPicked and give it the property selected, and then hunt for the selected index property. As I'm really new to this, I'm having a hard time understanding the consequences of nesting properties like this and could use a clue as to how to solve the "TypeError: Cannot read property 'selected' of undefined (line 39, file "Code")" error I've been getting for hours now.

There are so many similar answers but I can't sort out exactly how they apply to me - so forgive me for that!

var url = "REDACTED";

function doGet(e){

//var x = document.getElementById("driver").selectedIndex;  

var driverIndex = userClicked();
var ss = SpreadsheetApp.openByUrl(url);
var ws = ss.getSheetByName("Results_2019");
var listDrivers = ws.getRange(4,1,ws.getRange("A3").getDataRegion().getLastRow(),1).getValues();
var infoHeading = ws.getRange(3, 1, 1, 8).getValues();
var driverInfo = ws.getRange(driverIndex, 1, 1 ,8).getValues();


var tmp = HtmlService.createTemplateFromFile("page"); 
tmp.title = "GLTC Driver List";
//tmp.listDrivers = listDrivers.map(function(r){r[0];});
tmp.listDrivers = [].concat.apply([], listDrivers);
tmp.infoHeading = [].concat.apply([], infoHeading);
tmp.driverInfo = [].concat.apply([], driverInfo);
tmp.driverIndex = driverIndex;


return tmp.evaluate();

}


function include(filename){
  return HtmlService.createHtmlOutputFromFile(filename).getContent();
}


function userClicked(driverPicked) {
  console.log(driverPicked); 
  var driverName = driverPicked.value;
  return driverName;
}


<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <!--Let browser know website is optimized for mobile-->
     <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <!--Import Google Icon Font-->
     <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <!-- Compiled and minified CSS -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
    <?!= include("page-css"); ?>
  </head>
  <body>

    <h1><?= title; ?></h1>

    <div class="input-field">
  <select id="driver">
    <option disabled selected>Choose Driver</option>
    <? for(var i=0;i<listDrivers.length;i++){ ?>
      <option class="selected">
        <?= listDrivers[i]; ?>
      </option>
      <? } ?>
  </select>
</div>

  <h5><?= infoHeading; ?></h5>

  <h5><?= driverInfo; ?></h5>

  <h5><?= driverIndex; ?></h5>

<script>  

document.getElementById("driver").addEventListener("change", doStuff); // add onchange listener to the select element instead of options

function doStuff(event) {
  var driverPicked = {};

  driverPicked.driver = event.target.value;
  driverPicked.selected = event.target.options.selectedIndex;
  userClicked(driverPicked);
}


  </script>

    <!-- Compiled and minified JavaScript -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
    <?!= include("page-js"); ?>

  </body>
</html>

Upvotes: 0

Views: 133

Answers (2)

blurfus
blurfus

Reputation: 14031

There a a few simplifications done here. Main one would be that instead of invoking the function via google.script.run I am just directly calling it.

UPDATE

I forgot to include in the initial response. Element ids are supposed to be unique. If you loop through all your elements and do not change their id value then you are crafting incorrect HTML and the function getElementById(...) will return an array of elements instead of just on (thus, the rest of the code will not work as expected)

See demo below

document.getElementById("driver").addEventListener("change", doStuff);

function doStuff() {
  var driverPicked = {};
  selectBox = document.getElementById("driver");

  driverPicked = selectBox.options[selectBox.selectedIndex];
  console.log('User changed value to: ');
  console.log(driverPicked);

  // will this work on other browsers?
  // google.script.run.userClicked(driverPicked);
  // I am using this instead for demo purposes
  userClicked(driverPicked);
}

function userClicked(driverPicked) {
  var driverName = driverPicked.value;
  var driverText = driverPicked.text;
  console.log('name/value: ' + driverName + ' text: ' + driverText);

  return driverName;
}
<div class="input-field">
  <select id="driver">
    <option disabled>Choose Driver</option>
    <option value="1">One</option>
    <option value="2">Two</option>
    <option value="3">Three</option>
    <option value="4">Four</option>
  </select>
</div>

Upvotes: 0

Clarity
Clarity

Reputation: 10873

A few issues with the code:

  1. DOM elements' ids have to be unique, so when rendering options you either need to add index to id or use a class name instead.
  2. Instead of click use change event for select elements.
  3. You can access event object as an argument inside doStuff function instead of querying DOM elements manually. I'm not sure why would you need to get an index, but in order to get driver's name you can do smth like this:
<div class="input-field">
  <select id="driver">
    <option disabled selected>Choose Driver</option>
    <? for(var i=0;i<listDrivers.length;i++){ ?>
      <option class="selected"> // use class instead of id here
        <?= listDrivers[i]; ?>
      </option>
      <? } ?>
  </select>
</div>
document.getElementById("driver").addEventListener("change", doStuff); // add onchange listener to the select element instead of options

function doStuff(event) {
  var driverPicked = {};

  driverPicked.driver = event.target.value;
  driverPicked.selected = event.target.options.selectedIndex;

  google.script.run.userClicked(driverPicked);
}

function userClicked(driverPicked) {

  var driverName = driverPicked.value;
  return driverName;
}

Here's a runnable example with some mock select options:

document.getElementById("driver").addEventListener("change", doStuff); // add onchange listener to the select element instead of options

function doStuff(event) {
  var driverPicked = {};

  driverPicked.driver = event.target.value;
  driverPicked.selected = event.target.options.selectedIndex;
  userClicked(driverPicked);
}

function userClicked(driverPicked) {
  console.log(driverPicked); 
  var driverName = driverPicked.value;
  return driverName;
}
<div class="input-field">
  <select id="driver">
    <option disabled selected>Choose Driver</option>
      <option class="selected"> Driver 1</option>
      <option class="selected"> Driver 2</option>
  </select>
</div>

Upvotes: 1

Related Questions