【问题标题】:ExtJS Change Event Listener failing to fireExtJS 更改事件侦听器未能触发
【发布时间】:2026-01-13 06:15:01
【问题描述】:

http://twitter.com/jonathanjulian 要求我将这个问题作为一个问题发布到 * 上,然后被其他几个人转发。我已经有一个丑陋的解决方案,但正在按要求发布原始问题。

所以这是背景故事。我们有一个庞大的数据库应用程序,它专门为客户端视图使用 ExtJS。我们正在为从远程存储加载的行视图使用 GridPanel (Ext.grid.GridPanel)。

在我们的每个界面中,我们还有一个 FormPanel (Ext.form.FormPanel) 显示允许用户从 GridPanel 创建或编辑记录的表单。 GridPanel 列绑定到 FormPanel 表单元素,因此当在 GridPanel 中选择记录时,所有值都会填充到表单中。

在每个表单上,我们都有一个表行 ID(主键)的输入字段,其定义如下:

var editFormFields = [
{
     fieldLabel: 'ID',
     id: 'id_field',
     name: 'id',
     width: 100,
     readOnly: true, // the user cannot change the ID, ever.
     monitorValid: true
} /* other fields removed */
];

所以,这一切都很好。这适用于我们所有的应用程序。在构建新界面时,要求我们需要使用第三方文件存储 API,该 API 以加载在 IFrame 中的小网页形式提供界面。

我将 IFrame 代码放在了 FormPanel 的 html 参数中:

var editForm = new Ext.form.FormPanel({
   html: '<div style="width:400px;"><iframe id="upload_iframe" src="no_upload.html" width="98%" height="300"></iframe></div>',
   /* bunch of other parameters stripped for brevity */
});

所以,每当用户选择一条记录时,我需要将 IFrame 的 src 属性更改为我们正在使用的服务的 API URL。类似于http://uploadsite.com/upload?appname=whatever&id={$id_of_record_selected}

我最初进入 id 字段(粘贴在上面)并添加了一个更改侦听器。

var editFormFields = [
{
     fieldLabel: 'ID',
     id: 'id_field',
     name: 'id',
     width: 100,
     readOnly: true, // the user cannot change the ID, ever.
     monitorValid: true,
     listeners: {
        change: function(f,new_val) {
           alert(new_val);
        }
     }
} /* other fields removed */
];

很好很简单,只是它只在用户专注于该表单元素时才有效。其余时间它根本没有开火。

对我已经过了最后期限而只需要它工作感到沮丧,我迅速实施了一个衰减轮询器来检查值。这是一个可怕的,丑陋的黑客。但它按预期工作。

我会在这个问题的答案中粘贴我丑陋的肮脏技巧。

【问题讨论】:

  • 这是什么 ExtJS 版本?
  • 2009 年发布的任何版本。:) 很久以前了。

标签: javascript extjs


【解决方案1】:

"GridPanel 列绑定到 FormPanel 表单元素,以便 当一条记录被选中时 GridPanel,所有的值都是 填写在表单中。”

正如我从上面的引用中了解到的那样,rowclick 事件实际上是首先触发表单更改的原因。为避免轮询,这可能是监听的地方,并最终引发您的自定义更改事件。

【讨论】:

  • 谢谢。这完美地工作。现在,如果我能自己在 ext 文档中找到它就好了。 :D 我将你标记为正确答案。
  • 仅供参考,通常最好绑定到 SelectionModel 的更改事件而不是网格的单击事件。这样键盘导航将触发相同的处理程序。
  • 很高兴你得到了答案,埃里克!
【解决方案2】:

这是我为解决这个问题所做的丑陋的黑客攻击:

var current_id_value = '';
var check_changes = function(offset) {
   offset = offset || 100;
   var id_value = document.getElementById('id_field').value || '';
   if ( id_value && ( id_value != current_id_value ) ) {
      current_id_value = id_value;
      change_iframe(id_value);
   } else {
      offset = offset + 50;
      if ( offset > 2500 ) {
         offset = 2500;
      } 
      setTimeout(function() { check_changes(offset); }, offset);
   }
};

var change_iframe = function(id_value) {
   if ( id_value ) {
      document.getElementById('upload_iframe').src = 'http://api/upload.php?id=' + id_value;
   } else {
      document.getElementById('upload_iframe').src = 'no_upload.html';
   }   

   setTimeout(function() { check_changes(100); }, 1500);
};

它不漂亮,但它有效。所有的老板都很高兴。

【讨论】:

    【解决方案3】:

    如果您花一点时间阅读源代码,您会看到 Ext.form.Field 类仅在 onBlur 函数中触发该更改事件

    【讨论】:

    • 我阅读了源代码。我知道它为什么这样做。我只是原则上不同意这一点。我不应该求助于丑陋的黑客来获取表单字段中已更改的值。作为一个严肃的现代框架,应该有一些机制让我能够捕获这些信息。我担心(以及最初发布推文的原因)是我所做的 99% 的工作都能很快完成,为此我很感激。剩下的 1% 花费(平均(作为一个疯狂的猜测))我的时间多 1000%。
    • 您可以使用 keyup/keydown/keypress(将 enableKeyEvents 设置为 true)侦听器然后检查值吗?可能比轮询选项略好。
    • 这是个好主意,但不能保证用户会按下某个键。我们可能能够保证用户至少会触发一个点击事件,尽管我必须在正文级别捕获它以获取每个可能的点击场景。这是一个好主意(鼠标事件),我可能会尝试。好主意。
    • 这样做是因为 W3C 是这样定义更改事件的。 “当控件失去输入焦点并且在获得焦点后其值已被修改时,会发生更改事件。此事件对 INPUT、SELECT 和 TEXTAREA 元素有效。” (w3.org/TR/DOM-Level-2-Events/events.html)
    • 另外,关于“多出 1000% 的时间”的评论——难道没有任何框架可以这么说吗?您是否曾经使用过能够 100% 解决编码问题的框架?如果是这样,请给我它的链接;)