如何使用Angular输出动态表单字段?



我想呈现一个基于动态字段配置的表单:

$scope.fields = [
    { title: 'Label 1', type: 'text', value: 'value1'},
    { title: 'Label 2', type: 'textarea', value: 'value2'}
];

这应该输出如下行为:

<div>
    <label>{{field.title}}<br />
        <input type="text" ng-model="field.value"/>
    </label>
</div>
<div>
    <label>{{field.title}}<br />
        <textarea ng-model="field.value" rows="5" cols="50"></textarea>
    </label>
</div>

简单的实现是使用if语句来呈现每个字段类型的模板。然而,由于Angular不支持if语句,所以我选择了指令的方向。我的问题是理解数据绑定是如何工作的。指令的文档有点密集和理论性。

我在这里模拟了一个简单的例子:http://jsfiddle.net/gunnarlium/aj8G3/4/

问题是表单字段没有绑定到模型,所以$作用域。submit()中的字段没有更新。我怀疑我的指令函数的内容是完全错误的…:)

接下来,我还需要支持其他字段类型,如单选按钮、复选框、选择等

无论您试图创建的指令如何,您遇到的第一个问题是在具有表单元素的表单中使用ng-repeat。ng-repeat如何创建一个新的作用域是很棘手的。

这个指令创建一个新的作用域。

我还建议你在部分模板中使用ngSwitch而不是element.html。

<div class="form-row" data-ng-switch on="field.type">
    <div data-ng-switch-when="text">
        {{ field.title }}: <input type="text" data-ng-model="field.value" />
    </div>
    <div data-ng-switch-when="textarea">
        {{ field.title }}: <textarea data-ng-model="field.value"></textarea>
    </div>
</div>

这仍然给你留下了由于ng-repeat而在子范围内修改表单元素的问题,因此我建议在每个元素上使用ngChange方法来设置项更改时的值。这是我认为AngularJS目前处理得不是很好的少数几个项目之一。

您可以考虑为此使用Metawidget。它使用JSON模式,但在其他方面非常接近您的用例。完整的示例:

<html ng-app="myApp">
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js" type="text/javascript"></script>
        <script src="http://metawidget.org/js/3.5/metawidget-core.min.js" type="text/javascript"></script>
        <script src="http://metawidget.org/js/3.5/metawidget-angular.min.js" type="text/javascript"></script>
        <script type="text/javascript">
            angular.module( 'myApp', [ 'metawidget' ] )
                .controller( 'myController', function( $scope ) {
                    $scope.metawidgetConfig = {
                        inspector: function() {
                            return {
                                properties: {
                                    label1: {
                                        type: 'string'
                                    },
                                    label2: {
                                        type: 'string',
                                        large: true
                                    }
                                }
                            }
                        }
                    }
                    $scope.saveTo = {
                        label1: 'value1',
                        label2: 'value2'
                    }
                    $scope.save = function() {
                        console.log( $scope.saveTo );
                    }
                } );
        </script>
    </head>
    <body ng-controller="myController">
        <metawidget ng-model="saveTo" config="metawidgetConfig">
        </metawidget>
        <button ng-click="save()">Save</button>
    </body>
</html>

type属性可以改变时,元素是DOM,所以为什么不一个小的指令,从DOM中删除它,改变它的类型,然后添加回同一个地方?

$watch是可选的,因为目标可以动态地改变它一次,而不是不断地改变它。

var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
  $scope.rangeType = 'range';
  $scope.newType = 'date'
});
app.directive('dynamicInput', function(){
    return {
      restrict: "A",
      link: linkFunction
    };
    
    function linkFunction($scope, $element, $attrs){
      
      if($attrs.watch){
        $scope.$watch(function(){ return $attrs.dynamicInput; }, function(newValue){
          changeType(newValue);
        })
      }
      else
        changeType($attrs.dynamicInput);
      
      
      function changeType(type){
        var prev = $element[0].previousSibling;
        var parent = $element.parent();
        $element.remove().attr('type', type);
        if(prev)
          angular.element(prev).after($element);
        else
          parent.append($element);
      }
    }
});
span {
font-size: .7em;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="MainCtrl">
    <h2>Watching Type Change</h2>
    Enter Type: <input ng-model="newType" /><br/>
    Using Type (with siblings): <span>Before</span><input dynamic-input="{{newType}}" watch="true" /><span>After</span><Br>
    Using Type (without siblings): <div><input dynamic-input="{{newType}}" watch="true" /></div>
    
    <br/><br/><br/>
    <h2>Without Watch</h3>
    Checkbox: <input dynamic-input="checkbox" /><br />
    Password: <input dynamic-input="{{ 'password' }}" value="password"/><br />
    Radio: <input dynamic-input="radio"  /><br/>
    Range: <input dynamic-input="{{ rangeType }}" />
</div>

在最新版Chrome和IE11中测试

最新更新