hazaarlabs/hazaar-forms

该软件包已被 弃用 且不再维护。作者建议使用hazaarlabs/hazaar-forms软件包。

用于 MVC 框架的动态表单

安装量: 8,265

依赖项: 0

建议者: 0

安全: 0

语言:JavaScript


README

Hazaar 表单模块能够简化构建高级动态在线表单。

详细信息将很快提供。目前,这里是对使用表单的快速概述。

表单定义

表单定义正是如此。它们是定义您的表单字段及其操作方式的 JSON。这包括页面、部分、字段、验证等。

准备您的应用程序

现在正在使用一个新的应用程序子目录,称为 'forms'。因此,创建目录 {project_base}\application\forms 以存储您的表单定义。

创建一个简单的表单定义

这是一个开始使用的方法。文档将很快提供。

{
  "name": "Testing and Development Form #1",
  "pages": [
    {
      "sections": [
        {
          "label": "General Information",
          "fields": [
            {
              "name": "name",
              "required": true
            },
            "gender",
            "dob",
            "married"
          ]
        }
      ]
    },
    {
      "label": "Addresses",
      "sections": [
        {
          "label": "Home",
          "fields": [ "address1", "address2", "city", "country", "state" ]
        }
      ]
    }
  ],
  "fields": {
    "name": {
      "type": "text",
      "label": "Name",
      "placeholder": "Type your full name here...",
      "prefix": "Prefix",
      "suffix": "Suffix"
    },
    "gender": {
      "type": "select",
      "label": "Gender",
      "placeholder": "Please choose your gender...",
      "options": {
        "male": "Male",
        "female": "Female"
      }
    },
    "dob": {
      "type": "date",
      "label": "Date of Birth"
    },
    "country": {
      "type": "select",
      "label": "Country",
      "options": {
        "aus": "Australia",
        "nz": "New Zealand"
      }
    },
    "address1": {
      "type": "text",
      "label": "Address 1"
    },
    "address2": {
      "type": "text",
      "label": "Address 2"
    },
    "city": {
      "type": "text",
      "label": "City / Suburb"
    },
    "state": {
      "type": "select",
      "label": "State",
      "options": {
        "nsw": "New South Wales",
        "act": "Australian Captial Territory"
      }
    },
    "married": {
      "type": "boolean",
      "label": "Married"
    }
  }
}

目前只有 3 个必需元素。 namepagesfields

  • name - 这是一个友好的标签,可以在表单布局中显示
  • pages - 实际的页面定义。每个页面包含一个或多个部分。每个部分包含一个或多个字段。字段可以通过名称或对象声明来包含。
  • fields - 定义您的表单中所有可用的字段。字段可以定义一次并在表单中多次使用。字段定义具有名称、类型、标签和一些类型相关的属性。

表单控制器

您的应用程序需要一个表单控制器来处理与表单前端的数据通信。用于此目的的 Hazaar\Controller\Forms 类基本上是一个您每天使用的 Hazaar\Controller\Action 类,但带有一些额外的有助于获取和传输数据的方法。

这里有一个我提前准备好的

<?php

class IndexController extends \Hazaar\Controller\Form {

    private $cache;

    /*
     * Initialise all the bits we need.  We call these in init so that they are
     * only called in a single place.  The cache object is used to store form 
     * data for this test.  The form() method call is required to declare which
     * form definition we are going to use.
     */
    protected function init() {

        $this->cache = new \Hazaar\Cache('file', array('use_pragma' => false));

        $this->form('test1', array('id' => intval($this->request->get('id', 1))));

    }

    /*
     * This is just like any other action defined in \Hazaar\Controller\Action
     * Here we add the GUI view helper, which gives us a cool popup box to use,
     * include out application.js script and set the view to use.
     */
    public function index() {

        $this->view->addHelper('gui');

        $this->view->requires('application.js');

        $this->view('index');

    }

    /*
     * This is a simple example of a save method.  Here all we do is take the
     * form data and dump it into a cache object.  A more advanced use would
     * be to process the form data and store it in a database
     *
     * $params is the second argument from out forms() call in the init() 
     * method.  This can be used to pass on a unique ID to identify the form
     * data.
     */
    public function save($model, $params = array()){

        $this->cache->set('form-' . ake($params, 'id', 0), $model->get());

    }

    /*
     * The load method is basically the reverse of the save method.  In this
     * example we from the form data out of the cache object.  If it doesn't
     * exist we are nice and return an empty array.
     */
    public function load($params = array()){

        if(!($out = $this->cache->get('form-' . ake($params, 'id', 0))))
            $out = array();

        return $out;

    }
}

注意事项

  • init()dex() 定义在 Action 类中,因此工作方式与正常一样。
  • save()load() 是简单的方法,是必需的。这些方法获取表单模块中的数据并将其传输出去,允许您的应用程序在它想要的地方存储和检索实际的表单数据。在这个例子中,我们只是将其缓存到一个文件中。
  • $this->form('formname') 的调用是必需的。这通常在 init 方法中完成,尽管这可能会改变,并且只是告诉表单模块我们正在使用哪个表单。这也给您的应用程序一个机会来定义一些额外的参数,这些参数将在每个 GET/POST 调用中来回传递。这通常用于记录 ID 或表单数据标识符。

现在我们只需要一个视图,一切就会工作,但我们将添加一些 JavaScript 来使一切正常。

表单视图

视图本身可以非常简单。然而,为了充分利用我们的表单,我们可以将 DOM 元素绑定到表单数据,并使其动态更新。

<div class="row">
    <div class="col-xs-9">
        <div class="panel panel-default">
            <div class="panel-heading">
                <div id="formPage" class="pull-right"></div>
                <div id="formStatus">Initialising...</div>
            </div>
            <div class="panel-body" style="position: relative;">
                <div class="well" id="frmTest">
                    <?=$this->layout();?>
                </div>
            </div>
            <div class="panel-footer">
                <button id="btnPrev" class="btn btn-default">Previous</button>
                <button id="btnNext" class="btn btn-default pull-right">Continue</button>
            </div>
        </div>
    </div>
    <div class="col-xs-3">
        <div class="panel panel-default">
            <div class="panel-heading">
                <div id="formStatus">Summary</div>
            </div>
            <div class="panel-body">
                <div class="form-group">
                    <label class="control-label">Name</label>
                    <div data-bind="name"></div>
                </div>
                <div class="form-group">
                    <label class="control-label">Gender</label>
                    <div data-bind="gender"></div>
                </div>
                <div class="form-group">
                    <label class="control-label">DOB</label>
                    <div data-bind="dob"></div>
                </div>
            </div>
        </div>
        <div id="saveButtons">
            <button class="btn btn-default">Save</button>
            <button class="btn btn-success" data-submit="true">Submit</button>
        </div>
    </div>
</div>

真正重要的是调用 $this->layout(),这实际上告诉表单控制器将表单放在哪里。

额外的 JavaScript

为了导航表单,我们需要添加一些额外的 JavaScript。决定内置导航虽然使事物更加自我包含,但不会允许非常好的视图集成。

$(document).ready(function () {
    $('#frmTest').children('form').on('ready', function (e, def) {
        $('#formStatus').html(def.name);
    }).on('nav', function (e, page, pages) {
        $('#formPage').html('Page ' + page + ' of ' + pages);
    }).on('data', function (e, data) {
        //Data is loaded so do stuff!
    }).on('saved', function (e, data) {
        var popupOps = { title: "Success", icon: "success", buttons: [{ label: 'OK', "class": "btn btn-default" }] };
        if (data.submit === true)
            $('<div>').html('Form data has been submitted successfully!').popup(popupOps);
        else
            $('<div>').html('Form data has been saved successfully!').popup(popupOps);
    }).on('error', function (e, error) {
        $('<div>').html([
            $('<div>').html(error.str),
            $('<div>').html([$('<strong>').html('Line: '), $('<span>').html(error.line)]),
            $('<div>').html([$('<strong>').html('File: '), $('<span>').html(error.file)])
        ]).popup({ title: 'Form error', buttons: [{ label: 'OK', "class": "btn btn-default" }], icon: 'error' });
    });
    $('#btnPrev').click(function () {
        $('#frmTest').children('form').form('prev');
    });
    $('#btnNext').click(function () {
        $('#frmTest').children('form').form('next');
    });
    $('#saveButtons').children('button').click(function (e) {
        var submit = ($(e.target).attr('data-submit') == 'true');
        if ($(e.target).attr('data-submit') == 'true') {
            $('<div>').html('Are you sure you want to submit this form?').popup({
                title: "Confirm Submission",
                icon: "question",
                buttons: [
                    {
                        "label": "OK",
                        "class": "btn btn-success",
                        "action": function () {
                            $('#frmTest').children('form').form('save', false, { submit: true });
                            $(this).popup('close');
                        }
                    },
                    {
                        "label": "Cancel",
                        "class": "btn btn-default"
                    }
                ]
            });
        } else {
            $('#frmTest').children('form').form('save', false);
        }
    });
});

这段代码将处理一些按钮的点击事件,这些按钮允许我们保存和提交表单。保存是默认方法,而提交会弹出一个确认框,并发送一些额外的数据来表示我们正在提交。我们可以在表单控制器的save()方法中随意处理这些数据。在这个例子中,我们的想法是可以边保存表单边提交,然后在最后导航页面离开或执行其他操作。

自定义输入

使用jQuery可以创建一个完全自定义的输入。添加自定义输入是一个高级功能,如果你选择走这条路,需要考虑一些事情。

  • 你的代码负责渲染整个字段,包括标签。
  • 你的代码负责处理事件,如onchange或onkeypress。
  • 你的代码必须返回一个jQuery对象容器。
  • 如果你想使输入能够正确与MVVM数据绑定器交互,你需要记得将data-bind属性添加到实际的输入中(请参见下面的示例2)。

可用变量

你的函数中有两个全局变量可用。

  • field包含字段定义,包括字段的当前值。这至少包含namevalue属性。其余的是你在JSON字段定义中定义的内容。
  • form是表单数据对象。你可以通过直接修改这个dataBinder对象来访问表单数据。

示例1 - 简单文本输入

下面是如何创建自定义输入的示例。这将生成一个没有样式和标签的文本输入,完全在JSON字段定义文件中定义。

{
    "pages": [],
    "fields": {
        "custom": {
            "type": "text",
            "label": "A Simple Custom Input",
            "render": "return $('<input type="text">').val(field.value);"
        }      
    }
}
  

注意 - 请记住,上面的示例实际上不会做任何事情,因为我们没有处理任何更新表单数据的onChange事件。

示例2 - 较复杂的文本输入

这段代码创建了一个稍微复杂一点的文本输入,类似于内置的文本输入生成器。这个自定义输入定义在应用javascript的某个地方,作为一个函数调用,必须可访问并包含在表单控制器中(在控制器中,通常使用$this->require('yourscript.js');)。

function myCustomInput(field, form) {
    var group = $('<div class="form-group">');
    $('<label class="control-label">').attr('for', field.name).html(field.label).appendTo(group);
    $('<input type="text" placeholder="custom input" class="form-control">')
        .attr('data-bind', field.name)
        .val(field.value)
        .appendTo(group)
        .change(function () {
            form.data[field.name] = $(this).val();
        });
    return group;
};

然后我们可以使用与示例1类似的JSON字段定义,只是我们调用我们的函数并传递fieldform变量。

{
    "pages": [],
    "fields": {
        "custom": {
            "type": "text",
            "label": "A Simple Custom Input",
            "render": "return myCustomInput(field, form);"
        }      
    }
}
  

结论

就这些。这个模块非常新,仍在开发中,所以如果事情变化很大,请不要感到惊讶。