之前为F.Grid做了扩展,自动生成了三个按钮,并和本身的window控件联动弹出编辑窗口。编辑窗口以F.Form为主,这次我把F.Form控件增加扩展,主要用于绑定数据,拿取数据,自动验证并提交,以及简单的布局。

导言

之前为F.Grid做了扩展,自动生成了三个按钮,并和本身的window控件联动弹出编辑窗口。编辑窗口以F.Form为主,这次我把F.Form控件增加扩展,主要用于绑定数据,拿取数据,自动验证并提交,以及简单的布局。

1. 为F.Form绑定数据(字段赋值)

FineUICore本身有model和表单的对照demo,就是MVC的传统写法,通过用模型绑定控件,但是我项目里的Model是没有特性的,就是传统的类

    /// <summary>
    /// ID
    /// </summary>
    public int Id { get; set; }

所以如果为表单控件赋值就要写成 ID.Text = ms.XXX;,如果能实现自动匹配映射是很减少工作量的。

首先要在js为F.Form扩展 setValue 和 getValue 方法,将Model的Json格式绑定到表单的各个控件,然后在取值返回Json格式,传到后台(.cs)再转为Model进行保存操作。

这种模式绑定和读取数据很像Grid的操作,Grid中有两个关键属性,DataIDField 和 DataField 属性,一个是标识字段名,一个是绑定的字段名,如果我为F.Form()(.cshtml)扩展这两个属性

    F.Form().Width(900).LabelWidth(100).BodyPadding(5).EnableCollapse(true)
     .ID("Form1").Title("表单1")
     .DataIDField("Id")//数据标识字段名

表单项

    F.TextBox().Label("姓名").DataField("Name")//字段名称
    F.DropDownList().Label("性别").DataField("Gender")//字段名称

那么js怎么获取到我绑定的 Id,Name,Gender 呢,这里用到了F自带的为HTML标签扩展的属性 Attribute ,该属性在 ControlBaseExtension 下,所有控件继承,该方法直接为HTML标签生成一个属性,js通过 attr 很容易就拿到了,把这个属性拿到,然后直接和JSON匹配就行了。

点击F.Form(),按F12,

    //
    // 摘要:
    //     表单面板控件扩展
    public FormExtension Form();

FineUIEx类中扩展增加方法 DataIDField

    /// <summary>
    /// 数据标识字段名
    /// </summary>
    /// <param name="f">控件实例</param>
    /// <param name="DataIDField">主键字段</param>
    /// <returns></returns>
    public static FormExtension DataIDField(this FormExtension f, string DataIDField)
    {
        //为Form增加属性
        f.Attribute("DataID", DataIDField);
        return f;
    }

字段的DataField,在Form的FormRow.Items中参数为IControlBaseExtension[]

    //
    // 摘要:
    //     子控件集合
    //
    // 参数:
    //   extensions:
    public FormRowExtension Items(params IControlBaseExtension[] extensions);

如果将DataField扩展到IControlBaseExtension中,会有问题,所有控件都继承IControlBaseExtension,但是我只需要输入的控件 比如 TextBox 或 DropDownList ,又不能每个控件都单独扩展,所以找到所有文本输入控件都继承的类 FieldExtension

//
// 摘要:
//     表单字段基类扩展
//
// 类型参数:
//   T:
//
//   TExtension:
public abstract class FieldExtension

这里还要注意,DataField 方法返回的是当前实例,即谁继承FieldExtension就返回谁(比如F.DropDownList()有DataSource方法而F.TextBox()没有,如果返回统一类型将无法继承该控件实例的单独属性)。

FineUICore自己就解决了这个问题,比如 Label 方法,就在FieldExtension下,所有表单控件继承,

    //
    // 摘要:
    //     标签文本
    //
    // 参数:
    //   param:
    public TExtension Label(string param);

注意该方法的返回类型是TExtension,泛型,哪来的呢,上层传过来的,比如 DropDownList控件,

//
// 摘要:
//     下拉列表控件扩展
public class DropDownListExtension : TextFieldExtension<DropDownList, DropDownListExtension>

看<DropDownList, DropDownListExtension>,他已经把自己的类型,通过T参数传到他的继承类中了,所以Label后还可以返回当前实例,落在表单的DataField中,应该这么写

    /// <summary>
    /// 绑定数据字段
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TExtension"></typeparam>
    /// <param name="f"></param>
    /// <param name="datafield"></param>
    /// <returns></returns>
    public static TExtension DataField<T, TExtension>(this FieldExtension<T, TExtension> f,string datafield) 
        where T : Field
        where TExtension : FieldExtension<T, TExtension>
    {
        f.Attribute("data", datafield);
        return (TExtension)f;
    }

到此对.cshtml的扩展就结束了,下面是js的扩展,主要是用attr方法,查找 DataID 和 data 属性

if (F.Form) {
    $.extend(F.Form.prototype, {
        //得到数据 (是否验证)
        getdata: function (isv) {
            var t = this;
            var data = t.initData || {};
            if (isv) {
                if (!F.validateForm(t.id, '_self', false, false)) {
                    return false;
                }
            }
            var datael = $(t.el).find(".f-field[data]");
            //循环表单项
            $.each(datael, function (i, v) {
                var id = $(v).attr("id");
                var d = $(v).attr("data").replace("Data_", "");
                //如果存在data属性 
                if (d) {
                    //取值
                    data[d] = F.ui[id].getValue();
                    //注意日期控件 取text,否则会提取出时间戳
                    if (F.ui[id].type == 'datepicker') {
                        data[d] = F.ui[id].getText();
                    }
                }
            })
            //返回json
            return data;
        },
        //设置数据
        setdata: function (data) {
            var t = this;
            t.initData = data;
            var datael = $(t.el).find(".f-field[data]");
            //绑定dataid
            var dataid = $(t.el).attr("DataID");
            if (dataid && dataid != '') {
                F.ui[t.id].beforDataID = F.ui[t.id].DataID;
                F.ui[t.id].DataID = data[dataid];
            };
            //循环表单项
            $.each(datael, function (i, v) {
                var id = $(v).attr("id");
                var d = $(v).attr("data").replace("Data_", "");
                if (d && (typeof data[d]) != 'undefined') {
                    //找到data属性,并赋值
                    F.ui[id].setValue(data[d]);
                }
            })
        }
    });
}

扩展完成,使用时可以直接这样

表单控件(.cshtml)

    F.Form().Width(900).LabelWidth(100).BodyPadding(5).EnableCollapse(true)
     .ID("Form1").Title("表单1")
     .DataIDField("Id")

表单项(.cshtml)

    F.TextBox().Label("姓名").DataField("Name").Requ(true),
    F.DropDownList().Label("性别").DataField("Gend.Required(true)
     .Items(
        F.ListItem().Text("男").Value("1"),
        F.ListItem().Text("女").Value("0")
        ),
    F.TextBox().Label("入学年份").DataField("EntranceYear"),
    F.DropDownList().Label("是否在校").DataField("AtSchool")
     .Items(
        F.ListItem().Text("是").Value("1"),
        F.ListItem().Text("否").Value("0")
        ),
    F.TextBox().Label("所学专业").DataField("Major"),
    F.TextBox().Label("分组").DataField("Group")

js(.cshtml)

    F.ready(function () {
        F.ui['Form1'].setdata(@Html.Raw(ViewBag.data));
    })

后台.cs

    public ActionResult Index()
    {
        MsStudent ms1 = new MsStudent();
        //得到实体数据
        ms1 = studentHelper.GetMsById(Convert.ToInt32(101));
        ViewBag.data = JObject.FromObject(ms1).ToString();
        return View();
    }

具体效果,可以在FineUIMVC扩展中查看。