6

In Google Apps Script ist eines der grundlegenden Tools der onEdit-Trigger in einer Tabelle, mit dem wir erkennen können, wann ein Benutzer eine Zelle bearbeitet und reagiert zu ihm.Ermitteln, dass Benutzer Zeile oder Spalte in eine Google-Tabelle einfügen und in einem Skript reagieren

Wie wäre es, wenn ein Benutzer eine Zeile oder Spalte einfügt? Gibt es eine Möglichkeit, das zu erkennen?

Würde das einen onEdit auslösen? Wenn dem so ist, würde ich im ScriptDb eine Zählung der Anzahl der Zeilen oder Spalten beibehalten und dann jedes Mal überprüfen, aber das wäre sehr zeitaufwendig, da getMaxRows() schon ziemlich langsam ist und das Erreichen von ScriptDb ist wie Gut.

Was denkst du?

Antwort

16

Es gibt eine Reihe von Bearbeitungsaktionen, die onEdit() nicht auslösen, ist dies nicht eine umfassende Liste ist, gibt es many more reported exclusions:

  • Formularübermittlungen Issue 4568.
  • Einfügen oder Löschen von Zeilen oder Spalten Issue 1363
  • Änderungen im Aussehen (Schriftart, Format, Ausrichtung, Borders) Issue 2476
  • Bereich Füllung (zB mit 1,2,3 beginnen, jene Zellen auszuwählen, und ziehen Sie die 'handle', um mehr Zellen mit 4,5,6 zu füllen ...) Issue 399
  • Von Skripten geschriebene Werte. Issue 338
  • Suchen/Ersetzen Issue 1754
  • Issue 1568, Issue 1119

Wenn Sie wissen wollen, wie viele Zeilen in einer Tabelle sind, diese auszuführen, etwa 120 ms dauert:

var numCols = SpreadsheetApp.getActiveSheet().getRange("1:1").getLastColumn(); 
var numRows = SpreadsheetApp.getActiveSheet().getRange("A:A").getLastRow(); 

I‘ Habe schon gezeigt, dass it's faster to write a value to a sheet ScriptDB verwenden. Sie können eine unbedeutende Zeit erwarten, um einen kleinen Bereich um 1ms zu schreiben.

Also, wenn Sie könnte erkennen, eine Zeile oder Spalte hinzugefügt wird, würde es weniger als 2 Zehntel einer Sekunde kosten, um die Änderung zu registrieren. Diese onEdit() demonstriert eine Technik, um das Ausmaß einer Kalkulationstabelle zu messen und Änderungen in Blattdimensionen zu melden. (Um Zeilen oder Spalten zu testen, hinzuzufügen oder zu löschen, führen Sie eine Bearbeitung aus, die onEdit() auslöst.) Sie enthält auch Timer - Sie können mit anderen Methoden experimentieren, um Werte zu messen und/oder zu speichern.

function onEdit() { 
    // Use start & stop to time operations 
    var start = new Date().getTime(); 

    // We want the size of the sheet, so will select ranges across and down the 
    // whole sheet. Cannot use getDataRange(), as it selects only occupied cells. 
    var numCols = SpreadsheetApp.getActiveSheet().getRange("1:1").getLastColumn() 
    var numRows = SpreadsheetApp.getActiveSheet().getRange("A:A").getLastRow(); 

    var stop = new Date().getTime(); 
    var timeToMeasure = (stop-start); 

    // Did things change? 
    var oldSize = SpreadsheetApp.getActiveSheet().getRange("A1:B1").getValues(); 
    if (oldSize[0][0] != numCols || oldSize[0][1] != numRows) { 
    // Yes, they did - Let's store the new dimensions 
    start = new Date().getTime(); 

    SpreadsheetApp.getActiveSheet().getRange("A1:B1").setValues([[numCols,numRows]]); 

    var stop = new Date().getTime(); 
    var timeToStore = (stop-start); 

    Browser.msgBox("Sheet is "+numCols+" by "+numRows+"." 
        +" ("+timeToMeasure+"ms to measure, "+timeToStore+"ms to store.)"); 
    } 
} 
+0

Hallo Mogsdad! Vielen Dank für Ihre Antwort, es ist wirklich cool, so eine präzise Diskussion zu haben. Ich habe versucht, einen Onedit-Trigger für eine Funktion einzurichten, aber tatsächlich wird er nicht aufgerufen, wenn eine Zeile oder Spalte eingefügt wird. Das ist schade, denn eines der Dinge, die ich versuche zu tun, ist, dies sofort zu erkennen, so dass ich die Änderung rückgängig machen kann. Verhindern Sie auf gewisse Weise, dass der Benutzer Zeilen oder Spalten einfügen kann. Gibt es überhaupt keinen Auslöser, mit dem wir in diesen Situationen direkt oder indirekt etwas aufwecken könnten? –

+0

Das Beste, was Sie tun können, ist einen zeitbasierten Auslöser einzurichten - obwohl ich nicht weiß, wie Sie herausfinden würden, was hinzugefügt oder gelöscht wurde. Ich habe meine Antwort mit Links zu einer Reihe von gemeldeten Problemen aktualisiert. Gehen Sie zu ihnen und schalten Sie sie ein, um sich über den Fortschritt zu informieren. Fügen Sie außerdem eigene Kommentare hinzu, um Google zu ermutigen, sie anzusprechen. Wenn Ihre Frage so gut wie erwartet beantwortet wurde, denken Sie daran, die Antwort zu "akzeptieren". (Und ein upvote wird immer geschätzt!) – Mogsdad

+0

Ok danke :) –

19

Google hat ein „On Change“ Ereignis, die Zeile/Spalte Einfügen/Löschen erkennt zusammen mit anderen Arten von Änderungen, die Arten, für die Sie here unter den zulässigen Werten für changeType sehen können. Im Folgenden finden Sie die Anweisungen unter here, in denen beschrieben wird, wie Sie Ihrem Projekt einen Trigger hinzufügen, damit Ihre Funktion beim Auftreten des Ereignisses "On Change" aufgerufen wird.

manuell eine installierbare Trigger über einen Dialog im Script-Editor folgendermaßen erstellen vor:

  1. Aus dem Script-Editor, wählen Bearbeiten> Auslöser des aktuellen Projekts.
  2. Klicken Sie auf den Link, der besagt: Keine Trigger eingerichtet. Klicken Sie hier, um jetzt einen hinzuzufügen.
  3. Unter Führen Sie aus, wählen Sie den Namen der Funktion, die Sie auslösen möchten.
  4. Unter Event, wählen Sie entweder zeitgesteuerte oder die Google App, dass das Skript bound zu (zum Beispiel Aus Tabelle) ist.
  5. auswählen und konfigurieren Sie den Typ des Triggers Sie (zum Beispiel ein Stunden-Timer die fährt jede Stunde oder eine auf offenen Trigger) erstellen möchten.
  6. Klicken Sie optional auf Benachrichtigungen, um zu konfigurieren, wie und wann Sie per E-Mail kontaktiert werden, wenn die ausgelöste Funktion fehlschlägt.
  7. Klicken Sie auf Speichern Sie.

In Schritt 4 würden Sie wählen Aus Tabelle und 5 in Schritt würden Sie Bei Änderung wählen. Das sollte den gewünschten Effekt haben. Es gibt auch Optionen zum programmgesteuerten Hinzufügen von Triggern und zum Anfordern von Autorisierung, wenn Sie versuchen, dies in einem Add-On zu verwenden, um es an Benutzer zu verteilen. Beide sind in der Installable Triggers Dokumentation ausführlich beschrieben.

+0

Fast 3 Jahre später, und die Schritte sind sehr ähnlich. Große Antwort, +1, und all das, aber halte diesen Beitrag auf dem neuesten Stand. Anderer Tag, gleiches Blatt. Oder nicht. – bvj

+0

@bvj Danke, aktualisiert. –

3

Es gibt einen anderen Weg, den ich gerade erst benutzt habe. Jedes Mal, wenn onEdit() ausgelöst wird, gibt es eine event object (e) zurück, die Ihnen einige wertvolle Informationen darüber liefert, was gerade passiert.

Zum Beispiel gibt es Ihnen die Reichweite, die Sie von e.range abrufen können. Von dort aus können Sie auf viele verschiedene Arten Quer gehen und zB erfahren, welche Zeile bearbeitet wird. Aber es gibt auch mehr hilfreiche Daten in dem Objekt. Es gibt Ihnen den "oldvalue" (e.oldValue) der von Ihnen bearbeiteten Zelle und den neuen Wert (e.value).

Eine Möglichkeit, all diese Informationen zusammenzufassen, wäre, den Bereich zu erhalten, der der gerade bearbeiteten Zeile entspricht, und dann zu prüfen, ob alle Zellen leer sind (aber die gerade bearbeiteten Zellen) und ob kein oldValue vorhanden ist.

Das entspricht nicht unbedingt der letzten Zeile Ihrer Tabelle, sondern eine leere Zeile. Wenn Sie mit konsistent sind, wie Sie Ihre Daten füllen könnte dies für Sie arbeiten:

//val = inserted value (e.value); 
//old = old Value (e.oldValue); 
//col = number of column being edited 
//arr = array with the indexes of the columns that should be completed so as to make a new row [0,1,2...n] 
function isInsert(old, val, col, arr){ 
    if((typeof val != "object")&&!old&&(arr.some(isNotEmpty, col))) 
    return true; 
    else 
    return false; 
} 

function isNotEmpty(el){ 
    if(this == el) 
    return true; 
} 
1

Ich hatte Schwierigkeiten mit diesem haben, bis ich Berechtigungen für das Skript gab. Andernfalls funktioniert die PropertiesService-Funktionalität nicht.Sobald ich es tat, war ich in der Lage zu erkennen, welche Zeile mit dem folgenden Code eingefügt worden war:

var props = PropertiesService.getUserProperties(); 

function onEdit(e) { 
    props.setProperty("firstRow", e.range.getRow()); 
    props.setProperty("lastRow", e.range.getLastRow()); 
} 


function onChange(e){ 
if(e.changeType=="INSERT_ROW") 
    SpreadsheetApp.getUi().alert("Inserted Rows: " + 
           props.getProperty("firstRow") + 
           " - " + 
           props.getProperty("lastRow")); 
} 
+0

Ich habe auch Probleme, können Sie mir sagen, wie Sie "dem Skript Berechtigungen erteilt haben"? Ich sehe in den Unterlagen nichts darüber! – wulftone

0

Ich habe mit onEdit und onChange herum spielen. Mit der onEdit-Antwort können Sie auf die Zeilen zugreifen, die bearbeitet wurden. Leider erlaubt Ihnen die onChange-Antwort dies nicht. Für eine robuste Lösung sieht es also so aus, als müssten Sie beide Trigger ansprechen. Wenn für Ihr Blatt keine leeren Zeilen/Spalten erforderlich sind, entfernt das folgende Skript alle neu hinzugefügten Zeilen/Spalten, löscht alle leeren Zeilen/Spalten (falls die Benutzer-Gruppe Zeilen/Spalten hinzugefügt hat) und warnt den Benutzer, dass sie keine Zeilen hinzufügen können Spalten:

////////////////////// 
// Global Variables // 
////////////////////// 

var SHEET = SpreadsheetApp.getActiveSheet(); 
var PROPERTIES = PropertiesService.getScriptProperties(); 

//////////////////// 
// Event Triggers // 
//////////////////// 

/** 
* Track original sheet row/column count and register onChange trigger. 
*/ 
function onOpen() 
{ 
    // Set original dimensions 
    PROPERTIES.setProperty('rows', SHEET.getMaxRows()); 
    PROPERTIES.setProperty('columns', SHEET.getMaxColumns()); 

    // Create onChange trigger 
    ScriptApp 
     .newTrigger('deleteNewRowsAndColumns') 
     .forSpreadsheet(SpreadsheetApp.getActive()) 
     .onChange() 
     .create(); 
} 

/** 
* If new rows or columns were added to the sheet 
* warn the user that they cannot perform these 
* actions and delete empty (new) rows and columns. 
* 
* @param e 
*/ 
function deleteNewRowsAndColumns(e) 
{ 
    switch(e.changeType) { 
     case 'INSERT_COLUMN': 
      removeEmptyColumns(); 
      warn(); 
      break; 
     case 'INSERT_ROW': 
      removeEmptyRows(); 
      warn(); 
      break; 
     default: 
      return 
    } 
} 

/////////////// 
// Utilities // 
/////////////// 

/** 
* Remove empty columns. 
* 
* This function assumes you have a header row in which 
* all columns should have a value. Change headerRow value 
* if your headers are not in row 1. 
*/ 
function removeEmptyColumns() { 
    var maxColumns = SHEET.getMaxColumns(); 
    var lastColumn = SHEET.getLastColumn(); 
    if (maxColumns - lastColumn != 0) { 
     // New column(s) were added to the end of the sheet. 
     SHEET.deleteColumns(lastColumn + 1, maxColumns - lastColumn); 
    } else { 
     // New column was added in the middle of the sheet. 
     // Start from last column and work backwards, delete 
     // first column found with empty header cell. 
     var headerRow = 1; 
     var headers = SHEET.getRange(headerRow, 1, 1, lastColumn).getValues()[0]; 
     for (var col = lastColumn; col >= 1; col--) { 
      if (headers[col -1] == '') { 
       SHEET.deleteColumn(col); 
       // Since can only insert one column to the left 
       // or right at a time, can safely exit here; 
       break; 
      } 
     } 
    } 
} 

/** 
* Remove empty rows. 
* 
* This function assumes that all rows should 
* have data in the first cell. 
*/ 
function removeEmptyRows() { 
    var maxRows = SHEET.getMaxRows(); 
    var lastRow = SHEET.getLastRow(); 
    if (maxRows-lastRow != 0) { 
     // New row(s) were added to the end of the sheet. 
     SHEET.deleteRows(lastRow + 1, maxRows - lastRow); 
    } else { 
     // New row was added in the middle of the sheet. 
     // Start from last column and work backwards, delete 
     // first empty column found. 
     var values = SHEET.getRange('A:A').getValues(); 
     var startIndex = values.length - 1; 
     for (var i = startIndex; i >= 0; i--) { 
      if (values[i] && values[i][0] == '') { 
       SHEET.deleteRow(i + 1); 
       // User can bulk add rows to the bottom of the file 
       // but can only add 1 above or below at a time in the 
       // middle of the file, so it's safe to exit here. 
       break; 
      } 
     } 
    } 
} 

/** 
* Return user warning message about adding new rows and columns 
*/ 
function warn() 
{ 
    SpreadsheetApp.getUi().alert('You cannot add new rows or columns.'); 
}