表单事件
表单创建完成且 HTML 表单模板添加到 DOM 后, 应用程序会在表单的生命周期内触发以下表单事件:
on_view_form_created- 当表单已创建但尚未显示时触发此事件on_view_form_shown- 当表单已显示时触发此事件on_view_form_close_query- 当尝试关闭表单时触发此事件on_view_form_closed- 当表单已关闭时触发此事件on_view_form_keydown- 当表单发生 keydown 事件时触发此事件on_view_form_keyup- 当表单发生 keyup 事件时触发此事件
对于其他表单类型 —— edit(编辑)、filter(过滤)和 param(参数),
将 “view” 替换为表单类型,例如,编辑表单的 on_edit_form_created。
我们将首先解释如何使用 on_view_form_created 事件。
当用户点击菜单项时,应用程序执行 相应任务树实体项的 view 方法,该方法使用其 HTML 表单模板创建表单,并首先触发任务的 on_view_form_created 事件。
当您创建一个新项目时,任务客户端模块已包含一些代码,其中包括 on_view_form_created 事件处理程序。每次创建查看表单时都会执行此事件处理程序,并定义查看表单的默认行为。
您可以打开任务客户端模块查看此事件处理程序。 如果您需要更改项目中所有查看表单的默认行为,应在此处进行更改。
下面我们描述其主要执行步骤:
初始化 view_form 和 table_options , 这些属性在创建查看表单和表格时被某些方法使用。
根据用户权限,将默认按钮的 JQuery 事件处理程序分配给实体项的方法。 在下面的示例中,初始化了删除按钮:
if (item.can_delete()) { item.view_form.find("#delete-btn").on('click.task', function(e) { e.preventDefault(); item.delete_record(); }); } else { item.view_form.find("#delete-btn").prop("disabled", true); }
执行实体组的 on_view_form_created 事件处理程序,以及实体项的 on_view_form_created 事件处理程序(如果已定义):
if (!item.master && item.owner.on_view_form_created) { item.owner.on_view_form_created(item); } if (item.on_view_form_created) { item.on_view_form_created(item); }
创建一个表格来显示实体项数据,并为明细表创建表格(如果已通过调用
create_view_tables方法指定)执行 open 方法,从服务器获取实体项数据集。
最后返回 true,以防止调用所有者组和实体项的
on_view_form_created事件处理程序,因为它们已被调用(参见下面的_process_event方法)。
在我们初始化按钮之后、创建表格之前,我们调用了实体项本身的 on_view_form_created 事件处理程序。
例如,在演示应用程序的 tracks(曲目)实体项的客户端模块中,定义了以下 on_view_form_created 事件处理程序。 在其中,我们更改了 table_options 的 “高度(height)” 属性,创建了 “发票表格(invoice_table)” 的副本,设置其属性, 并调用其 create_table 方法来创建一个表格以显示其数据。
function on_view_form_created(item) {
item.table_options.height -= 200;
item.invoice_table = task.invoice_table.copy();
item.invoice_table.paginate = false;
item.invoice_table.create_table(item.view_form.find('.view-detail'), {
height: 200,
summary_fields: ['date', 'total'],
});
item.alert('Double-click the record in the bottom table to see track sales.');
}
该模块还有一个 on_after_scroll 事件处理程序,当用户移动到另一首曲目时将执行此处理程序,并获取该曲目的销售数据。
这个示例解释了表单事件的使用原理。
事件的触发顺序取决于事件的类型。事件生成的顺序也取决于事件的类型。
关闭查询事件
当用户尝试关闭表单时,首先会为实体项触发 on_close_query 事件(如果已定义)。
如果事件处理程序返回 true,应用程序将关闭窗体;如果事件处理程序返回 false,应用程序将保持窗体打开;否则,将以相同方式触发实体组的 on_close_query 事件(若已定义),再触发任务的 on_close_query 事件(若已定义)。
例如,默认情况下,任务客户端模块中有一个 on_edit_form_close_query 事件处理程序:
function on_edit_form_close_query(item) {
var result = true;
if (!item.virtual_table && item.is_changing()) {
if (item.is_modified()) {
item.yes_no_cancel(task.language.save_changes,
function() {
item.apply_record();
},
function() {
item.cancel_edit();
}
);
result = false;
}
else {
item.cancel_edit();
}
}
return result;
}
此代码检查记录是否已被修改,然后打开 “是/否/取消” 对话框。
如果我们想在不使用此对话框的情况下关闭表单,可以在实体项的客户端模块中定义以下 事件处理程序:
function on_edit_form_close_query(item) {
item.cancel()
return true;
}
Keydown、Keyup 事件
这些事件的触发方式与关闭查询事件相同,均从实体项开始;但如果事件处理程序返回 true,则不会执行组和任务的事件处理程序。
例如,默认情况下,任务客户端模块中有一个 on_edit_form_keyup 事件处理程序:
function on_edit_form_keyup(item, event) {
if (event.keyCode === 13 && event.ctrlKey === true){
item.edit_form.find("#ok-btn").focus();
item.apply_record();
}
}
此代码在用户按下 Ctrl+Enter 时将记录的更改保存到数据库表。
假设我们希望在用户按下 Enter 时保存更改。那么我们在实体项客户端模块中编写以下事件处理程序:
function on_edit_form_keyup(item, event) {
if (event.keyCode === 13){
item.edit_form.find("#ok-btn").focus();
item.apply_record();
return true;
}
}
在这种情况下,当用户按下 Enter 时,任务的事件处理程序将不会被调用。
所有其他事件
对于其他事件,首先调用任务的事件处理程序;如果任务的事件处理程序没有返回 true,则执行组的事件处理程序;如果组的事件处理程序没有返回 true,再调用实体项的事件处理程序。
此机制由 jam.js 模块中 Item 类的 _process_event 方法实现。
_process_event: function(form_type, event_type, e) {
var event = 'on_' + form_type + '_form_' + event_type,
can_close;
if (event_type === 'close_query') {
if (this[event]) {
can_close = this[event].call(this, this);
}
if (!this.master && can_close === undefined && this.owner[event]) {
can_close = this.owner[event].call(this, this);
}
if (can_close === undefined && this.task[event]) {
can_close = this.task[event].call(this, this);
}
return can_close;
}
else if (event_type === 'keyup' || event_type === 'keydown') {
if (this[event]) {
if (this[event].call(this, this, e)) return;
}
if (!this.master && this.owner[event]) {
if (this.owner[event].call(this, this, e)) return;
}
if (this.task[event]) {
if (this.task[event].call(this, this, e)) return;
}
}
else {
if (this.task[event]) {
if (this.task[event].call(this, this)) return;
}
if (!this.master && this.owner[event]) {
if (this.owner[event].call(this, this)) return;
}
if (this[event]) {
if (this[event].call(this, this)) return;
}
}
}