支持自定义表单中的 JavaScript{ #support-for-javascript-in-custom-forms }¶
重要 从数据录入应用程序的
102.0.0版本开始,恢复了对自定义表单中 JavaScript 的支持。要升级到此版本,请转到应用程序管理,搜索 "数据录入 "应用程序,然后升级到版本 "102.0.0 "或更高版本。这是 DHIS2 v43 的默认版本,但也适用于 DHIS2 的早期版本。
应用程序管理应用程序中的稳定版本](./data-entry-app-management.png)
介绍{ #intro }¶
作为新数据录入应用程序的一部分,我们放弃了对自定义表单中自定义逻辑和 JavaScript 的支持。这给多年来严重依赖自定义表单的实施带来了挑战。
为什么我们放弃了对自定义 JavaScript 的支持?¶
一般来说,自定义表单,特别是任意 JavaScript 所面临的挑战是多方面的:
-
安全问题:这些表单在用户的上下文中运行(使用其权限和访问模型),因此任何事情都有可能发生。虽然围绕此类表单作者的良好管理模式可以降低表单中运行恶意 JS 的部分风险,但无意中的安全漏洞仍有可能发生,例如在表单中硬编码秘密、调用其他服务器的 API、跨站脚本等。
-
更多安全问题(传统模式和库):表单通常依赖于陈旧过时的库(例如 jQuery 3.2.1 及其相关插件)。这不仅效率低下,因为此类库的大多数用例都已过时,现代浏览器也不支持它们,而且还可能带来安全风险,因为表单所依赖的过时库已多年未进行过积极开发。
-
与网络耦合:虽然本机(部分和默认)表单可以在移动设备上呈现,但自定义表单在移动设备上根本无法使用。实际上,传统的自定义表单(具有极大的灵活性)不可能有一天在移动设备上得到支持。
-
用户体验问题:自定义表单的设计和用户体验已经过时,不符合现代风格或模式。与其他本地表单和 DHIS2 生态系统中的其他应用程序相比,自定义表单给人一种过时的感觉。这不仅限于外观和感觉,还包括实际方面,如性能较差、这些表单通常会执行无关的 API 调用,以及由于使用传统过时的插件而大多无法访问。
这些挑战导致核心团队最初放弃了对 JavaScript 自定义表单的支持,但这对许多实施方案的升级造成了障碍。
为什么现在要恢复对自定义 JavaScript 的支持?¶
从数据录入应用程序 102.0.0 版开始,我们通过传统自定义表单插件恢复了对使用 JavaScript 的自定义表单的支持。
插件架构对最终用户是透明的。从技术角度看,该架构为开发人员(目前是核心团队,但在不久的将来会有外部开发人员)提供了一个通用点,可在不影响主应用程序安全性和用户体验的情况下扩展数据录入应用程序的功能。
就自定义表单而言,这在一定程度上隔离和控制了表单中自定义 JS 的固有风险,但并不能完全缓解这些风险。从最终用户和自定义表单作者的角度来看,该插件提供了一种实用的变通办法,既能升级到较新版本的 DHIS2,又能以最少的摩擦保持自定义表单的正常运行。
旧版自定义表单插件提供了一个 "垫片 "或适配器,可使旧版自定义表单在现代应用程序的上下文中发挥与旧版 Struts 传统应用程序类似的功能。这就改进了新应用程序外壳的功能和集成(如上下文选择、数据元素详情、离线支持等)。例如,访问元数据或数据元素的详细信息、填写表格等操作以及离线支持都是通过主应用程序的外壳实现的。
但我们的建议仍然是尽可能放弃自定义表单。我们意识到这并不总是可以实现的,但这也变得越来越可能,因为人们需要自定义表单的许多用例现在都已得到本地支持,这包括更好地支持从右向左的语言、显示标签(水平和垂直)的功能、透视表单的功能以及在表单的各部分之间显示自定义 HTML 的功能。我们的目标是在未来添加更多本地功能,使自定义表单成为不必要。
技术概览和表格作者须知{ #technical-overview-and-notes-for-forms-authors }¶
该插件旨在支持开箱即用的现有自定义表单。为此,该插件创建了一个 shim,尽可能完整地保留了旧的自定义表单 API。
该 API 是传统自定义表单需要遵守的契约,它由表单赖以构建其功能的功能和组件组成,例如
- dhis2.de` 命名空间:自定义表单依赖于 dhis2.de 命名空间 中的实用功能。我们尽量保留这些函数。它们的内部结构可能已经改变,但接口仍然存在。
该命名空间下的某些方法不用于自定义表单,而是用于旧 Struts 应用程序中的默认表单和部分表单。这些方法要么被移除,要么被标记为过时。
-
HTML 合同:大多数表单都是直接从 HTML 元素中获取上下文(组织单位、数据集、期限、属性选项)。为了实现无缝过渡,我们提供了这些 HTML 元素的病毒版本(对用户隐藏),这使得依赖于直接从 HTML 组件获取组织单位的代码(即使用
$('#selectedPeriodId').val())可以继续工作,就像该元素仍然存在一样。 -
元数据:新的数据录入应用程序处理元数据的加载和缓存方式与旧的 struts 应用程序不同,效率更高。对于传统的自定义表单,插件会在现代应用程序外壳加载时提供这些元数据。我们会尽力隐藏这一实现细节,因此诸如
dhis2.de.fetchDataSets()等传统助手会以对自定义表单透明的方式从元数据对象中获取数据集。
例如,所有这些全局属性仍然可用,尽管它们的检索方式与旧的 Struts 应用程序有本质区别:
- dhis2.de.currentOrganisationUnitId
dhis2.de.currentDataSetId.- dhis2.de.currentPeriodId
- dhis2.de.defaultCategoryCombo
dhis2.de.categories- dhis2.de.categoryCombos
- dhis2.de.dataElements
- dhis2.de.optionSets
dhis2.de.indicatorFormulas指示器公式- dhis2.de.dataSets
我们还尝试给其中一些对象打上补丁,使它们看起来与旧版应用程序相似,例如,数据集需要一个 periodId 字段,而在新版应用程序中,该字段显示为 period。在这种情况下,我们会给对象打上补丁,使两个属性都可用。
注意 虽然我们已尽量考虑到其中一些对象的差异,但您的表单所依赖的属性有可能已经不存在了。在这种情况下,您可以相应地更新表单,或者提出一个问题,如果您认为这是一种常见的使用情况,垫片应该处理。
- API 基本 URL:自定义表单有多种方法来处理 API 请求和决定 DHIS2 实例的基础 URL。我们整合了这些方法,这样 shim 就会根据实例配置的定义,将您的请求附加到正确的 DHIS2 BASE_URL 上。因此,如果您要调用
/me,您只需将其保留为/me,插件就会附加正确的基础 URL,例如调用https://play.dhis2.org/42/me(如果您的调用已正确指定了基础 URL,那么附加基础 URL 将被忽略)。
重要** 只有在使用 jQuery AJAX 方法(即
$.get或jQuery.post)的情况下,这种识别和附加基本 URL 的能力才会起作用。我们看到的大多数表单似乎都是这种情况。如果您以其他方式进行请求(例如使用fetch方法),那么您有责任正确构建 URL。我们在表单的全局窗口上下文中提供了一个属性,以简化操作:
window.DHIS2_BASE_URL属性
- dhis2.shim` 命名空间:这是插件为内部使用而公开的新命名空间。⚠️ 不应将其视为稳定的 API ⚠️。表单不应直接调用该命名空间下的可用方法,因为它可能会发生变化。
该命名空间对于将现代应用程序外壳中的某些功能公开给自定义表单是必要的,例如,在外壳中以现代风格显示警报。考虑到自定义表单中 JavaScript 的工作方式,有必要将这些帮助程序放在全局对象下,但表单作者不应直接依赖这些帮助程序,因为它们不能保证将来仍然存在。
- 翻译和国际化(i18n):对国际化的支持非常有限。插件会在构建时传递一些现有的翻译字符串(类似于 Struts 应用程序的做法),但如果自定义表单希望支持本地化和不同语言,则需要推出自己的解决方案(旧版应用程序也是如此)。
可能无法开箱即用的东西{ #things-that-might-not-work-out-of-the-box }¶
提示 需要注意的是,对于无法正常工作的表单,您现在可以更改自定义表单使其正常工作(例如,使其不再依赖于已不存在的方法)。该插件的主要目的是尽量减少您需要对表单进行的更改。
依靠内部方法{ #relying-on-internal-methods }¶
由于传统应用程序的构建方式,Struts 应用程序中的任何方法或库都是全局可用的(在浏览器的 window 对象中),因此可以通过自定义表单进行访问。在实践中,我们还没有见过直接依赖于这些方法的自定义表单,但这种可能性是存在的。对于此类表单,作者应更新它们,使其不再依赖这些内部方法。
已打补丁的内部方法{ #patched-internal-methods }¶
与此相关的一种可能更常见的模式是,自定义表单会覆盖这些内部方法,以此来修复传统应用程序中的错误。这些修补程序将无法正常工作,但由于插件依赖于现代应用程序外壳中的方法,因此可能不再需要这些修补程序。
已废弃的 dhis2.de 方法{ #deprecated-dhis2de-methods }¶
在 dhis2.de 下有一些方法已被弃用。这些方法主要是 Struts 用于 Default 和 Section 表单的操作,因此与自定义表单无关。我们还没有看到使用这些方法的表单,但从理论上讲,由于在旧应用程序中一切都是全局的、可访问的,自定义表单可以使用这些方法。
其中一些方法是dhis2.de.loadDataSetAssociations, dhis2.de.setMetaDataLoaded, dhis2.de.discardLocalDat, dhis2.de.uploadLocalData, dhis2.dhis2.de.resetSectionFilters、dhis2.de.clearSectionFilters、dhis2.de.clearPeriod、dhis2.de.clearEntryForm、dhis2.de.getOrFetchDataSetList, dhis2.de.setAttributesMarkup, dhis2.de.getAttributesMarkup, dhis2.de.clearAttributes, dhis2.de.attributeSelected, dhis2.de.inputSelected, dhis2.de.loadOptionSets, dhis2.de.enableDEDescriptionEvent, dhis2.de.lockForm.
这些控件会在控制台中发出已被弃用的警告。请更新您的表单,不要调用或依赖这些表单。
造型{ #styling }¶
我们试图在插件中复制旧版应用程序的大部分样式,但表单并不总是能保证看起来完全一样。在旧版应用程序中,样式来自不同的地方,很难将其全部复制:CSS 样式表、Struts 的内联样式、旧版 DHIS2 应用程序本身的基本样式、jQuery 插件,当然还有植入式(可通过 API 全局应用)的自定义样式。我们尽了最大努力尽可能多地复制这些样式,但不可能一直保证完全相同的外观(开箱即用)。
如果样式问题会导致功能问题,请提出问题,我们会看看是否可以更新模板。否则,您可以随时更新表单样式,使其按照您的期望运行。
或者,如果您的表单不包含任何 JS(只有自定义样式),可以考虑通过在表单中添加 <!-- NO_MODERN_HTML_ONLY_RENDERING --> 来启用现代渲染(更多信息请查看常见问题)。
已废弃的 dhis2.de 属性{ #deprecated-dhis2de-properties }¶
还有一些属性已不存在。鉴于 JavaScript 的动态特性,这些属性较难跟踪和记录,但如果您的自定义表单尝试访问dhis2.de.cst.some_object.some_property属性,却得到了该对象未定义的错误信息,这就意味着它访问的对象已不存在。
自定义表单不太可能出现这种情况,只需对表单进行简单更新即可正常工作。
其他废弃库{ #other-deprecated-libraries }¶
这些对象曾存在于全局 window 上下文中,但现在已被移除。自定义表单不应该直接使用这些对象,但以前是可以的,因为它们可以在全局范围内调用甚至覆盖。
-
DAO.store":在内部用于离线功能。现在由现代应用程序外壳处理。
-
jQuery "及其插件:我们捆绑了与上一个Struts应用程序("3.2.1")相同版本的jQuery,同时还捆绑了 "jQuery UI "和一些必要的插件,以简化过渡。这些插件包括:jQuery
select2,floatThead和 jQuerycalendar。 -
我们删除了
underscore、jquery.autogrow、jquery.cookie、jquery.blockUi、dhisAjaxSelect以及 Struts 应用程序中的其他实用程序(用于管理存储、翻译等)。 -
删除了用于管理组织单位逻辑的
ouwt和dhis2.array等 DHIS2 实用程序。在现代浏览器中,它们要么已经过时,要么没有必要。我们保留了dhis2.util命名空间,因为我们怀疑某些表单可能仍在使用其中的某些实用程序。
我们没有看到任何自定义表单直接使用这些库,它们的主要用途是供 Struts 应用程序内部使用。如果你的表单依赖于其中的一个库,那么你可以更新它。
支持和未来计划{ #support-and-future-plans }¶
传统自定义表单插件将继续保留,以确保现有的传统表单不间断地继续运行,但不会添加新的特性或功能。
未来,我们将重点加强插件模型的安全性,主要是限制 DHIS2 实例域外的 API 调用。对于自定义表单,我们可能会以选择加入的方式引入这些限制。
我们还在计划一个不同的现代插件入口点,它将允许人们创建外观更现代、默认更安全的自定义表单。 ...
常见问题{ #faqs }¶
- **使用插件对自定义表单是否有任何限制?
该插件的目的是尽可能支持现有的自定义表单,而无需对其进行修改,因此没有明确的限制。除非旧表单使用了 Struts 应用程序中可用的隐藏内部函数(无论如何,它们都不应该使用这些函数),否则它们应该可以正常工作。
如果有问题,您可以更新表单,在表单下运行 JavaScript 不会受到明确限制。可能会有一些 "隐性 "限制,例如 DHIS2 新版本中的 CSP Headers 可能会阻止内联 JavaScript,但只要对表单进行简单的更新,就足以使其正常运行。
- **有些自定义表单是使用现代应用程序而非插件加载的,为什么?
对于不包含 JavaScript 的自定义表单,即只包含自定义 HTML 和 CSS 的表单,我们默认使用现代应用程序进行渲染。
如果出于某种原因,您希望仍使用旧样式呈现表单,可以在自定义表单代码的任意位置添加指令 <!-- NO_MODERN_HTML_ONLY_RENDERING -->,在这种情况下,将使用传统自定义表单插件来呈现表单。
- X功能曾在旧版应用程序上使用过,但现在已失效
如果某项功能不能正常工作,作为表单作者,您有能力更新表单使其正常工作,因为在这些表单上运行的 JavaScript 没有任何限制。该插件的目的就是为了实现这些更新,但由于一切(包括使用内部方法)都是可能的,因此无法预测人们以前创建自定义表单的方式。如果有东西坏了,而你又认为这是一种常见的模式,插件应该默认支持,那么请联系我们,我们可以考虑更新插件。但如果这是您实现过程中的特定用例,那么您应该相应地更新表单。