Form Framework

本文介绍了Demandware平台提供的表单框架,该框架简化了表单显示和处理流程。通过XML元数据文件定义表单字段及验证规则,并利用ISML模板展示给用户。文章详细阐述了如何创建和使用这些组件,以及表单管道元素的工作原理。

 

Forms Framework

 

The Demandware platform provides a set of tools that help simplify form display and processing. Using the Demandware Forms framework, you can control how consumer-entered values are validated by the application, rendered on a browser and possibly stored on a server.

 

You need the following four files:

  1. An xml form to define and store the metadata

  2. A pipeline that will validate and process the form

  3. A properties file that contains externalized form labels and possible error messages

  4. An ISML template that will display the form to the user 

 There are 3 objects that interact:

  1. XML metadata file: located in the cartridge/forms/default directory. It describes the fields, labels, validation rules and actions that apply when the field is used in an ISML template.

  2. ISML template: it uses the form metadata fields and actions to show an HTML form to the user.

  3. Object (optional): this object represents a single system or custom object in the pdict, and it can be used to pre-fill the metadata file as well as to store submitted form data to the database.

     

     

XML Metadata File

As a developer, you will need to identify which fields a user will need to enter, and what actions can be taken when implementing a form. They will need to be created in an xml form that will set the form field parameters and hold the data for the form.

 

The form metadata file uses the following XML elements: 

Element

Description

form

Required: top level tag that contains all other elements inside <form>…</form>

field

Required: Defines data field with many attributes

options

Use as a child element inside a field to pre-fill multiple options like months, days, etc

option

Use as a child element inside an options element to specify a single option

action

Required: Defines a possible action the user might take on the form

include

Allows inclusion of one form metadata definition into another.

list

Allows inclusion of several items (i.e. collection of addresses) as a single field

group

Allows grouping of elements to be invalidated together

 

The field element may use the following attributes:

Attributes

Description

formid

Required: unique ID to identify the field for ISML templates and pipelines.

type

Required: data type for field

label

Usually a key to an externalized string in the forms.properties resource bundle.

description

Description for field, might be used in tooltips.

min-length, max-length

Restricts the field length for data entry.

min, max 

Valid range for integer, number and dates.

range-error

长度错误

Message shown if value provided does not fall within the specified range.

regexp

正则

Regular expression for string fields: email, phone, zipcode, etc.

parse-error

正则错误

Message shown when the data entered does not match the regex. Usually a key to an externalized string.

mandatory

经过后端验证

Field is required via server side validation when true.

missing-error

主键验证错误

Message shown if the primary key validation error is generated in a pipeline.

value-error

主键验证错误解释

解释 if a primary key validation error is generated in a pipeline.

binding

Used to match field to a persistent object attribute.

masked

Specify # of characters to mask.

format

Format for display of dates, numbers, etc.

whitespace

Specify whitespace handling (none or remove).

timezoned

Optional flag for date objects (true or false).

default-value 预先定义值

Pre-defines a value for a field.

checked-value 

Value when field is checked in a form.

unchecked-value 

Value when field is unchecked in form.

 

Here is an example of a simple form metadata file:

<?xml version="1.0"?>

<form>

<field formid="fname"

label="forms.contactus.firstname.label"

type="string" mandatory="true"

binding="custom.firstName"

max-length="50"/>

<field formid="lname"

label="forms.contactus.lastname.label"

type="string" mandatory="true"

binding="custom.lastName"

max-length="50"/>

<field formid="email"

label="forms.contactus.email.label"

type="string"

         mandatory="true"

         regexp="^[\w-\.]{1,}\@([\da-zA-Z-]{1,}\.){1,}[\da-zA-Z-]{2,6}$"

parse-error="forms.contactus.email.parse-error"

         value-error="forms.contactus.email.value-error"

         binding="custom.email"

         max-length="50"/>

<action formid="subscribe" valid-form="true"/>

</form>

 

In the example above, the fields fname, lname and email store the information needed to send a newsletter to a non-registered user. The fields are:

  • Mandatory 后端验证

  • Contain label keys that point to 翻译文件位置

    the cartridge/templates/resources/forms.properties file

The email field has an extra requirement: it uses a regular expression to define what an acceptable email can be. Additionally, it specifies a parse-error key which matches an error message in the forms.properties file.

 

Finally, the action subscribe identifies the possible actions that a user may take on the form. The attribute valid-form="true" means that this form requires validation: 3 required fields plus a valid email format for the last one will be enforced on the server side.

 

IMPORTANT NOTE

Although it is not a requirement, it is a best practice to use lower-case letters when naming your xml forms(小写字母命名). Pipelines are also xml files and use camel-case naming in SiteGenesis.

 

ISML Form Template

You define an ISML template with the same tags needed for a valid HTML form: <form>…</form>

You can choose to implement your own form action by specifying a pipeline URL, but that would circumvent(绕过) the Forms Framework. When using the framework you specify an Interaction Continue Node for the form action to post to, as follows:

 

<form action="${URLUtils.continueURL()}" method="post"

name="SendToFriendForm" id="SendToFriendForm">

 

The method dw.web.URLUtils.continueURL() ensures that the form gets submitted back to the Interaction Continue Node that displayed the form template.

 

When creating input fields, you must use the  objectpdict.CurrentForms.<form metadata file>.<formid> to reference the specific formid in the form metadata. SiteGenesis has an <isinputfield> custom tag which facilitates(促进) the creation of form fields. For example, to show the fname field from the

newsletter.xml file as a text field in an ISML template, you use:

 

<isinputfield formfield="${pdict.CurrentForms.newsletter.fname}" type="input">

 

The custom tag will use the fname formid from the metadata file and build an HTML label using the forms.properties file to pull the text for the

 

forms.contactus.firstname.label key. It also creates an HTML input field to the right of the label with the necessary client-side JavaScript to enforce required fields:

 

You can modify the behavior of the <isinputfield> tag since it is a custom tag implemented in the SiteGenesis cartridge.

 

The final requirement in the ISML template is to implement the button that matches the action in the form metadata. For this we create a standard HTML button with a name attribute that points to a specific action in the form metadata:

 

<input type="submit"  value="${Resource.msg('global.submit','locale',null)}"

     name="${pdict.CurrentForms.newsletter.subscribe.htmlName}"/>

 

Here the pdict.CurrentForms.newsletter.subscribe.htmlName refers to the htmlName property of the action subscribe in the form metadata. In the debugger you can view the value of this property at runtime: dwfrm_newsletter_subscribe. This value identifies a specific action for a specific form, which is necessary when the pipeline ICN determines which form action to process.

 

Form Pipeline Elements

A pipeline that uses the Demandware forms framework has a very distinctive pattern(独特模式) because it uses the following elements:

  • ClearFormElement pipelet to clear an existing form object in the pdict using a specified form metadata file.

  • InvalidateFormElement invalidates the specified FormElement.

  • ICN to show the ISML form, and to perform server-side validation

  • Transitions that match actions from the form metadata

  • A “next” transition that goes back to the ICN to handle validation errors.

 To create a form using the form framework, follow these steps:

  1. Create an xml metadata file that will hold your form data

  2. Create an ISML template that will display a form to a visitor.

  3. Create a pipeline that includes at minimum:

  1. Start node

  2. ClearFormElement pipelet that clears the form object next time you run the pipeline again.

  3. Interaction Continue node that links to the ISML template you created in step 2.

  4. Transition nodes to handle the following scenarios:

<think>我们正在使用GameFramework框架进行游戏开发,需要获取DialogForm的实例或实现相关功能。 根据引用[1]和引用[2],我们可以了解到: 1. UIFormInfo是用于管理UI表单信息的类,通过Create方法创建,其中包含了一个IUIForm实例。 2. UI模块通过AddUIForm方法将UI表单信息添加到链表中管理。 3. 在打开UI表单时,我们可以通过事件回调(OpenUIFormSuccessEventArgs)来获取UI表单实例。 因此,要获取DialogForm实例,我们可以考虑以下几种方法: 方法一:通过打开UI表单成功事件回调获取 在打开对话框表单(DialogForm)时,我们可以订阅打开成功事件,然后在事件回调中获取到DialogForm实例。 方法二:通过框架的UI组件获取已打开的UI表单 GameFramework的UI组件可能提供了获取已打开UI表单的方法。我们可以查找UI组件是否有类似GetUIForm的方法。 方法三:在DialogForm打开时,将其引用保存在某个地方(例如单例或管理器)以便后续获取。 由于引用[2]中提到,为了避免重复获取组件,新增了一个Component脚本来统一管理组件,我们可以参考这个思路,创建一个管理器来存储DialogForm的引用。 具体步骤: 方法一示例(事件回调): 1. 在打开对话框的表单时,传递一个回调函数。 2. 在回调函数中,将获取到的UI表单实例保存起来。 示例代码: ```csharp // 打开对话框表单 GameEntry.UI.OpenUIForm(UIFormId.DialogForm, userData, this); // 在打开UI表单成功事件的回调中处理 private void OnOpenUIFormSuccess(object sender, GameFramework.UI.OpenUIFormSuccessEventArgs e) { if (e.UIForm.SerialId == 这里可以是你打开时返回的序列id或者通过表单类型判断) { // 假设我们的DialogForm实现了IDialogForm接口(继承自IUIForm) IDialogForm dialogForm = e.UIForm.Logic as IDialogForm; if (dialogForm != null) { // 将dialogForm保存起来,比如保存在一个全局变量或者当前场景的管理器中 m_DialogForm = dialogForm; } } } ``` 方法二:使用UI组件的方法获取 查看GameFramework.UI.IUIModule的接口,看是否有类似GetUIForm的方法。例如: ```csharp IUIForm uiForm = GameEntry.UI.GetUIForm(UIFormId.DialogForm); if (uiForm != null) { IDialogForm dialogForm = uiForm.Logic as IDialogForm; // 使用dialogForm } ``` 方法三:在DialogForm的脚本中,在打开时将自己注册到某个管理器 例如,在DialogForm的OnOpen方法中: ```csharp public class DialogForm : UGuiForm, IDialogForm { protected override void OnOpen(object userData) { base.OnOpen(userData); // 注册到管理器 GameEntry.UI.SetDialogForm(this); } protected override void OnClose(bool isShutdown, object userData) { // 从管理器移除 GameEntry.UI.SetDialogForm(null); base.OnClose(isShutdown, userData); } } ``` 然后在UI组件中提供一个设置和获取DialogForm的方法(注意:如果同时存在多个对话框,可能需要用列表管理,但通常对话框是唯一的)。 根据引用[1]中的代码,UIFormInfo是被添加到链表(m_UIFormInfos)中的,所以UI组件内部应该已经维护了所有打开的UI表单。因此,方法二可能是框架已经支持的方式。 建议查阅GameFramework的文档或源码,确认IUIModule是否提供了获取已打开UI表单的方法。通常,框架会提供类似GetUIForm(int serialId)或GetUIForm(string uiFormAssetName)的方法。 总结: 推荐优先使用方法二,通过UI组件的GetUIForm方法来获取。如果框架没有提供这样的方法,则考虑使用方法一(事件回调)或方法三(自行注册)。 注意:在获取到DialogForm实例后,应将其转换为具体的接口(如IDialogForm)来调用其方法,而不是直接使用具体类型,以降低耦合。 如果上述方法都不能满足,可能需要扩展UI组件,添加获取指定类型UI表单的功能。 相关问题: 1. 在GameFramework中如何管理多个同时打开的对话框? 2. 如何安全地关闭对话框并释放资源? 3. 在GameFramework中,UI表单的生命周期是怎样的? 4. 如何扩展GameFramework的UI模块以支持更复杂的UI需求?</think>在GameFramework框架中获取`DialogForm`实例或实现相关功能,可以通过以下方法实现: ### 方法一:通过打开UI表单的回调获取(推荐) 1. **订阅打开成功事件** 在打开对话框时订阅`OpenUIFormSuccess`事件,在回调中获取表单实例: ```csharp // 打开对话框时传递回调 GameEntry.UI.OpenUIForm("DialogForm", userData, new OpenUIFormParams { OnSuccess = OnDialogOpened }); // 回调函数获取实例 private void OnDialogOpened(IUIForm uiForm) { DialogForm dialog = uiForm.Logic as DialogForm; if (dialog != null) { // 操作dialog实例 dialog.SetTitle("提示"); } } ``` 此方法符合引用[2]的事件回调机制[^2]。 2. **使用事件参数直接获取** 在已订阅的全局事件中通过事件参数获取: ```csharp GameEntry.UI.OpenUIFormSuccess += (sender, e) => { if (e.UIForm.UIFormAssetName == "DialogForm") { DialogForm dialog = e.UIForm.Logic as DialogForm; } }; ``` ### 方法二:通过UI组件获取已打开的表单 ```csharp // 获取所有已打开的表单 IUIForm[] allForms = GameEntry.UI.GetAllLoadedUIForms(); // 查找目标对话框 foreach (var form in allForms) { if (form.UIFormAssetName == "DialogForm") { DialogForm dialog = form.Logic as DialogForm; break; } } ``` > 注意:需确保对话框已打开,否则返回`null` ### 方法三:扩展UI组件(高级用法) 1. **创建静态访问器** 参考引用[2]的组件统一管理思路[^2]: ```csharp // 在GameEntry扩展静态属性 public static class GameEntryExtensions { public static DialogForm CurrentDialog { get; internal set; } } // 在DialogForm中设置引用 public class DialogForm : UGuiForm { protected override void OnOpen(object userData) { base.OnOpen(userData); GameEntryExtensions.CurrentDialog = this; } protected override void OnClose(bool isShutdown, object userData) { GameEntryExtensions.CurrentDialog = null; } } ``` 使用时直接调用:`GameEntryExtensions.CurrentDialog.ShowMessage(...)` ### 最佳实践建议 1. **生命周期管理** 始终在`OnOpen/OnClose`中管理引用,避免内存泄漏 2. **接口解耦** 定义`IDialogForm`接口而非直接依赖具体类: ```csharp public interface IDialogForm { void SetMessage(string message); } ``` 3. **异常处理** 添加空值检查: ```csharp var dialog = GameEntry.UI.GetUIForm("DialogForm")?.Logic as IDialogForm; if (dialog != null) { ... } ``` ### 注意事项 - 避免在非主线程访问UI组件 - 多对话框场景建议使用字典管理实例 - 关闭对话框时务必释放引用(在`OnClose`中置空) --- ### 相关问题 1. 如何在GameFramework中实现对话框的队列管理? 2. 如何扩展UI组件支持跨场景对话框? 3. GameFramework中UI表单的生命周期事件有哪些? 4. 如何优化GameFramework的UI内存占用? [^1]: UIFormInfo的创建和添加机制,详见框架源码 [^2]: 事件回调与组件统一管理方案,参考技术文档
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值