【问题标题】:How can I use Angular to output dynamic form fields?如何使用 Angular 输出动态表单字段?
【发布时间】:2013-03-08 15:01:29
【问题描述】:

我想根据动态字段配置呈现一个表单:

$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 语句,我将引导到指令的方向。我的问题是了解数据绑定的工作原理。 documentation for directives 有点密集和理论化。

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

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

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

【问题讨论】:

标签: angularjs


【解决方案1】:

无论您尝试创建什么指令,您遇到的第一个问题是在带有表单元素的表单中使用 ng-repeat。 ng-repeat 如何创建新范围可能会很棘手。

This directive creates new scope.

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

<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 目前不能很好处理的少数项目之一。

【讨论】:

  • 其实,ngSwitch 正是我所需要的。没有遇到范围问题:)(还)。我仍然不太了解指令,但这是另一天的任务..
  • @GunnarLium 你能解决这个问题吗?我正在尝试实施类似的解决方案,但由于完全相同的问题而陷入困境。想知道如果/如何解决问题,您是否可以分享您的见解
【解决方案2】:

您可以为此考虑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>

【讨论】:

    【解决方案3】:

    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 中测试。

    【讨论】:

      猜你喜欢
      • 2017-05-06
      • 1970-01-01
      • 2019-04-15
      • 1970-01-01
      • 1970-01-01
      • 2021-05-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多