2013-03-08 14 views
11

Voglio rendere un form, sulla base di una configurazione di campo dinamico:Come posso utilizzare Angolare per generare campi modulo dinamici?

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

Questo dovrebbe produrre qualcosa che si comporta come:

<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> 

La semplice implementazione sarebbe quella di utilizzare if dichiarazioni per rendere la modelli per ogni tipo di campo. Tuttavia, poiché Angular non supporta le dichiarazioni if, sono guidato alla direzione delle direttive. Il mio problema è capire come funziona il binding dei dati. Il documentation for directives è un po 'denso e teorico.

ho preso in giro un esempio ossa nude di quello che cerco di fare qui: http://jsfiddle.net/gunnarlium/aj8G3/4/

Il problema è che i campi del modulo non sono vincolati al modello, in modo che il $ scope.fields a presentare () non è aggiornato. Ho il sospetto che il contenuto della mia funzione direttiva è del tutto sbagliato ... :)

Andando avanti, ho bisogno di sostenere anche altri tipi di campo, come pulsanti di opzione, caselle di controllo, seleziona, ecc

+0

Possibile duplicato di [AngularJS: impossibile modificare il tipo di input] (http://stackoverflow.com/questions/15148456/angularjs-cant-change-input-type) – Aides

risposta

22

Il primo problema si sta eseguendo indipendentemente dalla direttiva che si sta tentando di creare utilizzando ng-repeat all'interno di un modulo con elementi del modulo. Può essere complicato fare come ng-repeat crea un nuovo scope.

This directive creates new scope.

Ho anche consigliamo invece di utilizzare element.html utilizzare ngSwitch invece in un modello parziale.

<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> 

Questo ti lascia ancora con il problema di modificare gli elementi del modulo in ambito bambino a causa di ng-repeat e per questo io suggerisco di usare il metodo ngChange su ogni elemento per impostare il valore quando un elemento è cambiato. Questo è uno dei pochi elementi che non penso che AngularJS funzioni molto bene in questo momento.

+0

In realtà, ngSwitch era esattamente ciò di cui avevo bisogno. Nessun problema di ambito riscontrato :) (ancora). Ancora non capisco le direttive, ma è un compito per un altro giorno .. –

+0

@GunnarLium Sei riuscito a risolvere questo problema? Sto cercando di implementare una soluzione simile ma sono rimasto bloccato a causa degli stessi problemi esatti ..Ti chiedi se puoi condividere i tuoi approfondimenti se/come hai risolto il problema – user6123723

4

Per questo è possibile considerare Metawidget. Utilizza lo schema JSON, ma è altrimenti molto vicino al tuo caso d'uso. campione completo:

<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> 
0

L'attributo type può essere modificato quando l'elemento è fuori di DOM, quindi perché non una piccola direttiva di rimozione dal DOM, lo cambia tipo e quindi aggiungere di nuovo nello stesso posto?

Il $watch è opzionale, poiché l'obiettivo può essere modificato dinamicamente una volta e non continuare a modificarlo.

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>

testato in ultima Chrome e IE11.

Problemi correlati