¿Cómo puedo establecer un nombre de modelo dinámico en AngularJS?

91

Quiero llenar un formulario con algunas preguntas dinámicas (violín aquí ):

<div ng-app ng-controller="QuestionController">
    <ul ng-repeat="question in Questions">
        <li>
            <div>{{question.Text}}</div>
            <select ng-model="Answers['{{question.Name}}']" ng-options="option for option in question.Options">
            </select>
        </li>
    </ul>

    <a ng-click="ShowAnswers()">Submit</a>
</div>
function QuestionController($scope) {
    $scope.Answers = {};

    $scope.Questions = [
    {
        "Text": "Gender?",
        "Name": "GenderQuestion",
        "Options": ["Male", "Female"]},
    {
        "Text": "Favorite color?",
        "Name": "ColorQuestion",
        "Options": ["Red", "Blue", "Green"]}
    ];

    $scope.ShowAnswers = function()
    {
        alert($scope.Answers["GenderQuestion"]);
        alert($scope.Answers["{{question.Name}}"]);
    };
}​

Todo funciona, excepto que el modelo es literalmente Respuestas ["{{question.Name}}"], en lugar de las Respuestas evaluadas ["GenderQuestion"]. ¿Cómo puedo configurar el nombre de ese modelo de forma dinámica?

Mike Pateras
fuente

Respuestas:

121

http://jsfiddle.net/DrQ77/

Simplemente puede poner la expresión de JavaScript en formato ng-model.

Tosh
fuente
1
Te juro que lo intenté. Muchas gracias. De hecho, tomé una ruta diferente y solo puse el modelo en cuestión. Respuesta (publicaré un violín actualizado en un momento), que resultó ser una respuesta más directa (tengo que salir de la mentalidad de jQuery), pero es genial saber que puedo, de hecho, hacerlo de la manera que originalmente planeé para el futuro. ¡Gracias de nuevo!
Mike Pateras
En caso de que esto ayude a alguien más, estaba teniendo problemas similares, pero mi problema era que estaba usando ng-pattern="field.pattern"cuando lo que realmente quería era pattern="{{field.pattern}}". Es un poco confuso que angular generalmente proporciona un ayudante para atributos dinámicos, pero esta vez escribió su propia validación del lado del cliente y le dio el mismo nombre.
colllin
¿Por qué decidió crear un objeto vacío (Respuestas) cuando no tiene un propósito real para ello? Parece que lo está usando solo en el modelo ng y, aparte de esto, no parece tener ningún propósito. Entonces, ¿por qué no omitirlo por completo y hacer que funcione de esa manera? ¿Podrías aclararlo por favor?
Devner
Intenté hacer un cambio mínimo del código original. Si miras el código revisado de Mike ( jsfiddle.net/2AwLM/23 ), decidió deshacerse de él.
Tosh
Muchas gracias por esto. Me ayudo mucho. Vea aquí: stackoverflow.com/questions/34081903/…
Albert
31

Puede usar algo como esto scopeValue[field], pero si su campo está en otro objeto, necesitará otra solución.

Para resolver todo tipo de situaciones, puede utilizar esta directiva:

this.app.directive('dynamicModel', ['$compile', '$parse', function ($compile, $parse) {
    return {
        restrict: 'A',
        terminal: true,
        priority: 100000,
        link: function (scope, elem) {
            var name = $parse(elem.attr('dynamic-model'))(scope);
            elem.removeAttr('dynamic-model');
            elem.attr('ng-model', name);
            $compile(elem)(scope);
        }
    };
}]);

Ejemplo de HTML:

<input dynamic-model="'scopeValue.' + field" type="text">
William Weckl
fuente
Funciona como se esperaba.
C0ZEN
1
¡Hurra! ¡Esto es lo que necesitaba! ¡Gracias!
Snapman
2
Agradable. Pero aún así desearíamos poder usar ng-model="{{ variable }}":)
davidkonrad
13

Lo que terminé haciendo es algo como esto:

En el controlador:

link: function($scope, $element, $attr) {
  $scope.scope = $scope;  // or $scope.$parent, as needed
  $scope.field = $attr.field = '_suffix';
  $scope.subfield = $attr.sub_node;
  ...

así que en las plantillas podría usar nombres totalmente dinámicos, y no solo bajo un determinado elemento codificado (como en su caso "Respuestas"):

<textarea ng-model="scope[field][subfield]"></textarea>

Espero que esto ayude.

abourget
fuente
3

Para que la respuesta proporcionada por @abourget sea más completa, el valor de scopeValue [campo] en la siguiente línea de código podría no estar definido. Esto resultaría en un error al configurar el subcampo:

<textarea ng-model="scopeValue[field][subfield]"></textarea>

Una forma de resolver este problema es agregando un atributo ng-focus = "nullSafe (campo)", por lo que su código se vería como el siguiente:

<textarea ng-focus="nullSafe(field)" ng-model="scopeValue[field][subfield]"></textarea>

Luego, define nullSafe (campo) en un controlador como el siguiente:

$scope.nullSafe = function ( field ) {
  if ( !$scope.scopeValue[field] ) {
    $scope.scopeValue[field] = {};
  }
};

Esto garantizaría que scopeValue [campo] no esté indefinido antes de establecer cualquier valor en scopeValue [campo] [subcampo].

Nota: No puede usar ng-change = "nullSafe (field)" para lograr el mismo resultado porque ng-change ocurre después de que el ng-model ha sido cambiado, lo que arrojaría un error si scopeValue [field] no está definido.

Max
fuente
1

O puedes usar

<select [(ngModel)]="Answers[''+question.Name+'']" ng-options="option for option in question.Options">
        </select>
Şafak
fuente