安全getElementById或尝试确定GUI中是否存在ID

时间:2012-10-04 08:02:01

标签: google-apps-script

方法UiInstance.getElementById(ID)始终返回GenericWidget个对象,即使ID不存在。

是否有某种方法可以找出我的应用中不存在返回的对象,或者检查UI是否包含具有给定ID的对象?


使用GUI构建器创建的UI解决方案:

function getSafeElement(app, txtID) {
    var elem = app.getElementById(txtID);
    var bExists = elem != null && Object.keys(elem).length < 100;
    return bExists ? elem : null;
}

如果ID不存在,则返回null。我没有测试所有小部件的密钥长度边界,所以要小心并用你的GUI测试它。

编辑:此解决方案仅适用于doGet()函数。它在服务器处理程序中不起作用,因此在这种情况下将它与@ corey-g answer结合使用。

2 个答案:

答案 0 :(得分:4)

这只会在您创建窗口小部件的同一执行中起作用,而不会在您检索窗口小部件的后续事件处理程序中起作用,因为在这种情况下,无论是否存在,所有内容都是GenericWidget。

您可以亲眼看到解决方案失败:

function doGet() {
  var app = UiApp.createApplication();
  app.add(app.createButton().setId("control").addClickHandler(
      app.createServerHandler("clicked")));
  app.add(app.createLabel(exists(app)));
  return app;
}

function clicked() {
  var app = UiApp.getActiveApplication();
  app.add(app.createLabel(exists(app)));
  return app;
}

function exists(app) {
  var control = app.getElementById("control");
  return control != null && Object.keys(control).length < 100;
}

应用程序将首先打印“true”,但在点击处理程序中,它将为同一个小部件打印“false”。

这是设计上的; GenericWidget是对浏览器中的窗口小部件进行排序的“指针”。我们不会跟踪您创建的小部件,以减少浏览器和脚本之间的数据传输和延迟(否则我们必须发送每个事件处理程序上存在的小部件的长列表)。您应该跟踪您创建的内容,并且只“询问”您已经知道的小部件(并且您已经知道“真实”类型)。

如果您真的想要跟踪存在哪些小部件,您有两个主要选项。第一种是在创建小部件时将条目记录到ScriptDb中,然后再查找它们。像这样:

function doGet() {
  var app = UiApp.createApplication();
  var db = ScriptDb.getMyDb();
  // You'd need to clear out old entries here... ignoring that for now
  app.add(app.createButton().setId('foo')
      .addClickHandler(app.createServerHandler("clicked")));
  db.save({id: 'foo', type: 'button'});
  app.add(app.createButton().setId('bar'));
  db.save({id: 'bar', type: 'button'});
  return app
}

然后在处理程序中,您可以查找其中的内容:

function clicked() {
  var db = ScriptDb.getMyDb();
  var widgets = db.query({}); // all widgets
  var button = db.query({type: 'button'}); // all buttons
  var foo = db.query({id: 'foo'}); // widget with id foo
}

或者,您可以通过使用标记

在纯粹的UiApp中完成此操作
function doGet() {
  var app = UiApp.createApplication();
  var root = app.createFlowPanel();  // need a root panel
  // tag just needs to exist; value is irrelevant.
  var button1 = app.createButton().setId('button1').setTag(""); 
  var button2 = app.createButton().setId('button2').setTag("");
  // Add root as a callback element to any server handler
  // that needs to know if widgets exist
  button1.addClickHandler(app.createServerHandler("clicked")
      .addCallbackElement(root));
  root.add(button1).add(button2);
  app.add(root);
  return app;
}

function clicked(e) {
  throw "\n" +
      "button1 " + (e.parameter["button1_tag"] === "") + "\n" + 
      "button2 " + (e.parameter["button2_tag"] === "") + "\n" + 
      "button3 " + (e.parameter["button3_tag"] === "");
}

这将抛出:

button1 true
button2 true
button3 false

因为按钮1和2存在但3不存在。您可以通过在标记中存储类型来获得更好的功能,但这足以检查小部件是否存在。它的工作原理是因为根的所有子节点都被添加为回调元素,并且所有回调元素的标签都与处理程序一起发送。请注意,这听起来和听起来一样昂贵,对于具有大量小部件的应用程序可能会影响性能,尽管在许多情况下它可能都可以,特别是如果您只将root作为回调元素添加到实际需要验证的处理程序任意小部件的存在。

答案 1 :(得分:3)

我的初始解决方案是错误的,因为它返回false存在的控件。

基于Corey的答案,解决方案是添加setTag(“”)方法,现在可以使用代码了。它仅适用于事件处理程序,因为它使用标记。

function doGet() {
  var app = UiApp.createApplication();
  var btn01 = app.createButton("control01").setId("control01").setTag("");
  var btn02 = app.createButton("control02").setId("control02").setTag("");
  var handler = app.createServerHandler("clicked");
  handler.addCallbackElement(btn01);
  handler.addCallbackElement(btn02);
  btn01.addClickHandler(handler);
  btn02.addClickHandler(handler);
  app.add(btn01);
  app.add(btn02);
  return app;
}

function clicked(e) {
  var app = UiApp.getActiveApplication();
  app.add(app.createLabel("control01 - " + controlExists(e, "control01")));
  app.add(app.createLabel("control02 - " + controlExists(e, "control02")));
  app.add(app.createLabel("fake - " + controlExists(e, "fake")));
  return app;
}

function controlExists(e, controlName) {
  return e.parameter[controlName + "_tag"] != null;
}