Как я могу использовать 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/
Проблема заключается в том, что поля формы не привязаны к модели, поэтому поля $scope.field в submit() не обновляются. Я подозреваю, что содержание моей директивной функции совершенно неверно...:)
Вперёд, мне нужно также поддерживать другие типы полей, такие как переключатели, флажки, флажки и т.д.
Ответы
Ответ 1
Первая проблема, с которой вы работаете, независимо от директивы, которую вы пытаетесь создать, - это использовать ng-repeat в форме с элементами формы. Это может быть сложно сделать, как ng-repeat создает новую область.
Эта директива создает новую область действия.
Я также рекомендую вместо использования 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 отлично справляется в это время.
Ответ 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>