it-swarm.com.de

Wie führen Sie dynamische/abhängige Dropdowns in Google Sheets durch?

Wie erhalten Sie eine Unterkategoriespalte, um eine Dropdown-Liste basierend auf dem in der Dropdown-Liste für die Hauptkategorie ausgewählten Wert in Google Sheets zu füllen?

Ich habe gegoogelt und konnte keine guten Lösungen finden, deshalb wollte ich meine eigenen teilen. Bitte sehen Sie meine Antwort unten.

37
tarheel

Sie können mit einem Google-Blatt beginnen, das mit einer Hauptseite eingerichtet ist, und die Quellseite wie unten gezeigt herunterklappen.

Sie können die erste Dropdown-Spalte über die normalen Eingabeaufforderungen des Menüs Daten> Validierungen einrichten.

Hauptseite

Main Page with the drop down for the first column already populated.

Dropdown-Quellseite

Source page for all of the sub-categories needed

Danach müssen Sie ein Skript mit dem Namen onEdit einrichten. (Wenn Sie diesen Namen nicht verwenden, führt getActiveRange () nur die Zelle A1 zurück.)

Und verwenden Sie den hier bereitgestellten Code:

function onEdit() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = SpreadsheetApp.getActiveSheet();
  var myRange = SpreadsheetApp.getActiveRange();
  var dvSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Categories");
  var option = new Array();
  var startCol = 0;

  if(sheet.getName() == "Front Page" && myRange.getColumn() == 1 && myRange.getRow() > 1){
    if(myRange.getValue() == "Category 1"){
      startCol = 1;
    } else if(myRange.getValue() == "Category 2"){
      startCol = 2;
    } else if(myRange.getValue() == "Category 3"){
      startCol = 3;
    } else if(myRange.getValue() == "Category 4"){
      startCol = 4;
    } else {
      startCol = 10
    }

  if(startCol > 0 && startCol < 10){
    option = dvSheet.getSheetValues(3,startCol,10,1);
    var dv = SpreadsheetApp.newDataValidation();
    dv.setAllowInvalid(false);  
    //dv.setHelpText("Some help text here");
    dv.requireValueInList(option, true);
    sheet.getRange(myRange.getRow(),myRange.getColumn() + 1).setDataValidation(dv.build());
   }

  if(startCol == 10){
    sheet.getRange(myRange.getRow(),myRange.getColumn() + 1).clearDataValidations();
  } 
  }
}

Richten Sie anschließend im Skripteditor einen Auslöser ein, indem Sie Bearbeiten> Aktuelle Projektauslöser wählen. Daraufhin wird ein Fenster geöffnet, in dem Sie verschiedene Dropdowns auswählen können, um schließlich zu diesem Ergebnis zu gelangen: 

Trigger set up

Sie sollten gut sein, danach zu gehen!

23
tarheel

Hinweis

Die Skripts haben ein Limit: Sie können bis zu 500 Werte in einer einzigen Dropdown-Liste verarbeiten.

Neues Skript. 201801

Das Skript wurde im Januar 2018 veröffentlicht. Bitte sehen Sie:

  1. Die Hauptseite mit Anweisungen und Demo, auf der Sie eine Frage stellen können.
  2. GitHub-Seite mit Anweisungen und Quellcode.

Verbesserungen:

  1. Beschleunigen
  2. Behandelt mehrere Regeln in einem Blatt
  3. Verknüpfen Sie andere Arbeitsblätter als Quelldaten.
  4. Benutzerdefinierte Spaltenreihenfolge von Dropdown-Listen

Alte Schrift <201801

Skriptversionen

  1. v.1 .
  2. v.2. 2016-03 . Verbessert: Arbeitet mit Duplikaten in jeder Kategorie. Zum Beispiel, wenn wir list1 mit Automodellen und list2 mit Farben haben. Die Farbe kann in jedem Modell wiederholt werden.
  3. v3. 2017-01 . Verbessert: Kein Fehler, wenn der einzige Wert eingegeben wird.
  4. Neueste Version: 2018-02. Siehe den Artikel hier .

Diese Lösung ist nicht perfekt, bietet jedoch einige Vorteile:

  1. Lassen Sie mehrere Dropdown-Listen erstellen
  2. Gibt mehr Kontrolle
  3. Die Quelldaten werden auf dem einzigen Blatt platziert, sodass sie einfach bearbeitet werden können

Zunächst einmal ist hier Arbeitsbeispiel , damit Sie es testen können, bevor Sie fortfahren.

 When you choose one option, script makes new validation rule

Mein Plan:

  1. Daten vorbereiten
  2. Erstellen Sie die erste Liste wie üblich: Data > Validation
  3. Skript hinzufügen, einige Variablen setzen
  4. Erledigt!

Daten vorbereiten

Daten sehen aus wie eine einzige Tabelle mit allen möglichen Varianten. Es muss sich auf einem separaten Blatt befinden, damit es vom Skript verwendet werden kann. Schau dir dieses Beispiel an:

 Sourse Data

Hier haben wir vier Stufen, jeder Wert wiederholt sich. Beachten Sie, dass 2 Spalten auf der rechten Seite der Daten reserviert sind. Geben Sie also keine Daten ein.


Erste einfache Datenvalidierung (DV)

Bereiten Sie eine Liste eindeutiger Werte vor. In unserem Beispiel handelt es sich um eine Liste von Planeten. Suchen Sie nach freiem Speicherplatz auf dem Arbeitsblatt mit den Daten und fügen Sie die Formel ein: =unique(A:A) Wählen Sie im Hauptblatt die erste Spalte aus, in der DV beginnen soll. Gehen Sie zu Daten> Validierung und wählen Sie den Bereich mit einer eindeutigen Liste aus.

 4 columns right from data


Skript

Fügen Sie diesen Code in den Skripteditor ein:

function SmartDataValidation(event) 
{
  //--------------------------------------------------------------------------------------
  // The event handler, adds data validation for the input parameters
  //--------------------------------------------------------------------------------------
  
  
  // Declare some variables:
  //--------------------------------------------------------------------------------------
  var TargetSheet = 'Main' // name of the sheet where you want to verify the data
  var LogSheet = 'Data' // name of the sheet with information
  var NumOfLevels = 4 // number of associated drop-down list levels
  var lcol = 2; // number of the leftmost column, in which the changes are checked; A = 1, B = 2, etc.
  var lrow = 2; // line number from which the rule will be valid
  //--------------------------------------------------------------------------------------
  
  //	===================================   key variables	 =================================
  //
  //		ss			sheet we change (TargetSheet)
  //			br				range to change
  //			scol			number of column to edit
  //			srow			number of row to edit	
  //			CurrentLevel	level of drop-down, which we change
  //			HeadLevel		main level
  //			r				current cell, which was changed by user
  //			X         		number of levels could be checked on the right
  //
  //		ls			Data sheet (LogSheet)
  //
  //    ======================================================================================
  
  
  // [ 01 ].Track sheet on which an event occurs
  var ts = event.source.getActiveSheet();
  var sname = ts.getName();
  
  if (sname == TargetSheet) 
  {
    
    // ss -- is the current book
    var ss = SpreadsheetApp.getActiveSpreadsheet();
    
    // [ 02 ]. If the sheet name is the same, you do business...
    var ls = ss.getSheetByName(LogSheet); // data sheet
    
    // [ 03 ]. Determine the level
    
    //-------------- The changing sheet --------------------------------
    var br = event.source.getActiveRange();
    var scol = br.getColumn(); // the column number in which the change is made
    var srow = br.getRow() // line number in which the change is made
    // Test if column fits
    if (scol >= lcol) 
    {
      // Test if row fits
      if (srow >= lrow) 
      {  
        var CurrentLevel = scol-lcol+2;
        // adjust the level to size of
        // range that was changed
        var ColNum = br.getLastColumn() - scol + 1;
        CurrentLevel = CurrentLevel + ColNum - 1; 
        
        // also need to adjust the range 'br'
        if (ColNum > 1) 
        {
          br = br.offset(0,ColNum-1);
        } // wide range
        
        var HeadLevel = CurrentLevel - 1; // main level
        
        // split rows
        var RowNum = br.getLastRow() - srow + 1;
        
        var X = NumOfLevels - CurrentLevel + 1;

        
        // the current level should not exceed the number of levels, or 
        // we go beyond the desired range
        if (CurrentLevel <= NumOfLevels )	
        {
          // determine columns on the sheet "Data"
          var KudaCol = NumOfLevels + 2
          var KudaNado = ls.getRange(1, KudaCol);
          var lastRow = ls.getLastRow(); // get the address of the last cell
          var ChtoNado = ls.getRange(1, KudaCol, lastRow, KudaCol);

          // ============================================================================= > loop >				
          for (var j = 1; j <= RowNum; j++)
          {		
            for (var k = 1; k <= X; k++) 
            {
               
              HeadLevel = HeadLevel + k - 1; // adjust parent level
              CurrentLevel = CurrentLevel + k - 1; // adjust current level
              
              var r = br.getCell(j,1).offset(0,k-1,1);
              var SearchText = r.getValue(); // searched text

              // if anything is choosen!
              if (SearchText != '') 
              {
                
                //-------------------------------------------------------------------
                
                // [ 04 ]. define variables to costumize data
                // for future data validation
                //--------------- Sheet with data --------------------------           
                // combine formula 
                // repetitive parts
                var IndCodePart = 'INDIRECT("R1C' + HeadLevel + ':R' + lastRow + 'C';
                IndCodePart = IndCodePart + HeadLevel + '",0)';
                // the formula
                var code = '=UNIQUE(INDIRECT("R" & MATCH("';
                code = code + SearchText + '",';
                code = code + IndCodePart;
                code = code + ',0) & "C" & "' + CurrentLevel
                code = code + '" & ":" & "R" & COUNTIF(';
                code = code + IndCodePart;   
                code = code + ',"' + SearchText + '") + MATCH("';
                code = code + SearchText + '";';
                code = code + IndCodePart;
                code = code + ',0) - 1'; 
                code = code + '& "C" & "' ;   
                code = code + CurrentLevel + '",0))';
                // Got it! Now we have to paste formula
                
                KudaNado.setFormulaR1C1(code);   
                // get required array
                var values = [];
                for (var i = 1; i <= lastRow; i++) 
                {
                  var currentValue = ChtoNado.getCell(i,1).getValue();
                  if (currentValue != '') 
                  { 
                    values.Push(currentValue);
                  } 
                  else 
                  {
                    var Variants = i-1; // number of possible values
                    i = lastRow; // exit loop
                  }       
                }
                //-------------------------------------------------------------------
                
                // [ 05 ]. Build daya validation rule
                var cell = r.offset(0,1);
                var rule = SpreadsheetApp
                .newDataValidation()
                .requireValueInList(values, true)
                .setAllowInvalid(false)
                .build();
                cell.setDataValidation(rule); 
                if (Variants == 1) 
                {
                  cell.setValue(KudaNado.getValue());		
                } // the only value
                else
                {
                  k = X+1;
                } // stop the loop through columns
                
                
              } // not blanc cell
              else
              {
                // kill extra data validation if there were 
                // columns on the right
                if (CurrentLevel <= NumOfLevels ) 
                {
                  for (var i = 1; i <= NumOfLevels; i++) 
                  {
                    var cell = r.offset(0,i);
                    // clean
                    cell.clear({contentsOnly: true});
                    // get rid of validation
                    cell.clear({validationsOnly: true});
                  }
                } // correct level
              } // empty row
            } // loop by cols
          } // loop by rows
          // ============================================================================= < loop <	
          
        } // wrong level
        
      } // rows
    } // columns... 
  } // main sheet
}

function onEdit(event) 
{
  
  SmartDataValidation(event);
  
}

Hier sind eine Reihe von Variablen, die geändert werden sollen. Sie finden sie im Skript:

  var TargetSheet = 'Main' // name of the sheet where you want to verify the data
  var LogSheet = 'Data' // name of the sheet with information
  var NumOfLevels = 4 // number of associated drop-down list levels
  var lcol = 2; // leftmost column, in which the changes are checked; A = 1, B = 2, etc.
  var lrow = 2; // line number from which the rule will be valid

Ich schlage vor, dass jeder, der sich mit Skripts auskennt, seine Änderungen an diesen Code sendet. Ich denke, es gibt einen einfacheren Weg, um eine Validierungsliste zu finden und das Skript schneller laufen zu lassen.

8
Max Makhrov

Hier haben Sie eine andere Lösung, die auf der von @tarheel bereitgestellten basiert

function onEdit() {
    var sheetWithNestedSelectsName = "Sitemap";
    var columnWithNestedSelectsRoot = 1;
    var sheetWithOptionPossibleValuesSuffix = "TabSections";

    var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    var activeSheet = SpreadsheetApp.getActiveSheet();

    // If we're not in the sheet with nested selects, exit!
    if ( activeSheet.getName() != sheetWithNestedSelectsName ) {
        return;
    }

    var activeCell = SpreadsheetApp.getActiveRange();

    // If we're not in the root column or a content row, exit!
    if ( activeCell.getColumn() != columnWithNestedSelectsRoot || activeCell.getRow() < 2 ) {
        return;
    }

    var sheetWithActiveOptionPossibleValues = activeSpreadsheet.getSheetByName( activeCell.getValue() + sheetWithOptionPossibleValuesSuffix );

    // Get all possible values
    var activeOptionPossibleValues = sheetWithActiveOptionPossibleValues.getSheetValues( 1, 1, -1, 1 );

    var possibleValuesValidation = SpreadsheetApp.newDataValidation();
    possibleValuesValidation.setAllowInvalid( false );
    possibleValuesValidation.requireValueInList( activeOptionPossibleValues, true );

    activeSheet.getRange( activeCell.getRow(), activeCell.getColumn() + 1 ).setDataValidation( possibleValuesValidation.build() );
}

Es hat einige Vorteile gegenüber dem anderen Ansatz:

  • Sie müssen das Skript nicht jedes Mal bearbeiten, wenn Sie eine "root-Option" hinzufügen. Sie müssen nur ein neues Blatt mit den verschachtelten Optionen dieser Stammoption erstellen.
  • Ich habe das Skript überarbeitet, das mehr semantische Namen für die Variablen usw. enthält. Außerdem habe ich einige Parameter in Variablen extrahiert, um die Anpassung an Ihren speziellen Fall zu erleichtern. Sie müssen nur die ersten 3 Werte einstellen.
  • Es gibt keine Begrenzung für verschachtelte Optionswerte (ich habe die Methode getSheetValues ​​mit dem Wert -1 verwendet).

Also, wie man es benutzt:

  1. Erstellen Sie das Blatt mit den verschachtelten Selektoren
  2. Gehen Sie zu "Tools"> "Script Editor ..." und wählen Sie die Option "Leeres Projekt"
  3. Fügen Sie den an diese Antwort angehängten Code ein
  4. Ändern Sie die ersten 3 Variablen des Skripts, die Ihre Werte festlegen, und speichern Sie sie
  5. Erstellen Sie ein Blatt innerhalb desselben Dokuments für jeden möglichen Wert des "Root-Selektors". Sie müssen als Wert + das angegebene Suffix angegeben werden.

Genießen!

2
JavierCane

Bearbeiten: Die Antwort unten mag zufriedenstellend sein, hat aber einige Nachteile:

  1. Es gibt eine merkliche Pause für die Ausführung des Skripts. Ich habe eine Latenzzeit von 160 ms, und das reicht, um nervig zu sein.

  2. Sie können jedes Mal einen neuen Bereich erstellen, wenn Sie eine bestimmte Zeile bearbeiten. Dies gibt den vorherigen Einträgen einen "ungültigen Inhalt" zeitweise 

Ich hoffe, das können andere etwas aufräumen.

Hier ist eine andere Möglichkeit, die Ihnen eine Menge Bereichsnamen erspart:

Drei Arbeitsblätter im Arbeitsblatt: Nennen Sie sie als Main, List und DRange (für den dynamischen Bereich.) Auf dem Hauptblatt enthält Spalte 1 einen Zeitstempel. Dieser Zeitstempel wird am Edit geändert.

Auf Liste Ihre Kategorien und Unterkategorien sind als einfache Liste angeordnet. Ich verwende dies für das Pflanzeninventar in meiner Baumfarm, daher sieht meine Liste folgendermaßen aus:

Group   | Genus | Bot_Name
Conifer | Abies | Abies balsamea
Conifer | Abies | Abies concolor
Conifer | Abies | Abies lasiocarpa var bifolia
Conifer | Pinus | Pinus ponderosa
Conifer | Pinus | Pinus sylvestris
Conifer | Pinus | Pinus banksiana
Conifer | Pinus | Pinus cembra
Conifer | Picea | Picea pungens
Conifer | Picea | Picea glauca
Deciduous | Acer | Acer ginnala
Deciduous | Acer | Acer negundo
Deciduous | Salix | Salix discolor
Deciduous | Salix | Salix fragilis
...

Wo | gibt die Trennung in Spalten an.
Der Einfachheit halber habe ich auch die Überschriften als Namen für benannte Bereiche verwendet.

DRrange A1 hat die Formel 

=Max(Main!A2:A1000)

Dies gibt den neuesten Zeitstempel zurück.

A2 bis A4 unterscheiden sich bei:

=vlookup($A$1,Inventory!$A$1:$E$1000,2,False) 

wobei die 2 für jede Zelle nach rechts erhöht wird.

Bei der Ausführung von A2 bis A4 werden die aktuell ausgewählte Gruppe, Gattung und Art angezeigt.

Unter jedem von diesen befindet sich ein Filterbefehl, der etwa so aussieht:

= eindeutig (filter (Bot_Name, REGEXMATCH (Bot_Name, C1)))

Diese Filter füllen einen Block mit übereinstimmenden Einträgen zum Inhalt der obersten Zelle.

Die Filter können an Ihre Bedürfnisse und an das Format Ihrer Liste angepasst werden.

Zurück zum Hauptmenü: Die Datenvalidierung in Hauptdatenverzeichnis erfolgt mit Hilfe von Bereichen aus DRange.

Das Skript, das ich verwende: 

function onEdit(event) {

  //SETTINGS
  var dynamicSheet='DRange'; //sheet where the dynamic range lives
  var tsheet = 'Main'; //the sheet you are monitoring for edits
  var lcol = 2; //left-most column number you are monitoring; A=1, B=2 etc
  var rcol = 5; //right-most column number you are monitoring
  var tcol = 1; //column number in which you wish to populate the timestamp
  //

  var s = event.source.getActiveSheet();
  var sname = s.getName();
  if (sname == tsheet) {
    var r = event.source.getActiveRange();
    var scol = r.getColumn();  //scol is the column number of the edited cell
    if (scol >= lcol && scol <= rcol) {
      s.getRange(r.getRow(), tcol).setValue(new Date());
      for(var looper=scol+1; looper<=rcol; looper++) {
         s.getRange(r.getRow(),looper).setValue(""); //After edit clear the entries to the right
      }
    }
  }
}

Ursprüngliche Youtube-Präsentation, die mir den größten Teil der onEdit-Zeitstempelkomponente verlieh: https://www.youtube.com/watch?v=RDK8rjdE85Y

2

Die Weiterentwicklung dieser Lösung fortsetzen Ich habe den Ansatz erhöht, indem sie Unterstützung für mehrere Stammselektionen und tiefere verschachtelte Selektionen hinzugefügt hat. Dies ist eine Weiterentwicklung der Lösung von JavierCane (die wiederum auf tarheel aufbaut). 

/**
 * "on edit" event handler
 *
 * Based on JavierCane's answer in 
 * 
 *   http://stackoverflow.com/questions/21744547/how-do-you-do-dynamic-dependent-drop-downs-in-google-sheets
 *
 * Each set of options has it own sheet named after the option. The 
 * values in this sheet are used to populate the drop-down.
 *
 * The top row is assumed to be a header.
 *
 * The sub-category column is assumed to be the next column to the right.
 *
 * If there are no sub-categories the next column along is cleared in 
 * case the previous selection did have options.
 */

function onEdit() {

  var NESTED_SELECTS_SHEET_NAME = "Sitemap"
  var NESTED_SELECTS_ROOT_COLUMN = 1
  var SUB_CATEGORY_COLUMN = NESTED_SELECTS_ROOT_COLUMN + 1
  var NUMBER_OF_ROOT_OPTION_CELLS = 3
  var OPTION_POSSIBLE_VALUES_SHEET_SUFFIX = ""
  
  var activeSpreadsheet = SpreadsheetApp.getActiveSpreadsheet()
  var activeSheet = SpreadsheetApp.getActiveSheet()
  
  if (activeSheet.getName() !== NESTED_SELECTS_SHEET_NAME) {
  
    // Not in the sheet with nested selects, exit!
    return
  }
  
  var activeCell = SpreadsheetApp.getActiveRange()
  
  // Top row is the header
  if (activeCell.getColumn() > SUB_CATEGORY_COLUMN || 
      activeCell.getRow() === 1 ||
      activeCell.getRow() > NUMBER_OF_ROOT_OPTION_CELLS + 1) {

    // Out of selection range, exit!
    return
  }
  
  var sheetWithActiveOptionPossibleValues = activeSpreadsheet
    .getSheetByName(activeCell.getValue() + OPTION_POSSIBLE_VALUES_SHEET_SUFFIX)
  
  if (sheetWithActiveOptionPossibleValues === null) {
  
    // There are no further options for this value, so clear out any old
    // values
    activeSheet
      .getRange(activeCell.getRow(), activeCell.getColumn() + 1)
      .clearDataValidations()
      .clearContent()
      
    return
  }
  
  // Get all possible values
  var activeOptionPossibleValues = sheetWithActiveOptionPossibleValues
    .getSheetValues(1, 1, -1, 1)
  
  var possibleValuesValidation = SpreadsheetApp.newDataValidation()
  possibleValuesValidation.setAllowInvalid(false)
  possibleValuesValidation.requireValueInList(activeOptionPossibleValues, true)
  
  activeSheet
    .getRange(activeCell.getRow(), activeCell.getColumn() + 1)
    .setDataValidation(possibleValuesValidation.build())
    
} // onEdit()

Wie Javier sagt: 

  • Erstellen Sie das Blatt mit den verschachtelten Selektoren
  • Gehen Sie zu "Tools"> "Script Editor ..." und wählen Sie die Option "Blank project" .__
  • Fügen Sie den an diese Antwort angehängten Code ein
  • Ändern Sie die Konstanten am oberen Rand des Skripts, indem Sie Ihre Werte einrichten Und speichern Sie sie
  • Erstellen Sie ein Blatt innerhalb desselben Dokuments für jeden möglichen Wert von Dem "Stammselektor". Sie müssen als Wert + das angegebene Suffix benannt werden.

Und wenn Sie es in Aktion sehen wollten, habe ich ein Demo-Blatt erstellt, und Sie können den Code sehen, wenn Sie eine Kopie machen.

1
Andrew Roberts