精华内容
下载资源
问答
  • js激活和禁用input文本_软件开发网 [Ctrl+A 全选 注:如需引入外部Js需刷新才能执行]
  • 登记 注册的相关源代码(表单校验,自定义校验,邮箱激活等)
  • 表单脚本

    千次阅读 2016-04-03 12:26:14
    下述内存主要讲述了《JavaScript高级程序设计(第3版)》第14章关于“表单脚本”。 刚开始人们使用JavaScript,最主要的目的之一就是表单的验证,分担服务器处理表单的责任。虽然现流行的大部分提交方式是通过ajax,...

    下述内存主要讲述了《JavaScript高级程序设计(第3版)》第14章关于“表单脚本”。

    刚开始人们使用JavaScript,最主要的目的之一就是表单的验证,分担服务器处理表单的责任。虽然现流行的大部分提交方式是通过ajax,但了解表单,对于ajax方式也是有重大帮助的!所以,大家不要看轻表单。

    一、表单的基础知识

    在HTML中,表单由元素来表示,而在JavaScript中,表单对应的则是HTMLFormElement类型。
    表 HTMLFormElement的属性和方法

    属性或方法作用说明
    acceptCharset服务器能够处理的字符集;等价于HTML中的accept-charset特性
    action接收请求的URL;等价于HTML中的action特性
    elements表单中所有控件的集合(HTMLCollection)
    enctype请求的编码类型;等价于HTML中的enctype特性
    length表单中控件的数量
    method要发送的HTTP请求类型;等价于HTML的method特性
    name表单的名称;等价于HTML的name特性
    reset()将所有表单域重置为默认值
    submit()提交表单
    target用于发送请求和接收响应的窗口名称;等价于HTML的target特性

    1. 取得<form>元素

    方式1:通过getElementById(元素id)

    var form = document.getElementById("form1");

    方式2:通过document.forms获取页面所有表单,然后通过数值索引获取对应表单

    var firstForm = document.forms[0];

    方式3:通过document.forms获取页面所有表单,然后页面中form表单名称获取对应表单

    var myForm = document.forms["form2"];

    方式4:早期浏览器会把每个设置了name特性的表单作为属性保存在document对象中【建议不要使用此方式】

    var myFormf = document.form2;

    2. 提交表单

    (1)提交按钮提交
    方式1:通用提交按钮

    <input type="submit" value="Submit Form" />

    方式2:自定义提交按钮

    <button type="submit">Submit Form</button>

    方式3:图像按钮

    <input type="image" value="submitBtn.gif" />

    只要表单中存在上面列出的任何一种按钮,那么在相应表单控件拥有焦点的情况下,按回车键就可以提交表单。(textarea除外,在文本区中回车会换行)。如果表单没有提交按钮,安回车键不会提交表单。
    注意,通过上述方式提交表单,浏览器会在将请求发送给服务器之前触发submit事件。
    这样就可以决定是否需要验证表单。阻止这个事件的默认行为就可以取消表单提交。

    <form action="http://www.baidu.com">
        <input id="name"/>
        <button type="submit">Submit Form</button>
    </form>
    <script type="text/javascript">
        var form = document.forms[0];
        form.addEventListener("submit", function(event) {
            var name = document.getElementById("name");
            if(name.value === "") {
                event.preventDefault();
            }
        });
    </script>

    补充:想禁止通过回车提交表单,请参考【HTML防止input回车提交表单

    (2)JavaScript中提交

    var form = document.forms[0];
    form.submit();

    注意,这种方式不会触发sumbit事件。

    提交表单过程中有可能发生的最大问题就是,重复提交表单。
    解决方式:
    (1)第一次提交表单后就禁用提交按钮。
    要在“submit”事件处理函数中处理,不能在“click”事件处理函数中处理。因为有的浏览器会在click事件触发前,触发submit事件!
    (2)利用onsubmit事件处理程序取消后续的表单提交方式。

    我们项目中,请求通过ajax提交,防重复提交的方式大致类似于上述第(2)种。拦截ajax发送前、发送成功,发送完成过程,使用状态机标识当前处于哪种状态(loading、resubmit、success、error)。当用户请求ajax时,我们判断当前处于哪种状态:

    • 如果是初始状态null,则直接发送请求,将状态切换为loading;
    • 如果是loading或resubmit,提示“请求正在处理,不要重复请求”,将状态切换为resubmit;
    • 如果是success或error,提示“成功或失败”,然后变为状态恢复初始。

    3. 重置表单

    (1)重置按钮提交
    方式1:通用重置按钮

    <input type="reset" value="Reset Form" />

    方式2:自定义重置按钮

    <button type="reset">Reset Form</button>

    注意,通过上述方式重置表单,浏览器会触发reset事件。阻止这个事件的默认行为就可以取消重置提交。

    <form action="http://www.baidu.com">
        <input id="name"/>
        <button type="submit">Submit Form</button>
        <button type="reset"> Reset Form</button>
    </form>
    <script type="text/javascript">
        var form = document.forms[0];
        form.addEventListener("reset", function(event) {
            alert("我就不让你重置,咋地!");
            event.preventDefault();
        })
    </script>

    (2)JavaScript中重置

    var form = document.forms[0];
    form.reset();

    注意,这种方式不会触发reset事件。

    4. 表单字段

    • form.elements,获取表单中所有控件集合(HTMLCollection)。
    • form.elements[n]; // 返回第n+1个元素
    • form.elements[“name”]; // 返回name值为“name”的NodeList
    <form action="http://www.baidu.com">
    <input id="name" name="name"/>
    <input type="radio" name="color" value="red"/>Red
    <input type="radio" name="color" value="green"/>Green
    <input type="radio" name="color" value="blue"/>Blue
    <button type="submit">Submit Form</button>
    <button type="reset"> Reset Form</button>
    </form>
    <script type="text/javascript">
    var form = document.forms[0];
    form.elements[1] === form.elements["color"][0];// value值为red的input标签
    </script>

    (1)表单字段属性

    属性作用说明
    disabled布尔值,表示当前字段是否被禁用
    form指向当前字段所属表单的指针;只读
    name当前字段的名称
    readOnly布尔值,表示当前字段是否只读
    tabIndex表示当前字段的切换(tab)序号
    type当前字段的类型
    value当前字段被提交给服务器的值。对文件字段来说,这个属性是只读的,包含着文件在计算机的路径

    (2)表单字段方法

    • foucs()获取焦点,激活字段,使其可以响应键盘事件
    • blur()失去交单。
    window.addEventListener("load", function() {
       document.forms[0].elements[0].focus();   // 让表单第一个元素获取焦点
    });

    HTML5中表单字段新增了autofoucs属性。

    <input type="text" autofoucs />

    (3)表单字段事件

    • blur:当前字段失去焦点触发
    • change:对于和元素,在它们失去焦点且value值改变时触发;对于元素,在其选项改变时触发。
    • focus:当前字段获取焦点时触发

    表单错误提示流程:利用focus事件修改文本框的背景颜色,以便清楚表明这个字段获取了焦点;利用blur事件恢复文本框的背景颜色;利用change事件在用户输入了非规定字符时提示错误。
    在项目中的validate插件,只用到了blur和focus事件。因为某些浏览器中,blur事件会先于change事件;有些会恰好相反!

    二、文本框脚本

    HTML中,有两种方式表示文本框:单行文本框<input type="text">、多行文本框<textarea>
    (1)单行文本框
    通过设置size特性,可以指定文本框中能够显示的字符数;通过设置value特性,可以指定文本框的初始值;通过设置maxlength特性,可以指定文本框可以接受的最大字符数。

    <!-- 显示5个字符(input 元素的宽度),输入不能超过10个字符-->
    <input type="text" value="初始值放到这里" size="5" maxlength="10"/>

    (2)多行文本框
    rows设置文本框行数,cols设置文本框列数。

    <textarea cols="10" rows="5">初始值必须放在这里</textarea>

    上述两种文本框,都会将用户输入的内容保存在value属性中!!!

    1. 选择文本

    (1)选择(select)事件
    选择文本框中所有文本select()方法,对应的是一个select事件,同样存在触发时间的问题!

    var input = document.getElementById("name");
    input.addEventListener("focus", function(event) {
        event.target.select();
    });

    (2)取得选择的文本

    var textarea = document.getElementById("content");
    textarea.addEventListener("select", function(event) {
    if(typeof textarea.selectionStart === "number") {
    console.log(textarea.value.substring(textarea.selectionStart, textarea.selectionEnd));
    }else if(document.selection){
    // IE下
    console.log(document.selection.createRange().text);
    }
    });

    (3)选择部分文本
    setSelectionRange(要选择的第一个字符索引, 要选择的最后一个字符索引)
    注意要看到被选择的文本,必须在调用setSelectionRange()之前或之后立即将焦点设置到文本框。

    function selectText(textbox, startIndex, endIndex) {
        if(textbox.setSelectionRange) {
            textbox.setSelectionRange(startIndex, endIndex);
        } else if(textbox.createTextRange) {
            var range = textbox.createTextRange();
            range.collapse(true);
            range.moveStart("character", startIndex);
            range.moveEnd("character", endIndex - startIndex);
            range.select();
        }
        // 将焦点设置到文本框上
        textbox.focus();
    }

    部分选择文本的技术在实现高级文本输入框时很有用,例如提供自动完成建议的文本框就可以使用这种技术。

    2. 过滤输入

    (1)屏蔽字符
    当需要用于输入的文本中不能包含某些字符时,例如手机号,只能输入字符!

    var input = document.getElementById("name");
    input.addEventListener("keypress", function(event) {
        if(!/\d/.test(String.fromCharCode(event.charCode)) && event.charCode > 9 && !event.ctrlKey) {
            // 只允许输入数字和退格特殊键以及Ctrl
            event.preventDefault();
        }
    });

    更极端的方式,可以通过event.preventDefault();阻止其默认行为来禁止按键操作,即文本框只读!!
    (2)操作剪贴板

    var EventUtil = {
        getClipboardText: function(event){
            var clipboardData =  (event.clipboardData || window.clipboardData); // 兼容IE
            return clipboardData.getData("text");
        },
        setClipboardText: function(event, value){
            if (event.clipboardData){
                event.clipboardData.setData("text/plain", value);
            } else if (window.clipboardData){   // 兼容IE
                window.clipboardData.setData("text", value);
            }
        }
    };
    
    var input = document.getElementById("name");
    input.addEventListener("paste", function(event) {
        var data = event.clipboardData.getData("text");
        console.log(data);
        if(!/^\d*$/.test(data)) {
            // 只允许粘贴数字
            event.preventDefault();
        }
    });

    3. 自动切换焦点

    用户填写完当前字段时,自动将焦点切换到下一个字段。

    <p>Enter your telephone number:</p>
    <input type="text" name="tel1" id="txtTel1" size="3" maxlength="3" >
    <span>-</span>
    <input type="text" name="tel2" id="txtTel2" size="3" maxlength="3" >
    <span>-</span>
    <input type="text" name="tel3" id="txtTel3" size="4" maxlength="4" >
    (function(){    
        function tabForward(event){            
            event = EventUtil.getEvent(event);
            var target = EventUtil.getTarget(event);
            if (target.value.length == target.maxLength){
                var form = target.form;        
                for (var i=0, len=form.elements.length; i < len; i++) {
                    if (form.elements[i] == target) {
                        if (form.elements[i+1]){
                            form.elements[i+1].focus();
                        }
                        return;
                    }
                }
            }
        }
    
        var textbox1 = document.getElementById("txtTel1"),
            textbox2 = document.getElementById("txtTel2"),
            textbox3 = document.getElementById("txtTel3");
    
        EventUtil.addHandler(textbox1, "keyup", tabForward);        
        EventUtil.addHandler(textbox2, "keyup", tabForward);        
        EventUtil.addHandler(textbox3, "keyup", tabForward);            
    })();

    4. HTML5约束验证API

    (1)必填字段:<input type="text" required />
    (2)特殊输入类型:<input type="email | url" />
    (3)数值范围:<input type="number" min="0" max="10" />
    (4)输入模式:<input type="text" pattern="\d+" />注意,模式的开头和末尾不用加^和$符合(默认已经有了)
    (5)检测有效性:checkValidatity()
    (6)禁用验证:

    <!-- 整个表单不进行验证 -->
    <form method="post" action="" novalidate >
    <!-- 某个按钮提交不必验证表单-->
    <input type="submit" formnovalidate name="btnNoValidate" />

    三、选择框脚本

    <select><option>元素创建
    HTMLSelectElement的属性和方法:

    属性和方法作用说明
    add(newOption, relOption)向控件中插入新项,其位置在相关项relOption之前
    multiple是否支持多项选择
    options所有项集合
    remove(index)移除给定位置的选项
    selectIndex基于0的选中项的索引,如果没有选中项,则该值为-1;对于支持多选的控件,只保存选中项的第一项索引
    size选择框中可见的行数

    HTMLOptionElement的属性和方法:

    属性和方法作用说明
    index当前选项在options集合中的索引
    label当前选项的标签
    selected当前选项是否被选中
    text选项的文本
    value选项的值
    <form method="post" action="" id="myForm">
        <label for="selLocation">Where do you want to live?</label>
        <select name="location" id="selLocation">
            <option value="Sunnyvale, CA">Sunnyvale</option>
            <option value="Los Angeles, CA">Los Angeles</option>
            <option value="Mountain View, CA">Mountain View</option>
            <option value="">China</option>
            <option>Australia</option>
        </select>
    </form>
    // 选择上述每个选项后,value值分别为:["Sunnyvale, CA", "Los Angeles, CA", "Mountain View, CA", "", "Australia"]
    document.getElementById("selLocation").value; 
    // 获得第一个选项的文本和值
    document.forms[0].elements["location"].options[0].text;
    document.forms[0].elements["location"].options[0].value;

    (1)展示规则:有value属性且值不为空,则展示value属性的值;否则展示该项的文本值。
    (2)value值规则:有value属性(不管是否为空),获得的都是对应value属性的值;否则为该项文本值。

    1. 选择选项

    function getSelectedOptions(selectbox){
        var result = new Array();
        var option = null;
    
        for (var i=0, len=selectbox.options.length; i < len; i++){
            option = selectbox.options[i];
            if (option.selected){
                result.push(option);
            }
        }
    
        return result;            
    }

    2. 添加选项

    (1)DOM方式

    var newOption = document.createElement("option");
    newOption.appendChild(document.createTextNode("Option text"));
    newOption.setAttribute("value", "Option value");
    selectbox.appendChild(newOption);

    (2)Option构造函数

    var newOption = new Option("Option text", "Option value");
    selectbox.appendChild(newOption);

    (3)选择框的add方法(最佳方案)

    var newOption = new Option("Option text", "Option value");
    selectbox.add(newOption, undefined);    // 插入到最后

    3. 移除选项

    (1)DOM方

    selectbox.removeChild(selectbox.options[0]);

    (2)选择框的remov

    selectbox.remove(0);

    (3)将相应的选项设置为null(遗留机制)

    selectbox.options[0] = null;

    4. 移动和重排选项

    DOM的appendChild方法(只能添加到最后),如果appendChild传入一个文档中已有的元素,那么就会先从该元素的父节点中移除它,再把它添加到指定的位置。

    // 将第一个选择框中的第一个选项移动到第二个选择框中
    var selectbox1 = document.getElementById("selLocations1"),
        selectbox2 = document.getElementById("selLocations2");
    selectbox2.appendChild(selectbox1.options[0]);  

    DOM的insertBefore方法

    // 将选择框中的选项向后移动一个位置
    var optionToMove = selectbox.options[selectbox.options.length - 1]; 
    selectbox.insertBefore(optionToMove, selectbox.options[0]);

    四、表单序列化

    • 对表单字段的名称和值进行URL编码,使用“&”分隔;
    • 不发送禁用的表单字段;
    • 只发送勾选的复选框和单选按钮;
    • 不发送type为“reset”和“button”的按钮;
    • 选择框中每个选中的值单独条目发送;

    五、富文本编辑

    contenteditable:用户立即可编辑该元素

    data:text/html, <html contenteditable>
    展开全文
  • Struts2 表单和非表单标签

    千次阅读 2017-06-02 08:46:01
    学习内容Struts 2表单标签 Struts 2非表单标签能力目标 熟练使用Struts 2表单标签开发表单 熟练使用Struts 2非表单标签 本章简介上一章讲述了ognl和Struts2标签库中的数据访问标签和逻辑控制标签。本章将重点...

    学习内容

    Struts 2表单标签

     Struts 2非表单标签

    能力目标

     熟练使用Struts 2表单标签开发表单

     熟练使用Struts 2非表单标签

     


    本章简介

    上一章讲述了ognlStruts2标签库中的数据访问标签和逻辑控制标签。本章将重点介绍Struts2标签库中的表单标签和非表单标签。

    表单标签主要用于生成HTML中的表单。

    非表单标签主要包含一些常用的功能标签,例如显示日期或树形菜单。熟练使用Struts2标签将大大简化视图页面的代码编辑工作,提高视图页面的维护效率。

    核心技能部分 

     

    7.1 表单标签

     Struts的表单标签,可分为两种:form标签本身和单个表单元素的标签。form标签本身的行为不同于表单元素标签。Struts 2的表单元素标签都包含了非常多的属性,但有很多属性完全是通用的。

    7.1.1 表单标签的通用属性

    所有表单标签处理类都继承了UIBean类,UIBean包含了一些通用属性,这些通用属性分成3:

    模板相关属性。

     JavaScript相关属性。

     通用属性。

    除了这些属性之外,所有表单元素标签都存在一个特殊的属性:form,这个属性引用表单元素所在的表单,通过该form属性,可以实现表单元素和表单之间的交互。例如,我们可以通过${ parameters.form. id }来取得表单元素所在表单的ID。下面详细列出这些表单标签的通用属性。

    模板相关的通用属性如下:

     templateDir:指定该表单所用的模板文件目录。

     theme:指定该表单所用的主题。

     template:指定该表单所用的模板。

    Javascript相关的通用属性如下:

     onclick:指定鼠标在该标签生成的表单元素上单击时触发的JavaScript函数。

     onmousedown:指定鼠标在该标签生成的表单元素上按下时触发的JavaScript函数。

     onmouseup:指定鼠标在该标签生成的表单元素上松开时触发的JavaScript函数。

     onmouseover:指定鼠标在该标签生成的表单一元素上悬停时触发的JavaScript函数。

     onmouseout:指定鼠标移出该标签生成的表单元素时触发的JavaScript函数。

     onfocus:指定该标签生成的表单元素得到焦点时触发的函数。

     onblur:指定该标签生成的表单元素失去焦点时触发的函数。

     onkeypress:指定单击键盘上某个键时触发的函数。

     onkeyup:指定松开键盘某个键时触发的函数。

     onkeydown:指定按下键盘上某个键时触发的函数。

     onselect:对下拉列表项等可以选择表单元素,指定选中该元素时触发的JavaScript函数。

     onchange:对于文本框等可以接受输入的表单元素,指定当值改变时触发的JavaScript函数。

    因为HTML元素本身的限制,并不是每个HTML元素都可以触发以上的所有函数。因此,上面的属性并不是对Struts 2的每个标签都有效。

    Struts 2还允许为表单元素设置提示,但鼠标在这些元素上悬停时,系统将出现提示,

    Struts 2将这种特性称为Tooltip。与Tooltip相关的通用属性如下:

     tooltip:设置此组件的Tooltip

     tooltipIcon:设置Tooltip图标的URL路径。

     tooltipAboveMousePointer:是否在光标位置上显示Tooltip。也可通过设置tooltipOffseY属性,设置Tooltip与光标位置的垂直位移。

     tooltipBgColor:设置Tooltip的背景色。

     tooltipBgImg:设置Tooltip的背景图片。

     tooltipBorderWidth:设置Tooltip边框的宽度。

     tooltipBorderColor:设置Tooltip边框的颜色。

     tooltipDelay:设置显示Tooltip的时间延迟(单位是毫秒)

     tooltipFixCoordinateX:设置固定Tooltip在指定的X坐标上,与tooltipSticky属性结合时很有用。

     tooltipFixCoordinateY设置固定Tooltip在指定的Y坐标上,与tooltipSticky属性结合时很有用。

     tooltipFontColor:设置Tooltip的字体颜色。

     tooltipFontFace:设置Tooltip的字体,例如verdana.  geneva,  sans-serif等。

     tooltipFontSize:设置Tooltip的字体大小,例如30px

     tooltipFontWeight:设置Tooltip的字体是否使用粗体,可以接受normalbold(粗体)两个值。

     tooltipLeftOfMousePointer:设置是否在光标左侧显示Tooltip,默认是在右边显示。

     tooltipOffsetX:设置Tooltip相对光标位置的水平位移。

     tooltipOffsetY:设置Tooltip相对光标位置的垂直位移。

     tooltipOpacity:设置Tooltip的透明度,设置值可以是0(完全透明)100(不透明)之间的数字。Opera浏览器不支持该属性。

     tooltipPadding:指定Tooltip的内部间隔。例如,边框和内容之间的间距。

     tooltipShadowColor:使用指定的颜色为Tooltip创建阴影。

     tooltipShadowWidth:使用指定的宽度为Tooltip创建阴影。

     tooltipStatic:设置Tooltip是否随着光标的移动而移动。

     tooltipSticky:设置Tooltip是否一直停留在它初始的位置,直到另外一个Tooltip被激活,或者浏览者点击了HTML页面。

     tooltipStayAppearTime:指定一个Tooltip消失的时间间隔(毫秒),即使鼠标还在相关的HTML元素上不动。设置值<=0,就和没有定义一样。

     tooltipTextAlign:设置Tooltip的标题和内容的对齐方式,可以是right(右对齐)left(左对齐)justify(居中对齐)

     tooltipTitle:设置Tooltip的标题文字。

     tooltipTitleColor:设置Tooltip的标题文字的颜色。

     tooltipWidth:设置Tooltip的宽度。

    除此之外,Struts 2还有其他的通用属性,用于设置表单元素的CSS样式等。

     cssClass:设置该表单元素的class属性。

     cssStyle:设置该表单一元素的style属性,使用内联的CSS样式。

     title:设置表单元素title属性。

     disabled:设置表单元素的disabled属性。

     label:设置表单元素的label属性。

     labelPosition:设置表单元素label所在位置,可接受的值为top(上面)left(左边),默认是在左边。

     requiredposition:定义必填标记(默认以*作为必填标记)位于label元素的位置,可接受的值为left(左面)right(右边),默认是在右边。

     name:定义表单一元素的name属性,该属性值用十与Action的属性形成对应。

     required:定义是否在表单元素的label上增加必填标记(默认以*作为必填标记),设置为true时增加必填标记,否则不增加。

     tabIndex:设置表单元素的tabindex属性。

     value:设置表单兀素的value属性。

    7.1.2 表单标签的namevalue属性

    对于表单元素而言,namevalue属性之间存在一个独特的关系:因为表单元素的name属性会映射到Action的属性,当该表单对应的Action已经被实例化,且对应属性有值时,表单元素会显示出该属性的值,该值就是表单元素的value值。

    name属性设置表单元素的名字,表单元素的名字实际上封装着一个请求参数,而请求参数是被封装到Action属性的。因此,可以将该name属性指定为你希望绑定值的表达式。    例如如下代码:

    <!--将下面文本框的值绑定到Action的person属性的firstName属性 -- >

          <s:textfield  name="person.firstName”/>

    大多数场景下,我们希望表单元素里可以显示出对应Action的属性值,此时就可以通过指定该表单元素的value属性来完成该工作。例如使用如下代码:

        <!--使用表达式生成表单元素的值一>

          <s:textfield name=”person.firstName” value=”${person.firstName}”/>

    事实上,因为namevalue属性的特殊关系,如果我们使用Struts 2的标签库,则无需指定value属性,因为Struts 2会为我们处理这些。

    对于如下代码:

        <!一将下面文本框的值绑定到Action的person属性的firstName属性一>

          <s:textfield name=”person.firstName"/>

    虽然上面的文本框没有指定value属性,但Struts 2一样会在该文本框中输出对应Action里的属性值。

    7.1.3 常用表单标签

    1.  <s:form>标签

    该标签的主要属性是actionnamespace,例如下面的代码:

    <s:form action="tag1.action" namespace="/tags">

    </s:form>

    生成的HTML代码如下所示:

    <form action="tag1.action" method="post">

    2. 文本类标签

    此类标签包括文本框(<s:textfield />)、密码框(<s:password />)、文本区域(<s:textarea>),下面是一个简单的示例。

    示例7.1

     <body>
    <s:form action="tag1" namespace="/tags">
    <s:textfield name="loginname" label="登录名称"></s:textfield>
    <s:password name="pwd" label="登录密码"></s:password>
    <s:textarea name="info" label="个人简介" cols="28" rows="10"></s:textarea>
    <s:submit value="提交"></s:submit>
    <s:reset value="重填"></s:reset>
    </s:form>
     </body>


     

    7.1.1 文本类标签

    上述代码生成的HTML如下所示。

    <body>
    <form action="/Struts8/tags/tag1.action" method="post">
    <table class="wwFormTable">
    <tr>
    <td class="tdLabel">
    <label for="tag1_loginname" class="label">登录名称:</label></td>
    <td>
    <input type="text" name="loginname"/></td>
    </tr>
    <tr>
        <td class="tdLabel"><label for="tag1_pwd" class="label">登录密码:</label>
    </td>
    <td>
    <input type="password" name="pwd" id="tag1_pwd"/></td>
    </tr>
    <tr>
        <td class="tdLabel">
    <label for="tag1_info" class="label">个人简介:</label>
       </td>
        <td>
    <textarea name="info" cols="28" rows="10"></textarea>
    </td>
    </tr>
    <tr>
        <td colspan="2"><input type="submit" value="提交"/></td>
    </tr>
    <tr>
        <td colspan="2"><input type="reset" value="重填"/></td>
    </tr>
    </table>
    </form>
    </body>

    3. checkboxlist标签

    checkboxlist标签可以一次创建多个复选框,用于一次生成多个HTML标签中的<input type="checkbox"/>,它根据list属性指定的集合来生成多个复选框。因此,使用该标签指定一个list属性。除此之外,其他属性大部分是通用属性,此处不再赘述。

    除此之外,checkboxlist表单还有两个常用属性:

     listKey:该属性指定集合元素中的某个属性(例如集合元素为Person实例,指定Person实例的name属性)作为复选框的value。如果集合是Map,则可以使用keyvalue值分别代表Map对象的keyvalue作为复选框的value

     listValue:该属性指定集合元素中的某个属性(例如集合元素为Person实例,指定Person实例的name属性)作为复选框的标签。如果集合是Map,则可以使keyvalue值分别代表Map对象的keyvalue作为复选框的标签。

    下面是使用该标签的代码示例,其中分别使用了简单集合、简单Map对象、集合里放置Java实例来创建多个复选框。

    下面是该JSP页面代码:

    <body>
    <h2>使用checkboxlist标签</h2>
    <s:form>
    <!-- 使用字符串集合生成多个复选框 -->
    <s:checkboxlist
      	list="{'JSP','Servlet','Struts2','Ajax'}" name="skills"
    label="熟悉技术">
    </s:checkboxlist>
    <!-- 使用Map对象来生成多个复选框 -->
    <s:checkboxlist name="skillsarea" label="你所关注的技术领域"
        list="#{'java':'JAVA', '.net':'.NET', 'db':'Database','ajax':'Ajax'}"
       listKey="key"
       listValue="value"/>
    </s:form>
    </body>

    在上面代码中,简单集合对象和简单Map对象都是通过OGNL表达式直接生成,但实际使用中一般是从后台查询出一个javaBean集合然后传递到页面进行显示。该JavaBean的类代码如下:

    public class Book

    {

    private int id;

         private  String bookName;

    private  String author;

    //getter setter方法省略…….

    }

    Jsp页面代码:

    <%
        	//模拟后台查询到的数据 并转发到页面
        	List books = new ArrayList();
        	books.add(new Book(1,"杀杀人,喝喝酒---水浒传","施耐庵"));
        	books.add(new Book(2,"疯狂的猴子---西游记","吴承恩"));
        	books.add(new Book(3,"疯狂的石头---红楼梦","曹雪芹"));
        	books.add(new Book(4,"大哥,我们动手吧---三国演义","罗贯中"));
        	request.setAttribute("bs",books);
         %>
        <s:form>
    <!-- 使用字符串集合生成多个复选框 -->
    <s:checkboxlist
      	list="{'JSP','Servlet','Struts2','Ajax'}" name="skills"
    label="熟悉技术">
    </s:checkboxlist>
    <!-- 使用Map对象来生成多个复选框 -->
    <s:checkboxlist name="skillsarea" label="你所关注的技术领域"
        list="#{'java':'JAVA', '.net':'.NET', 'db':'Database','ajax':'Ajax'}"
       listKey="key"
       listValue="value"/>
       
         <s:checkboxlist label="你喜欢的图书" name="lb" list="#attr.bs"
         	listKey="id" listValue="bookName" value="1"
         >
         </s:checkboxlist>
    </s:form>

    当我们在浏览器中浏览该页面时,将看到如图7.1.2所示的页面。

     

    7.1.2 checklist标签

    生成的HTML代码如下所示:

    <html>
      <head>
        <base href="http://localhost:8080/test23/">
        
        <title>My JSP 'index.jsp' starting page</title>
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    <!--
    <link rel="stylesheet" type="text/css" href="styles.css">
    -->
      </head>
      
      <body>
        
        <form id="" action="/test23/" method="post">
    <table class="wwFormTable">
    <!-- 使用字符串集合生成多个复选框 -->
     
    <tr>
        <td class="tdLabel"><label for="_skills" class="label">熟悉技术:</label></td>
        <td
    >
    <input type="checkbox" name="skills" value="JSP" id="skills-1"/>
    <label for="skills-1" class="checkboxLabel">JSP</label>
    <input type="checkbox" name="skills" value="Servlet" id="skills-2"/>
    <label for="skills-2" class="checkboxLabel">Servlet</label>
    <input type="checkbox" name="skills" value="Struts2" id="skills-3"/>
    <label for="skills-3" class="checkboxLabel">Struts2</label>
    <input type="checkbox" name="skills" value="Ajax" id="skills-4"/>
    <label for="skills-4" class="checkboxLabel">Ajax</label>
    <input type="hidden" id="__multiselect__skills" name="__multiselect_skills" value="" />     </td>
    </tr>
     <!-- 使用Map对象来生成多个复选框 -->
    <tr>
        <td class="tdLabel"><label for="_skillsarea" class="label">你所关注的技术领域:</label></td>
        <td
    >
    <input type="checkbox" name="skillsarea" value="java" id="skillsarea-1"/>
    <label for="skillsarea-1" class="checkboxLabel">JAVA</label>
    <input type="checkbox" name="skillsarea" value=".net" id="skillsarea-2"/>
    <label for="skillsarea-2" class="checkboxLabel">.NET</label>
    <input type="checkbox" name="skillsarea" value="db" id="skillsarea-3"/>
    <label for="skillsarea-3" class="checkboxLabel">Database</label>
    <input type="checkbox" name="skillsarea" value="ajax" id="skillsarea-4"/>
    <label for="skillsarea-4" class="checkboxLabel">Ajax</label>
    <input type="hidden" id="__multiselect__skillsarea" name="__multiselect_skillsarea" value="" />     </td>
    </tr>
         <tr>
        <td class="tdLabel"><label for="_lb" class="label">你喜欢的图书:</label></td>
        <td
    >
    <input type="checkbox" name="lb" value="1" id="lb-1" checked="checked"/>
    <label for="lb-1" class="checkboxLabel">杀杀人,喝喝酒---水浒传</label>
    <input type="checkbox" name="lb" value="2" id="lb-2"/>
    <label for="lb-2" class="checkboxLabel">疯狂的猴子---西游记</label>
    <input type="checkbox" name="lb" value="3" id="lb-3"/>
    <label for="lb-3" class="checkboxLabel">疯狂的石头---红楼梦</label>
    <input type="checkbox" name="lb" value="4" id="lb-4"/>
    <label for="lb-4" class="checkboxLabel">大哥,我们动手吧---三国演义</label>
    <input type="hidden" id="__multiselect__lb" name="__multiselect_lb" value="" />     </td>
    </tr> 
    </table></form> 
    </body>
    </html>

    从图7.1.2中可以看出,通过指定checkboxlist标签的listKeylistValue属性,可以分别指定多个复选框的value和标签。

    4. radio标签

    该标签与上面的checkboxlist标签的用法几乎完全相同,都是通过指定listlistKeylistValue等属性来生成HTML元素。不同的是,checkboxlist标签生成的是复选框,而radio标签生成的是单选框。

    下面我们看一个实例,代码如下所示:

    示例7.2

    <body>

    <h2>使用radio标签</h2>

    <s:form>

    <!-- 使用字符串集合来生成多个单选框 -->

    <s:radio

       list="{'JSP','Servlet','Struts2','Ajax'}" name="skills"

         label="熟悉技术">

    </s:radio>

    <!-- 使用Map对象来生成多个单选框 -->

    <s:radio name="skillsarea" label="你所关注的技术领域"

        list="#{'java':'JAVA', '.net':'.NET', 'db':'Database','ajax':'Ajax'}"

      listKey="key"

      listValue="value"

      value="'java'"/>

    </s:form>

    </body>

    该页面的运行效果如图7.1.3所示。

     

    <body>
    <h2>使用radio标签</h2>
    <form action="/Struts8/s-radio.jsp" method="post">
    <table class="wwFormTable">
    <!-- 使用字符串集合来生成多个单选框 -->
    <tr>
        <td class="tdLabel">
    <label for="s-radio_skills" class="label">熟悉技术:</label></td>
        <td>
    <input type="radio" name="skills" value="JSP"/>
    <label for="s-radio_skillsJSP">JSP</label>
    <input type="radio" name="skills" value="Servlet"/>
    <label for="s-radio_skillsServlet">Servlet</label>
    <input type="radio" name="skills" value="Struts2"/>
    <label for="s-radio_skillsStruts2">Struts2</label>
    <input type="radio" name="skills" value="Ajax"/>
    <label for="s-radio_skillsAjax">Ajax</label>
     </td>
    </tr>
    <!-- 使用Map对象来生成多个单选框 -->
    <tr>
        <td class="tdLabel"><label for="s-radio_skillsarea" class="label">
    你所关注的技术领域:</label></td>
        <td>
    <input type="radio" name="skillsarea" checked="checked" value="java"/>
    <label for="s-radio_skillsareajava">JAVA</label>
    <input type="radio" name="skillsarea" value=".net"/>
    <label for="s-radio_skillsarea.net">.NET</label>
    <input type="radio" name="skillsarea" value="db"/>
    <label for="s-radio_skillsareadb">Database</label>
    <input type="radio" name="skillsarea" value="ajax"/>
    <label for="s-radio_skillsareaajax">Ajax</label>
    </td>
    </tr>
    </table>
    </form>
    </body>


    5. select标签

    select标签用于生成一个下拉列表框,通过为该元素指定list属性,系统会使用list属性指定的集合来生成下拉列表框的选项。这个list属性指定的集合,既可以是普通集合,也可以是Map对象,还可以是集合元素是对象的集合。

    除此之外,select表单还有如下几个常用属性:

    Ø listKey:该属性指定集合元素中的某个属性(例如集合元素为Person实例,指定Person实例的name属性)作为复选框的value。如果集合是Map,则可以使用keyvalue值分别代表Map对象的keyvalue作为复选框的value

    Ø listValue:该属性指定集合元素中的某个属性(例如集合元素为Person实例,指定Person实例的name属性)作为复选框的标签。如果集合是Map ,则可以使用keyvalue值分别代表Map对象的keyvalue作为复选框的标签。

    Ø multiple:设置该列表框是否允许多选。

    从上面介绍中可以看出,select标签的用法与checkboxlist标签的用法非常相似。

    下面我们看一个实例,其中分别使用了简单集合、简单Map对象、集合里放置Java实例来创建多个复选框。代码如下所示:

    示例7.3

     

         <body>

      <h2>使用select标签</h2>

    <!-- 使用字符串集合来生成下拉列表框 -->

    <s:elect list="{'Struts2从入门到精通' , 'Spring2开发宝典' , 'Ajax实战'}"

         name="books" label="图书列表">

    </s:select>

    <br><br>

    <!-- 使用简单对象来生成下拉列表框 -->

    <s:select name="books_map" label="图书列表"

        list="#{'struts2':'Struts2从入门到精通', 'spring2':'Spring2开发宝典',

                 'ajax':'Ajax实战'}"

    listKey="key"

    listValue="value"/>

    <br><br>

    <s:select name="b" label="图书列表"

              multiple="true"

                list="#attr.bs"

              listKey="id"

                listValue="bookName" value="2"/>

      </body>

     

    该页面的运行效果如图7.1.4所示。

     

    7.1.4  使用select标签

    注意:可以使用selectvalue属性设置<s:select>标签生成下拉框时的初始值,默认情况下将以生成下拉框的第一项为初始值。

    6. doubleselect标签

        doubleselect标签会生成一个级联列表框(会生成两个下拉列表框),当选择第一个下

    拉列表框时,第二个下拉列表框的内容会随之改变。

        因为两个都是下拉选择框,因此需要指定两个下拉选择框的选项,因此有如下常用的属性:

     list:指定用于输出第一个卜拉列表框中选项的集合。

     listKey:该属性指定集合元素中的某个属性(例如集合元素为Person实例,指定Person实例的name属性)作为第一个下拉列表框的value。如果集合是Map,则可以使用keyvalue值分别代表Map对象的keyvalue作为复选框的value

     listValue:该属性指定集合元素中的某个属性(例如集合元素为Person实例,指定Person实例的name属性)作为复选框的标签。如果集合是Map,则可以使用keyvalue值分别代表Map对象的keyvalue作为第一个下拉列表框的标签。

     doubleList:指定用于输出第一个下拉列表框中选项的集合。

     doubleList Key:该属性指定集合元素中的某个属性(例如集合元素为Person实例,指定Person实例的name属性)作为第一个下拉列表框的value。如果集合是Map,则可以使用keyvalue值分别代表Map对象的keyvalue作为复选框的value

     doubleList Value:该属性指定集合元素中的某个属性(例如集合元素为Person实例,指定Person实例的name属性)作为复选框的标签。如果集合是Map,则可以使用keyvalue值分别代表Map对象的keyvalue作为第一个下拉列表框的标签。

     doubleName:指定第二个下拉列表框的name属性。

     cssStyle:设置第一级下拉框的样式。

     doubleCssStyle:设置第二级下拉框的样式。

    下面看一个实例,代码如下所示:

    示例7.4

    (1)创建用于描述部门的实体类。

     

    public class DeptInfo {

     

    private String deptId;

    private String deptName;

    private Set<EmpInfo> empSet = new HashSet<EmpInfo>();//包含多个职工

    //无参构造函数

    public DeptInfo() {

    super();

    }

    //有参构造函数

    public DeptInfo(String deptId, String deptName) {

    super();

    this.deptId = deptId;

    this.deptName = deptName;

    }

    //getter setter 方法省略......

    }

    (2)创建用于描述职工的实体类。

    public class EmpInfo { 
    private String empId;
    private String empName;
    //无参构造函数
    public EmpInfo() {
    super();
    }
    //有参构造函数
    public EmpInfo(String empId, String empName) {
    super();
    this.empId = empId;
    this.empName = empName;
    }
    //getter setter 方法省略......
    } 

    (3)创建业务逻辑控制器。

    public class DoubleSelectAction extends BaseAction
    {
    //用于封装用户请求的部门编号
    private String deptId;
    //用于封装用户请求的职工编号
    private String empId;
    public String execute() throws Exception
    {
    //获取输出对象
    PrintWriter out  = super.getResponse().getWriter();
    out.println("<h2>你选择的信息是: <hr/>部门编号: " + 
    this.deptId + "<br/>");
    out.println(" 职工编号: " + this.empId + "</h2>");
    return null;
    } 
    public String getDeptId() {
    return deptId;
    } 
    public void setDeptId(String deptId) {
    this.deptId = deptId;
    } 
    public String getEmpId() {
    return empId;
    } 
    public void setEmpId(String empId) {
    this.empId = empId;
    }
    }

    (4)创建jsp页面。

      <body>
        <%
        	DeptInfo d1 = new DeptInfo("D001","销售部");
        	DeptInfo d2 = new DeptInfo("D002","财务部");
        	EmpInfo e1  = new EmpInfo("E001","张三");
        	EmpInfo e2  = new EmpInfo("E002","李四");
        	EmpInfo e3  = new EmpInfo("E003","王五");
        	EmpInfo e4  = new EmpInfo("E004","赵六");
        
        	d1.getEmpSet().add(e1);
        	d1.getEmpSet().add(e2);
        
        	d2.getEmpSet().add(e3);
        	d2.getEmpSet().add(e4);
        	//构造map,用于存放所有部门
        	Map map = new HashMap();
        	map.put(d1,d1.getEmpSet());
        	map.put(d2,d2.getEmpSet());
        	request.setAttribute("map",map);
        %>
        
        <s:form action="doubleSelect" namespace="/test">
        	级联下拉框:<hr/>
        	<s:doubleselect cssStyle="width:130px" list="#request.map.keySet()" listKey="deptId" listValue="deptName" name="deptId"
        	  doubleCssStyle="width:130px" doubleList="#request.map[top]" doubleListKey="empId" doubleListValue="empName"  doubleName="empId"/>
        	<hr>
        	<s:submit value="提交"/>  
        </s:form>
      </body>

    运行以上Jsp页面,结果如图7.1.5所示,

     

    7.1.5 DoubleSelect标签的使用

    在图7.1.5中,对第一级“部门”下拉框做任意选择时,第二级“职工”下拉框内容将做相应的级联改变。

    单击“提交”按钮,提交到DoubleSelectAction控制器,运行结果如图7.1.6所示。

     

    7.1.6 级联下拉框提交

    注意:

    使用doubleselect标签时,必须放在<<s:form/>标签中使用,且必须为该<s:form .../>标签指定action属性 

    7. optiontransferselect标签

    Optiontransferselect创建两个选项用来转移下拉列表项,该标签会生成两个<select/>标签,并且会生成系列的按钮,这系列的按钮可以控制选项在两个下拉列表之间移动、升降。当提交该表单时,两个<select/>标签的请求参数都会被提交。

    因为该标签会生成两个下拉列表框,因此需要分别指定两个下拉列表框中的集合、Label等属性,下面是该标签常用的属性。

     addAllToLeftLabel:设置全部移动到左边按钮上的文本。

     addAllToRightLabel:设置全部移动至右边按钮上的文本。

     addToLeftLabel:设置向左移动按钮上的文本。

     addToRightLabel:设置向右移动按钮上的文本。

     a1lowAddAllToLeft:设置是否出现全部移动到左边的按钮。

     a1lowAddAllToRight:设置是否出现全部移动到右边的按钮。

     a1lowAddToLeft:设置是否出现移动到左边的按钮。

     a1lowAddToRight:设置是否出现移动到右边的按钮。

     leftTitle:设置左边列表框的标题。

     rightTitle:设置右边列表框的标题。

     a1lowSelectAll:设置是否出现全部选择按钮。

     selectAllLabel:设置全部选择按钮上的文本。

     doubleList:设置用于创建第二个下拉选择框的集合。

     doubleListKey:设置创建第二个下拉选择框的选项value的属性。

     doubleListValue:设置创建第二个下拉选择框的选项label的属性。

     doubleName:设置第二个下拉选择框的name属性。

     doubleValue:设置第二个下拉选择框的value属性。

     doubleMultiple:设置第二个下拉选择框是否允许多选。

     list:设置用于创建第一个下拉选择框的集合。

     listKey:设置创建第一个下拉选择框的选项value的属性。

     listValue:设置创建第一个下拉选择框的选项label的属性。注意此处的list ,  doubleList ,   listKey ,  doubleListKey ,  listValuedoubleListValue非常类似于checkboxlist标签中list, listKeylistValue的用法,只是此处用于生成两个下拉列表框,而前者是生成多个复选框而已。

     name:设置第一个下拉选择框的name属性。

     value:设置第一个下拉选择框的value属性。

     multiple:设置第一个下拉选择框是否允许多选。

    下面代码是使用optiontransferselect标签的示范,它分别指定了两个简单集合来生成两个下拉列表框的列表项。下面是使用optiontransferselect标签的代码:

    <!-- 使用简单集合对象来生成可移动的下拉列表框 其中list指定生成第一个下拉选择框的选项的集合

              doubleList指定生成第二个下拉选择框的选项的集合-->

          <s:optiontransferselect

            label="请选择你喜欢的图书"

                  name="cnb00k"

              leftTitle="中文图书"

            rightTitle="外文图书"

            list="{'Spring2.。宝典','轻量级J2EE企业应用实战','基于J2EE的Ajax宝典'}"

              multiple="true"

            addToLeftLabel="向左移动"

            selectAllLabel="全部选择"

              addAllToRightLabel="全部右移"

                headerKey="cnKey"

            headerValue="一选择中文图书一"

              emptyOption="true"

              doubleList="{'Expert One一on一One J2EE Design and Development’,

                    'JavaScript:The Definitive Guide’}"

                  doubleName="enBook"

    doubleHeaderKey="enKey"

    doubleHeaderValue="-一选择外文图书-一"

    doubleEmptyOption="true"

    doubleMultiple="true"></s:optiontransferselect>

    在浏览器中浏览该页面,将看到如图7.1.7所示的页面。

     

    7.1.7  optiontransferselect标签

    8. token标签

    <s:token>标签用于避免刷新页面时多次提交表单,通过借助于TokenInterceptor拦截器来实现阻止页面重复提交。当Jsp使用<s:token/>标签的时候,Struts2会建立一个GUID(全局唯一的字符串)并放在Session中,同时在表单中添加一个隐藏域。而TokenInterceptor拦截器会拦截用户的请求,并判断客户端form提交的tokensession中保存的token是否相等 ,如果相等,则执行action对应的方法并清空;否则,拦截器将直接返回invalid.token结果,不会执行Action对应的方法。示例7.5演示了使用token标签的过程。

    示例7.5

    (1)创建用于处理用户注册的业务逻辑控制器Action.

    public class RegAction extends ActionSupport { 
    private String uid;
    private String pwd;
    public String execute() throws Exception
    {
    HttpServletResponse response = (HttpServletResponse)ActionContext.getContext().get(ServletActionContext.HTTP_RESPONSE);
    response.setContentType("text/html;charset=gbk");
    response.setCharacterEncoding("gbk");
    PrintWriter out  = response.getWriter();
    Date today = new Date();
    out.println("<h2>用户注册信息如下: <hr/>用户名:" + this.uid + "<br/>密  码:" + this.pwd+"<hr>");
    out.println("正在进行注册  " + today.toLocaleString() + "...");
    return null;
    }
    public String getUid() {
    return uid;
    }
    public void setUid(String uid) {
    this.uid = uid;
    }
    public String getPwd() {
    return pwd;
    }
    public void setPwd(String pwd) {
    this.pwd = pwd;
    }
    }

    (2)创建Jsp页面,命名为“reg.jsp”。

     

     <body>
      	<font color="red"><s:actionerror/></font>
        <s:form action="reg" οnsubmit="return check()">
        <!-- 加入token标签 -->
        <s:token/>
        填写会员注册信息:<hr/>
        用户名:<s:textfield name="uid"/><br/>
        密  码:<s:password name="pwd"/><br>
        确认密码:<s:password name="surePwd"/><br>
        <s:submit value="提交"></s:submit>
        <s:reset value="重置"></s:reset>
        </s:form>
    <script type="text/javascript">
    function $(elementName)
    {
    return document.getElementById(elementName);
    }
    function check()
    {
    if($("reg_uid").value == "")
    {
    alert("用户名不能为空!");
    $("reg_uid").focus();
    return false;
    }
    if($("reg_pwd").value == "")
    {
    alert("密码不能为空!");
    $("reg_pwd").focus();
    return false;
    }
    if($("reg_pwd").value != $("reg_surePwd").value)
    {
    alert("两次密码不一致!");
    $("reg_surePwd").focus();
    return false;
    }
    return true;
    }
    </script>
      </body>

    (3)编写用于显示重复提交错误信息的资源文件,创建application_zh.properties中文资源文件,将文件存放于WEB-INF/classes目录下,内容为“Struts.messages.invalid.token=表单已提交,不能重复提交”,并使用native2Ascii命令进行转化。

    (4)struts.xml中配置token拦截器。

     

    <struts>
       <constant name="struts.ui.theme" value="simple" />
      <constant name="struts.custom.i18n.resources" value="application" />
    <package name="mypackage" extends="struts-default">
    <!--  定义拦截器栈
      -->
    <interceptors>
    <interceptor-stack name="tokenStack">
      <interceptor-ref name="token" />
    <!--  使用token拦截器
      -->
      <interceptor-ref name="defaultStack" />
     <!--  使用默认拦截器
      -->
      </interceptor-stack>
      </interceptors>
    <!--  将拦截器栈token作为默认拦截器,则拦截器栈中定义的所有拦截器将作用于本包的所有Action
      -->
      <default-interceptor-ref name="tokenStack" />
     <!--  配置注册Action
      -->
    <action name="reg" class="com.zzab.entity.RegAction">
    <!--  当用户重复提交表单时,流程转到stoken.jsp
      -->
      <result name="invalid.token">/stoken.jsp</result>
      </action>
      </package>
      </struts>

    运行reg.jsp,运行效果如图7.1.8所示。

     

    7.1.8 登录页面

    单击【提交】按钮后就执行了对应的Action,如图7.1.9所示。

     

    7.1.9 执行Action

    此时刷新页面,则根据token拦截器配置,将进入invalid.token逻辑结果,即reg.jsp。该页面会通过<s:actionerror/>标签读取application_zh.prooperties中文资源文件的内容并显示。如图7.1.10所示。

     

    7.1.10 不能重复提交

    7.1 非表单标签

    7.2.1 Datetimepicker标签 

    datetimepicke:标签生成一个日期、时间下拉选择框,当我们使用该日期、时间选择框选择某个日期、时间时,系统会自动将选中的日期、时间输入指定文本框。

    系统将指定日期、时间输入指定文本框时,必须转换成日期、时间字符串,为了设置该字符串的格式,必须使用日期、时间的格式符。datetimepicker支持如下几个格式符:

    #dd:以两位数字来显示当前日期的日。

    #d:试图以一位数字来显示当前日期的日,它不会使用两位数字。

    #MM:以两位数字来显示当前日期的月。

    #M:试图以一位数字来显示当前日期的月,它不会使用两位数字。

    #YYYY:4位数字来显示当前日期的年。

    #YY:以两位数字来显示当前日期的年。

    #y:以一位数字来显示当前日期的年的最后一位。

    使用该标签时,有如下几个常用属性:

     displayFormat:该属性指定日期的显示格式,例如,可以使用dd/MM/yyyy作为日期格式。

     displayWeeks:该属性指定该日历能显示星期数。

     endDate:指定日期集的最后可用日期。例如2941-10-12,一旦指定了该日期,则后面的日期不可用。

     formatLength:指定日期显示的格式,这些格式值就是DateFormat中的格式,该属性支持的值有long,  short,  mediumfull 4个值。

     language:指定日期显示的Locale,例如需要指定简体中文,则指定zh_ CN

     startDate:指定日期集的最后可用日期。例如1941-10-18,一旦指定了该日期,则前面的日期不可用。

     toggleDuration:指定日期选择框出现、隐藏的切换时间。

     toggletype:指定日期选择框出现、隐藏的方式,可以选择plain, wipe, explodefade 4个值。

     type:指定日期选择框的类型,支持的值有datetime,分别代表日期选择框、时间选择框。

     value:指定当前日期、时间。可使用today来代表今天。

     weekStartsOn:指定口期选择框中哪一天才是一周的第一天。周日是0,周六是6

    在使用datetimepicker标签之前,首先要通过MyEclipse导入struts2-dojo-plugin-2.2.1.jar文件,因为该标签包含在这个Jar文件中。

    示例7.6

    下面我们看一个该标签的实例,代码如下所示:  

     <s:form theme="simple">
        日期选择部件,指定toggleType属性,且指定value="today"<br>
        <sx:datetimepicker name="order.date" label="购买日期" toggleType="explode"
              value="%{'today'}"/><hr>
        日期选择部件,指定了format属性<br>
        <sx:datetimepicker name="order.date" label="购买日期" displayFormat=
              "yyyy年MM月dd日"/><hr>
        日期选择部件,指定了weekStartOn属性<br>
        <sx:datetimepicker name="order.date" label="购买日期" displayFormat="yyyy年MM月dd日" weekStartsOn="2"/><hr> 
    </s:form>

    在浏览器中浏览该页面,将看到如7.1.11所示的页面,在7.1.11中,如果我们单击那些文本框后选择日期,将出现一个日期选择框。如果我们指定weekStartsOn属性,则可以设置口期选择框中每周中哪一天才会是第一天。

     

    7.1.11 日期控件

     7.2.2 tree标签和treenode标签

    这两个标签主要用来生成一个树形结构,例如常见的树形菜单、树形列表等。tree标签表示树根,treenode标签表示树的的节点,需要嵌套在tree标签中。在使用这两个标签之前,也需要通过MyEclipse导入struts2-dojo-plugin-2.2.1.jar文件。

    示例7.7

    下面是一个使用tree标签和treenode标签生成静态树形列表的示例。

    <%@ page contentType="text/html; charset=utf-8" language="java" %>
    <%@taglib prefix="s" uri="/struts-tags" %>
    <%@ taglib prefix="sdt" uri="/struts-dojo-tags"%>
    <html>
    <sdt:head parseContent="true"/>
    <title>使用tree和treenode标签生成静态树</title>
    <body>
    <h3>使用tree和treenode标签生成静态树</h3>
    <sdt:tree label="计算机编程系列图书" id="books">
        <sdt:treenode label="JAVA编程系列" id="java">
            <sdt:treenode label="Spring2.0开发宝典" id="spring"/>
            <sdt:treenode label="Struts2.0权威指南" id="struts"/>
            <sdt:treenode label="Hibernate3从入门到精通" id="hibernate"/>
        </sdt:treenode>
        <sdt:treenode label="Ajax编程系列" id="ajax">
            <sdt:treenode label="JavaScript一百例" id="javascript"/>
            <sdt:treenode label="Ajax实战" id="ajaxsz"/>
        </sdt:treenode>
    </sdt:tree>
    </body>
    </html>

    通过上述代码可以看到,treetreenode标签都是通过label属性来设置树形列表文本,此案例树形列表中的文本数据都写死到里面了,实际应用中通常是从数据库查询出来并由Action返回到页面中。运行效果如图7.1.12所示。

     

    7.1.12 树形列表

    本章总结

     Struts的表单标签

    可分为两种:form标签本身和单个表单元素的标签。form标签本身的行为不同于表单元素标签。Struts 2的表单元素标签都包含了非常多的属性,但有很多属性完全是通用的。

     非表单标签

    1datetimepicke:标签生成一个日期、时间下拉选择框,当我们使用该日期、时间选择框选择某个日期、时间时,系统会自动将选中的日期、时间输入指定文本框。

    2tree标签和treenode标签。这两个标签主要用来生成一个树形结构,例如常见的树形菜单、树形列表等。tree标签表示树根,treenode标签表示树的的节点,需要嵌套在tree标签中。

    任务实训部分

    1:脏字过滤

    训练技能点

     token标签

     token拦截器

      需求说明

    在第五章的任务实现部分,我们实现了对评论进行脏字过滤功能,现在要求增加防止表单重复提交的功能。

    2:简易计算器

    训练技能点

     OGNL

     表单标签

      需求说明

    在前面的章节中我们实现了一个简易计算器案例,现在要求使用Struts 2表单标签和OGNL对该案例进行重构。

    3:动态生成树

    训练技能点

     OGNL

     Tree标签

      需求说明

    要求使用Struts 2 tree标签和OGNL动态生成数据。

    实现思路

    (1) 创建用于查询树节点信息的Action,命名为TreeAction.java

    public class TreeAction extends ActionSupport {  public String execute() { Object[][] obj = {   {"1","0","短信平台管理","#"},   {"2","1","用户管理","#"},   {"3","1","员工管理","#"},   {"4","1","短信管理","#"},   {"5","2","用户权限管理","userRight.jsp"},   {"6","2","用户列表","userList.jsp"},   {"7","3","员工信息列表","empList.jsp"},   {"8","3","员工信息修改","empUpdate.jsp"},   {"9","4","接收短信","acceptMessage.jsp"},   {"10","4","短信统计","messageCount.jsp"}   };  ServletActionContext.getRequest().setAttribute("obj",obj);  return SUCCESS; }}

    (2) 配置TreeAction

    <action name="treeaction" class="com.zzzy.action.TreeAction"> <result>/sx_tree.jsp</result> </action>

    (3) 创建sx_tree.jsp

    <sx:tree label="短信管理平台">  <s:iterator id="row" value="#request.obj">  <s:if test="#row[1]==1">  <sx:treenode label="%{#row[2]}">  <s:iterator id="row1" value="#request.obj">  <s:if test="#row1[1]==#row[0]">  <sx:treenode label="<a href=%{#row1[3]}>%{#row1[2]}</a>" />  </s:if>  </s:iterator>  </sx:treenode>  </s:if>    </s:iterator>  </sx:tree>

    巩固练习

    一、选择题

    1. 以下()不属于Struts 2的表单标签。

    A. <s:textfield>

    B. <s:textarea>

    C. <s:submit>

    D. <s:tree>

    2. token标签的作用是()。

    A. 显示错误消息

    B. 校验表单数据

    C. 防止重复提交

    D. 生成相应的html标签

    3. Datetimepicker标签的()属性用来指定该日历能显示星期数。

    A.  displayFormat

    B.  endDate

    C.  formatLength

    D.  displayWeeks

    4. 以下对checkboxlist标签描述正确的是()。

    A. list属性指定要循环的集合

    B. 该标签使用集合生成一组复选框

    C. Listkey指定集合元素中的某个属性作为复选框的文本

    D. listvalue指定集合元素中的某个属性作为复选框的value

    5. 以下对tree treenode标签描述错误的是()。

    A. 两个标签主要用来生成一个树形结构。

    B. tree标签表示树根。

    C. treenode标签表示树的的节点。

    D. 可以通过treenode list属性指定一个集合并生成一组树节点。 

    二、上机练习

     在本章的核心任务部分,我们使用doubleselect标签实现了一个省市级联列表框功能,现在要求把省市级联改成树形列表的形式。

     

    展开全文
  • Activiti动态表单开发技术分享

    千次阅读 2018-10-18 17:26:00
    如图所示,区别于普通表单和外置表单动态表单是直接将工作流节点处的表单嵌入流程定义文件BPMN中,系统利用JS或者模板引擎根据流程定义中表单定义的各个子控件及其属性动态渲染出表单加载出来。 2.  动态表单...

    1. 动态表单特点

    一般而言,工作流引擎常用表单有三种:普通表单、外置表单和动态表单。各自都有其优缺点,可根据具体场景灵活选用。需要说明的是,三种表单方式只是在任务节点上用户的表单定义方式上面有差别,而流程的运转机制则完全相同。 image.png

    如图所示,区别于普通表单和外置表单,动态表单是直接将工作流节点处的表单嵌入流程定义文件BPMN中,系统利用JS或者模板引擎根据流程定义中表单定义的各个子控件及其属性动态渲染出表单加载出来。

    2. 动态表单流程设计

    在模板管理界面,点击新增模板按钮,进入流程模板设计页面。

    image.png

    编辑流程信息:流程key、流程name等: image.png

    拖拽添加启动节点,点击启动节点,在下面的属性中点击动态表单属性: image.png

    编辑启动节点的动态表单属性,编辑活动编号、名称、类型、必输、可读、可写等属性后,点击保存: image.png

    添加下一个活动事件,并编辑该节点属性信息,重点是代理、和动态表单属性信息: image.png

    点击代理,编辑该事件的代理人、候选人(组),点击保存: image.png

    中间流程设计不详细讲述,最后添加一个结束节点并连接: image.png

    动态表单节点的常用属性介绍: TIM截图20180705173841.pngTIM截图20180705174046.png

    流程全部设计完成后,点击保存按钮进行保存:

    image.png

    在流程玩法-流程列表界面点击部署流程按钮:

    image.png

    3. 流程列表

    流程部署成功后,在待启动流程列表界面可以看到已部署的流程:

    image.png

    点击启动按钮,弹出启动节点的动态表单,输入信息后点击启动流程按钮:

    image.png

    4. 任务列表

    启动成功后,登录流程设计的该节点候选人(组)用户登录,在任务列表界面可以看到该流程,并可以点击签收按钮进行签收:

    image.png

    签收成功后,该项操作会变成“办理”状态,可以点击进行办理:

    image.png

    点击办理按钮,弹出该节点定义的动态表单,并进行提交操作:

    image.png

    5. 运行中流程

    点击运行中流程菜单可以查看已启动但未结束的流程列表,并且可以查看每个流程正在运行的节点:

    image.png

    点击当前节点,可以查看每个流程图及当前运行节点位置:

    image.png

    6. 已结束流程

    在已结束流程页面可以看到已经结束的流程列表:

    image.png

    7. 动态表单开发关键点

    标准流程的启动和运转直接调用Activiti的通用API即可实现,下面主要从以下几个方面讲解。

    1) 动态表单渲染

    动态表单将表单定义在了流程定义的文件中,因此在启动节点和任务节点处能分别通过流程定义ID和任务ID去获取节点处的表单属性JSON,如下所示:

    获取启动节点处表单数据链接:

    <u>http://localhost:8083/form/dynamic/get-form/start/leave-dynamic-from:2:47515</u>

    获取表单定义数据结果: {

    "form":{

    "deploymentId":"47512",

    "formKey":"",

    "formProperties":[

    {

    "id":"startDate",

    "name":"请假开始日期",

    "readable":true,

    "required":true,

    "type":{

    "name":"date"

    },

    "value":"",

    "writable":true

    },

    {

    "id":"endDate",

    "name":"请假结束日期",

    "readable":true,

    "required":true,

    "type":{

    "name":"date"

    },

    "value":"",

    "writable":true

    },

    {

    "id":"reason",

    "name":"请假原因",

    "readable":true,

    "required":true,

    "type":{

    "mimeType":"text/plain",

    "name":"string"

    },

    "value":"",

    "writable":true

    }

    ],

    "processDefinition":""

    }

    }

    获取到了表单定义属性文件后,就可以利用JS或者模板引擎渲染出表单了。比如利用layui的模板引擎来渲染,就可以定义如下模板:

    var**getTpl = `

    <form class="layui-form" lay-filter="form-tpl" style="padding: 10px;">

    {{# $.each(d.taskFormData.formProperties, function(i,v1) { }} {{# console.log(i,v1)}}

    <div>

    {{# if(v1.type.name=="string" ){ }}

    <div class="layui-form-item">

    <label class="layui-form-label">{{v1.name}}</label>

    <div class="layui-input-block">

    <input class="layui-input" type="text" name="{{v1.writable ? 'fp_'+v1.id : v1.id}}" autocomplete="off" {{v1.required? 'lay-verify="required"': ''}} placeholder="{{v1.name}}" lay-blur lay-verType="tips" value="{{v1.value}}" {{v1.writable ? '':'disabled'}}/>

    </div>

    </div>

    {{# } }}

    <div></div>

    {{# if(v1.type.name=="date" ){ }}

    <div class="layui-form-item">

    <label class="layui-form-label">{{v1.name}}</label>

    <div class="layui-input-block">

    <input class="layui-input date" type="text" name="{{v1.writable ? 'fp_'+v1.id : v1.id}}" autocomplete="off" {{v1.required? 'lay-verify="required|date"': ''}} placeholder="yyyy-MM-dd" lay-verType="tips" value="{{v1.value}}" {{v1.writable ? '':'disabled'}}/>

    </div>

    </div>

    {{# } }}

    <div></div>

    {{# if(v1.type.name=="enum" ){ }}

    <div class="layui-form-item">

    <label class="layui-form-label">{{v1.name}}</label>

    <div class="layui-input-block">

    <select name="{{v1.writable ? 'fp_'+v1.id : v1.id}}" {{v1.writable ? '':'disabled'}}>

    <option value=""></option>

    {{# $.each(d[v1.id+''],function(i2,v2){ }}

    <option value="{{i2}}" {{i2==v1.value ? 'selected':''}}>{{v2}}</option>

    {{# }) }}

    </select>

    </div>

    </div>

    {{# } }}

    </div>

    {{# }) }}

    <div style="display:none;">

    <!--此处隐藏但不能省略,为触发事件准备-->

    <button lay-submit>提交</button><button type="reset">重置</button>

    </div>

    </form>

    <style type="text/css">

    .layui-layer-page .layui-layer-content {

    overflow: visible;

    }

    </style>

    `**;

    上述模板对常见的string、date、enum类型进行了解析和渲染,有更多类型可以自己根据需要添加。

    此外,需要注意的是,要区别开表单中可编辑参数与不可编辑参数的属性配置,如下所示(红色标注的部分),可编辑参数的name属性值前面加上“fp_”(约定)。这样配置的好处是后台在接收到参数后可以区分开哪些参数是当前节点需要保存的参数信息,以便进行保存(见下面第二点表单的参数解析部分)。

    <input class="layui-input" type="text" name="{{v1.writable ? 'fp_'+v1.id : v1.id}}"* autocomplete="off" {{v1.required? 'lay-verify="required"': ''}} placeholder="{{v1.name}}" lay-blur lay-verType="tips" value="{{v1.value}}" {{v1.writable ? '':'disabled'}}/>*

    使用方法是在需要进行动态表单渲染的页面JS中引入该模板:

    //引入动态表单渲染模板

    $.use(ctx+'/static/js/dynamic-form-common.js', function*(){});*

    然后在获得表单属性数据后,调用renderForm方法,传入data数据和需要渲染的页面dom节点元素即可:

    renderForm(JSON.parse(form), $form.get(0));

    2) 表单参数解析

    前文已经提到,在表单渲染时就已经通过设置不同的name属性值来区分开了可编辑参数和不可编辑参数,因此在后台进行参数解析时就能很方便地对可编辑参数进行提取:

    // 从request中读取参数然后转换

    Map<String, String[]> parameterMap*=* request*.getParameterMap();*

    Set<Entry<String, String[]>> entrySet*=* parameterMap*.entrySet();*

    for*(Entry<String, String[]>* entry*:* entrySet*) {*

    String key*=* entry*.getKey();*

    // fp_的意思是form <u>paremeter</u>

    if* (StringUtils.defaultString(key).startsWith("fp_")) {      formProperties.put(key.replaceFirst("fp_",* ""**), entry*.getValue()[0]);* *} }

    上述代码就能将可编辑参数封装在formProperties这个HashMap中。

    3) 动态表单自定义类型

    下面以上传附件为例,讲述自定义表单类型的步骤和流程:

    a. 定义表单扩展类型

    
    ***public******class****FileFormType* ***extends**** AbstractFormType {*
    
    */***
    
    * **
    
    * */*
    
    ***private******static******final******long******serialVersionUID**** = 1L;*
    
    *@Override*
    
    ***public**** String getName() {*
    
    *//* ***TODO**** Auto-generated method stub*
    
    ***return****"file"**;*
    
    *}*
    
    *@Override*
    
    ***public****Object convertFormValueToModelValue(String* *propertyValue**) {*
    
    *//* ***TODO**** Auto-generated method stub*
    
    ***return****propertyValue**;*
    
    *}*
    
    *@Override*
    
    ***public****String convertModelValueToFormValue(Object* *modelValue**) {*
    
    *//* ***TODO**** Auto-generated method stub*
    
    ***return**** (String)**modelValue**;*
    
    *}*
    
    *}*
    

    b. 在activiti配置类中注册表单扩展类型

    *//注册自定义表单类型*
    
    *List<AbstractFormType>* *formTypes**=* ***new**** ArrayList<>();*
    
    *formTypes**.add(****new**** FileFormType());    **processEngineConfiguration**.setCustomFormTypes(**formTypes**);*
    

    c. 针对表单自定义类型新增动态渲染解析器

    *{{# if(v1.type.name=="file" ){ }}*
    
    *<div class="layui-form-item">*
    
    *<label class="layui-form-label">{{v1.name}}</label>*
    
    *<div>*
    
    *<button type="button" class="layui-btn layui-btn-normal" id="test8">选择文件</button>*
    
    *</div>*
    
    *</div>*
    
    *{{# } }}*
    

    d. 任务办理时判断是否包含附件类型,如果包含则应将表单类型设置为“multipart/form-data”:

    ***if****($(**"#test8"**)){*
    
    *     $form.attr(**'enctype'**,**'multipart/form-data'**);*
    
    *}*
    

    e. 在任务办理时新增附件的保存逻辑:

    首先接收参数应新增:

    @RequestParam(value="file", required=false) MultipartFile file

    在参数解析后,保存file:

    **if**(**null**!= file) {
    
    attachmentService.createAttachment(file, taskId, processInstanceId, formProperties.get("attachmentDescription"), request);        
    
    }
    

    4) 常用API总结

    引擎API是与Activiti打交道的最常用方式。 从ProcessEngine中,你可以获得很多囊括工作流/BPM方法的服务。 ProcessEngine和服务类都是线程安全的。 你可以在整个服务器中仅保持它们的一个引用就可以了。

    ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();    
    RuntimeService runtimeService = processEngine.getRuntimeService();  
    RepositoryService repositoryService = processEngine.getRepositoryService();  
    TaskService taskService = processEngine.getTaskService();  
    ManagementService managementService = processEngine.getManagementService();  
    IdentityService identityService = processEngine.getIdentityService();  
    HistoryService historyService = processEngine.getHistoryService();  
    FormService formService = processEngine.getFormService();
    

    ① ProcessEngines:

    ProcessEngines.getDefaultProcessEngine()会在第一次调用时 初始化并创建一个流程引擎,以后再调用就会返回相同的流程引擎。 使用对应的方法可以创建和关闭所有流程引擎:ProcessEngines.init()和 ProcessEngines.destroy()。

    ProcessEngines会扫描所有activiti.cfg.xml和 activiti-context.xml 文件。 对于activiti.cfg.xml文件,流程引擎会使用Activiti的经典方式构建: ProcessEngineConfiguration.createProcessEngineConfigurationFromInputStream(inputStream).buildProcessEngine(). 对于activiti-context.xml文件,流程引擎会使用Spring方法构建:先创建一个Spring的环境, 然后通过环境获得流程引擎。

    所有服务都是无状态的。这意味着可以在多节点集群环境下运行Activiti,每个节点都指向同一个数据库, 不用担心哪个机器实际执行前端的调用。 无论在哪里执行服务都没有问题。

    ② RepositoryService***:***

    RepositoryService可能是使用Activiti引擎时最先接触的服务。 它提供了管理和控制发布包和流程定义的操作。 这里不涉及太多细节,流程定义是BPMN 2.0流程的java实现。 它包含了一个流程每个环节的结构和行为。 发布包是Activiti引擎的打包单位。一个发布包可以包含多个BPMN 2.0 xml文件和其他资源。 开发者可以自由选择把任意资源包含到发布包中。 既可以把一个单独的BPMN 2.0 xml文件放到发布包里,也可以把整个流程和相关资源都放在一起。 (比如,'hr-processes'实例可以包含hr流程相关的任何资源)。 可以通过RepositoryService来部署这种发布包。 发布一个发布包,意味着把它上传到引擎中,所有流程都会在保存进数据库之前分析解析好。 从这点来说,系统知道这个发布包的存在,发布包中包含的流程就已经可以启动了。

    除此之外,服务可以

    ü 查询引擎中的发布包和流程定义。

    ü 暂停或激活发布包,对应全部和特定流程定义。 暂停意味着它们不能再执行任何操作了,激活是对应的反向操作。

    ü 获得多种资源,像是包含在发布包里的文件, 或引擎自动生成的流程图。

    ü 获得流程定义的pojo版本, 可以用来通过java解析流程,而不必通过xml。

    ③ RuntimeService***:***

    正如RepositoryService负责静态信息(比如,不会改变的数据,至少是不怎么改变的), RuntimeService正好是完全相反的。它负责启动一个流程定义的新实例。 如上所述,流程定义定义了流程各个节点的结构和行为。 流程实例就是这样一个流程定义的实例。对每个流程定义来说,同一时间会有很多实例在执行。 RuntimeService也可以用来获取和保存流程变量。 这些数据是特定于某个流程实例的,并会被很多流程中的节点使用 (比如,一个排他网关常常使用流程变量来决定选择哪条路径继续流程)。 Runtimeservice也能查询流程实例和执行。 执行对应BPMN 2.0中的'token'。基本上执行指向流程实例当前在哪里。 最后,RuntimeService可以在流程实例等待外部触发时使用,这时可以用来继续流程实例。 流程实例可以有很多暂停状态,而服务提供了多种方法来'触发'实例, 接受外部触发后,流程实例就会继续向下执行。

    ④ TaskService***:***

    任务是由系统中真实人员执行的,它是Activiti这类BPMN引擎的核心功能之一。 所有与任务有关的功能都包含在TaskService中:

    ü 查询分配给用户或组的任务

    ü 创建独立运行任务。这些任务与流程实例无关。

    ü 手工设置任务的执行者,或者这些用户通过何种方式与任务关联。

    ü 认领并完成一个任务。认领意味着一个人期望成为任务的执行者, 即这个用户会完成这个任务。完成意味着“做这个任务要求的事情”。 通常来说会有很多种处理形式。

    ⑤ IdentityService***:***

    IdentityService非常简单。它可以管理(创建,更新,删除,查询...)群组和用户。 请注意, Activiti执行时并没有对用户进行检查。 例如,任务可以分配给任何人,但是引擎不会校验系统中是否存在这个用户。 这是Activiti引擎也可以使用外部服务,比如ldap,活动目录,等等。

    ⑥ FormService***:***

    FormService是一个可选服务。即使不使用它,Activiti也可以完美运行, 不会损失任何功能。这个服务提供了启动表单任务表单两个概念。 启动表单会在流程实例启动之前展示给用户, 任务表单会在用户完成任务时展示。Activiti支持在BPMN 2.0流程定义中设置这些表单。 这个服务以一种简单的方式将数据暴露出来。再次重申,它时可选的, 表单也不一定要嵌入到流程定义中。

    ⑦ HistoryService***:***

    HistoryService提供了Activiti引擎手机的所有历史数据。 在执行流程时,引擎会保存很多数据(根据配置),比如流程实例启动时间,任务的参与者, 完成任务的时间,每个流程实例的执行路径,等等。 这个服务主要通过查询功能来获得这些数据。

    ⑧ ManagementService***:***

    ManagementService在使用Activiti的定制环境中基本上不会用到。 它可以查询数据库的表和表的元数据。另外,它提供了查询和管理异步操作的功能。 Activiti的异步操作用途很多,比如定时器,异步操作, 延迟暂停、激活,等等。后续,会讨论这些功能的更多细节。

    以下总结下在开发工作流引擎动态表单相关功能时用到的一些API:

    查询流程列表:

    ProcessDefinitionQuery dynamicQuery*=* repositoryService*.createProcessDefinitionQuery()*

    *.*orderByDeploymentId().desc();

    启动流程:

    identityService*.setAuthenticatedUserId(user.getId());*

    processInstance*=* formService*.submitStartFormData(processDefinitionId,* formProperties*);*

    读取启动节点表单数据:

    StartFormDataImpl*<u>startFormData</u>** = (StartFormDataImpl)* formService*.getStartFormData(processDefinitionId);*

    任务列表查询:

    TaskQuery taskQuery*=* taskService*.createTaskQuery()*

    .taskCandidateOrAssigned(user== null*?* "kafeitu"**:user.getId())

    .active().orderByTaskCreateTime().desc();

    签收任务:

    taskService*.claim(taskId,* userId*);*

    办理任务:

    identityService*.setAuthenticatedUserId(user.getId());*

    formService*.submitTaskFormData(taskId, formProperties);*

    读取Task表单数据:

    *TaskFormDataImpltaskFormData = (*TaskFormDataImpl)formService.getTaskFormData(taskId);

    运行中流程列表查询:

    ProcessInstanceQuery dynamicQuery*=* runtimeService*.createProcessInstanceQuery()*

    .orderByProcessInstanceId().desc();

    已结束流程列表查询:

    HistoricProcessInstanceQuery dynamicQuery*=* historyService*.createHistoricProcessInstanceQuery()**.finished().orderByProcessInstanceEndTime().desc();*

    挂起流程:

    repositoryService*.suspendProcessDefinitionById(processDefinitionId,* isCascade*,* new* Date());*

    激活流程:

    repositoryService*.activateProcessDefinitionById(processDefinitionId,* isCascade*,* new* Date());*

    删除流程:

    repositoryService*.deleteDeployment(deploymentId,* isCascade*);*

    挂起流程实例:

    runtimeService*.suspendProcessInstanceById(processInstanceId);*

    激活流程实例:

    runtimeService*.activateProcessInstanceById(processInstanceId);*

    删除流程实例:

    runtimeService*.deleteProcessInstance(processInstanceId,* deleteReason*);*

    关于Activiti的更多详细介绍,请参考以下资料:

    网址:http://www.mossle.com/docs/activiti/index.html#apiEngine

    书籍:Activiti实战

    转载于:https://my.oschina.net/u/180480/blog/2248973

    展开全文
  • 解压密码:RJ4587 之前我向大家分享过一款非常绚丽的CSS3发光表单,的确是很酷,大家可以先看...表单整体看上去很干净,特别的是,该CSS3联系表单有一个自定义的美化select表单,表单项在被激活的时候边框颜色会改变。
  • 解压密码:RJ4587 这款CSS3表单非常有特色,估计各位不太会看到过,这是一款CSS3环形特色表单,整个环形由一个个输入框组成,当用鼠标激活输入框焦点时,环形圈即会转到该表单项。我们也可以用tab键来逐个选择输入框...
  • 解压密码:RJ4587 之前我们分享过一款纯CSS3...今天我们要再来分享一款放光表单,是一款CSS3发光搜索表单表单整体是黑色的风格,搜索框被激活时,输入框就会发光,由于黑色的背景,整个搜索表单就显得格外绚丽光亮。
  • 激活 基本控制流模式 顺序流(Sequence) 并行分叉(Parallel Split) 同步(Synchronization) 独占式选择 简单聚合 综合实现   处理任务时的操作   可指派 是否可以由上一环节指定本环节的处理...

     

    工作流模式

    1. 抢占
    2. 会签
    3. 委托代办
    4. 催办
    5. 自由流
    6. 回退
    7. 取回
    8. 指派
    9. 前加签
    10. 后加签
    11. 改派
    12. 驳回
    13. 终止
    14. 挂起
    15. 激活

    基本控制流模式

    • 顺序流(Sequence)
    • 并行分叉(Parallel Split)
    • 同步(Synchronization)
    • 独占式选择
    • 简单聚合

    综合实现

     

    处理任务时的操作

     

    可指派   是否可以由上一环节指定本环节的处理人?
    可加签   是否可以在处理本环节任务时,添加其他人参与审批或任务处理?
    可驳回   是否可以拒绝处理本环节任务,并退回到指定的人或环节?
    可改派   是否允许处理人把本环节任务转移给其他人?
    会签     审批或任务分发给多人并行处理
    连岗审批 是否允许连岗审批

     

    每个工作流引擎都会支持多种方式的表单。目前大家讨论到的大概有三种,动态表单、外置表单、普通表单。

    一、动态表单

    这是程序员最喜欢的方式,同时也是客户最讨厌的……因为表单完全没有布局,所有的表单元素都是顺序输出显示在页面。

    此方式需要在流程定义文件(bpmn20.xml)中用activiti:formProperty属性定义,可以在开始事件(Start Event)和Task上设置,而且支持变量自动替换,语法就是UEL。

    注意:表单的内容都是以key和value的形式数据保存在引擎表中!!!

    动态表单采用activiti:formProperty定义,示例如下。

    <process id="leave-dynamic-from" name="请假流程-动态表单">
        <documentation>请假流程演示-动态表单</documentation>
        <startEvent id="startevent1" name="Start" activiti:initiator="applyUserId">
          <extensionElements>
            <activiti:formProperty id="startDate" name="请假开始日期" type="date" datePattern="yyyy-MM-dd" required="true" readable="true" writable="true"></activiti:formProperty>
            <activiti:formProperty id="endDate" name="请假结束日期" type="date" datePattern="yyyy-MM-dd" required="true" readable="true" writable="true"></activiti:formProperty>
            <activiti:formProperty id="reason" name="请假原因" type="string" required="true" readable="true" writable="true"></activiti:formProperty>
          </extensionElements>
        </startEvent>
        <userTask id="deptLeaderAudit" name="部门领导审批" activiti:candidateGroups="deptLeader">
          <extensionElements>
            <activiti:formProperty id="startDate" name="请假开始日期" type="date" value="${startDate}" datePattern="yyyy-MM-dd" readable="true" writable="false"></activiti:formProperty>
            <activiti:formProperty id="endDate" name="请假结束日期" type="date" value="${endDAte}" datePattern="yyyy-MM-dd" readable="true" writable="false"></activiti:formProperty>
            <activiti:formProperty id="reason" name="请假原因" type="string" value="${reason}" readable="true" writable="false"></activiti:formProperty>
            <activiti:formProperty id="deptLeaderPass" name="审批意见" type="enum" required="true" writable="true">
              <activiti:value id="true" name="同意"></activiti:value>
              <activiti:value id="false" name="不同意"></activiti:value>
            </activiti:formProperty>
          </extensionElements>
        </userTask>

    二、外置表单

    这种方式常用于基于工作流平台开发的方式,代码写的很少,开发人员只要把表单内容写好保存到.form文件中即可,然后配置每个节点需要的表单名称(form key),实际运行时通过引擎提供的API读取Task对应的form内容输出到页面。

    此种方式对于在经常添加新流程的需求比较适用,可以快速发布新流程,把流程设计出来之后再设计表单之后两者关联就可以使用了。例如公司内部各种简单的审批流程,没有业务逻辑处理,仅仅是多级审批是否通过等等情况

    当流程需要一些特殊处理时可以借助Listener或者Delegate方式实现。

    Activiti Explorer就是使用的这种方式,表单信息都配置在流程定义文件中。

    <process id="leave-formkey" name="请假流程-外置表单">
        <documentation>请假流程演示</documentation>
        <startEvent id="startevent1" name="Start" activiti:formKey="start.form" activiti:initiator="applyUserId"></startEvent>
        <userTask id="deptLeaderAudit" name="部门领导审批" activiti:candidateGroups="deptLeader" activiti:formKey="dept-leader-audit.form"></userTask>
        <exclusiveGateway id="exclusivegateway5" name="Exclusive Gateway"></exclusiveGateway>
        <userTask id="modifyApply" name="调整申请" activiti:assignee="${applyUserId}" activiti:formKey="modify-apply.form"></userTask>
        <userTask id="hrAudit" name="人事审批" activiti:candidateGroups="hr" activiti:formKey="hr-audit.form"></userTask>
        <exclusiveGateway id="exclusivegateway6" name="Exclusive Gateway"></exclusiveGateway>
        <userTask id="reportBack" name="销假" activiti:assignee="${applyUserId}" activiti:formKey="report-back.form"></userTask>
        <endEvent id="endevent1" name="End"></endEvent>

    三、普通表单

    这个是最灵活的一种方式,常用于业务比较复杂的系统中,或者业务比较固定不变的需求中,例如ERP系统。

    普通表单的特点是把表单的内容存放在一个页面(jsp、jsf、html等)文件中,存放方式也有两种(一体式、分离式):

    1.一体式:把整个流程涉及到的表单放在一个文件然后根据处理的任务名称匹配显示,kft-activiti-demo的普通表单模式就是一体式的做法,把表单内容封装在一个div里面,div的ID以节点的名称命名,点击“办理”按钮时用对话框的方式把div的内容显示给用户。

    2.分离式:对于非Ajax应用来说比较常用,每个任务对应一个页面文件,点击办理的时候根据任务的ID动态指定表单页面。

    参见Activiti入门Demo演示:Activiti快速入门项目-kft-activiti-demo

    和以上两种方式比较有两点区别:

    1. 表单:和第二种外置表单类似,但是表单的显示、表单字段值填充均由开发人员写代码实现。
    2. 数据表:数据表单独设计而不是和前两种一样把数据以key、value形式保存在引擎表中。

    从业务数据和流程关联比较

    1. 动态表单:引擎已经自动绑定在一起了,不需要额外配置。
    2. 外置表单:和业务关联是可选的,提供的例子中是没有和业务关联的,如果需要关联只需要在提交StartForm的时候设置businessKey即可。
    3. 普通表单:这个应该是必须和业务关联,否则就是无头苍蝇了……,关联方式可以参考:工作流引擎Activiti使用总结中的2.3 业务和流程的关联方式
    展开全文
  • HTML表单

    千次阅读 2018-08-04 12:59:52
    HTML表单(form) 表单作用 :与用户交互,把用户输入的数据传送到服务器端,...表单包含内容:表单控件 提示信息 表单域 form标签用于定义表单域: < form method = " post / get " action = “ ...”> ...
  • 基于其他表单字段值动态启用和禁用文本输入的自动完成的示例 最常见的是,自动完成是为 Django 表单定义中的特定表单字段定义的。 我需要将字段的值设置为特定值,或使用自动完成功能,或允许基于其他因素的自由...
  • 重力表单HTML 5占位符 该插件将HTML 5占位符支持添加到Gravity表单中。 只需启用插​​件并将文本添加到字段编辑器中的“占位符文本”字段即可。 积分(应交):
  • 后来发现是动态添加的元素没用绑定form事件。只需再添加完元素后 再加一句 Form.api.bindevent($("form[role=form]"));就可以了。 $('form').append(dom); Form.api.bindevent($("form[r...
  • 这款CSS3表单非常有特色,估计各位不太会看到过,这是一款CSS3环形特色表单,整个环形由一个个输入框组成,当用鼠标激活输入框焦点时,环形圈即会转到该表单项。我们也可以用tab键来逐个选择输入框来输入表单内容。 ...
  • JavaScript 表单脚本

    2016-07-23 17:18:40
    理解表单 JavaScript最初的一个应用,就是分担服务器处理表单的责任,打破处处依赖服务器的局面,现在表单已经成为开发Web程序必不可少的一部分了,可是我们又对它了解多少啦,下面一起来了解一下表单表单的...
  • 表单设计】优秀表单设计案例

    千次阅读 2015-07-29 10:05:56
    表单设计二三事-20150725早读课 时间: 2015-07-25 / 编辑:早读课 / 分类:交互体验,产品设计 / 451 人看过 / 暂无评论 发表评论 来源:24Says 微信ID:iiiv-says 更多详情请联系 @UEDetail 在这个被...
  • 解压密码:RJ4587 我们在网页上经常会用到一些联系表单或者留言表单,不久前我们也分享过一款外观清新的Select联系表单,今天我们要分享一款背景透明...表单输入框被激活时输入框也呈现透明的效果,非常具有立体感。
  • HTML登录表单

    千次阅读 2017-11-28 16:22:54
    随着现代浏览器的发展,我们就可以用HTML5和CSS3来制作炫酷的注册登录表单,下面我们收集了9款大气实用的HTML5/CSS3注册登录表单,希望你会喜欢...利用CSS3制作的登录表单的确很漂亮,我们在html5tricks网站上也分享过
  • Wordpress 网站设计入门 联系表单设计

    千次阅读 2018-08-08 09:32:17
    在网站设计中,联系人表单是信息反馈和收集的一个重要部分。在Wordpress 网站设计中联系表单设计有好几种,我选择了 Contact Form 7。 1: 插件安装 在控制面板Dashboard 中 : Plugins-&gt;Add New 在新加...
  • 为了更好的互动,更好的与访客朋友们联系,你是否想过为自己的博客添加留言板功能,在自己的博客中添加联系表单功能,如果自己写的话,如果你的CSS功底比较不错,写过具有这样功能的表单没有什么问题,如果你CSS学的...
  • 激活函数作用

    千次阅读 2018-01-08 17:45:46
    激活函数作用在神经网络结构中,通过激活函数将非线性的特性引入到神经网络中,从而让神经网络可以理解、处理复杂的数据及问题。
  • SharePoint Infopath表单服务-PowerShell

    千次阅读 2013-07-02 19:21:13
    1. 激活可浏览的表单模板 Set-SPInfoPathFormsService -AllowUserFormBrowserEnabling $true -AllowUserFormBrowserRendering $true 2. 设置数据连接超时 Set-SPInfoPathFormsService –...
  • 纯CSS3发光登录表单

    2019-08-23 17:15:03
    当鼠标激活表单时,表单周围就会出现颜色渐变的发光动画,配合黑色的背景,整个CSS3表单显得格外亮丽。更重要的是,这个CSS3发光表单基本用CSS3实现,很少的JS代码,在CSS3引擎上优化非常明显。
  • 标签 定义整体的表单区域 action属性 定义表单数据提交地址 method属性 定义表单提交方式,一般为get或post方式 <label>标签 为表单元素定义文字标注 for属性:提高用户体验度,点击label中的内容自动激活...
  • Flask web表单

    千次阅读 2016-07-31 13:52:51
    为了能够处理 web 表单,我们将使用 Flask-WTF ,该扩展封装了 WTForms 并且恰当地集成进 Flask 中。 许多 Flask 扩展需要大量的配置,因此我们将要在 microblog 文件夹的根目录下创建一个配置文件以至于...
  • 表单标签:表单的作用是收集信息 (1)属性 action="URL":一个URL地址,指定form表单向何处发送数据 name:给表单定一个名字 enctype=“string”:规定表单数据以什么形式进行编码 method="get/post":以何种...
  • 表单onsubmit return false 无效

    千次阅读 2017-06-11 16:56:10
    今天,在做表单任务时,想要当密码为空时弹出密码为空的提示框,但是用onSubmit,页面依然跳转,查了资料...在通过按钮来启动submit,是网络上最常用的方法,原理是通过按钮click事件去激活submit事件。这里你就会发现,

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 41,763
精华内容 16,705
关键字:

表单动态激活