Como posso definir um nome de modelo dinâmico no AngularJS?

91

Quero preencher um formulário com algumas perguntas dinâmicas (mexa aqui ):

<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}}"]);
    };
}​

Tudo funciona, exceto que o modelo é literalmente Respostas ["{{question.Name}}"], em vez das Respostas avaliadas ["GenderQuestion"]. Como posso definir esse nome de modelo dinamicamente?

Mike Pateras
fonte

Respostas:

121

http://jsfiddle.net/DrQ77/

Você pode simplesmente colocar a expressão javascript em ng-model.

Tosh
fonte
1
Eu juro que tentei isso. Muito obrigado. Na verdade, fui por um caminho diferente e apenas configurei o modelo para questionar. Resposta (vou lançar um violino atualizado em breve), que acabou sendo uma resposta mais direta (preciso sair da mentalidade jQuery), mas é ótimo saber que posso, de fato, fazer da maneira que planejei originalmente para o futuro. Obrigado novamente!
Mike Pateras
Caso isso ajude mais alguém, eu estava tendo problemas semelhantes, mas meu problema era que eu estava usando ng-pattern="field.pattern"quando o que eu realmente queria era pattern="{{field.pattern}}". É meio confuso que o angular geralmente fornece um auxiliar para atributos dinâmicos, mas desta vez escreveu sua própria validação do lado do cliente e deu a ela o mesmo nome.
colllin de
Por que você decidiu criar um objeto vazio (Respostas) quando não tinha um propósito real para isso? Você parece estar usando-o apenas no modelo ng e, fora isso, não parece haver nenhum propósito. Então, por que não omiti-lo totalmente e fazê-lo funcionar dessa maneira? Você poderia por favor esclarecer?
Devner
Eu apenas tentei fazer uma alteração mínima no código original. Se você olhar o código revisado de Mike ( jsfiddle.net/2AwLM/23 ), ele decidiu se livrar dele.
Tosh
Muito obrigado por isso. Me ajudou muito. Veja aqui: stackoverflow.com/questions/34081903/…
Albert
31

Você pode usar algo assim scopeValue[field], mas se o seu campo estiver em outro objeto, você precisará de outra solução.

Para resolver todos os tipos de situações, você pode usar esta diretiva:

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);
        }
    };
}]);

Exemplo de HTML:

<input dynamic-model="'scopeValue.' + field" type="text">
William Weckl
fonte
Funciona conforme o esperado.
C0ZEN
1
Yay! Isso é o que eu precisava! Obrigado!
Snapman
2
Agradável. Mas ainda gostaria de poder usar ng-model="{{ variable }}":)
davidkonrad
13

O que acabei fazendo é algo assim:

No controlador:

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

então, nos modelos, eu poderia usar nomes totalmente dinâmicos, e não apenas sob um determinado elemento codificado (como em seu caso "Respostas"):

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

Espero que isto ajude.

abourget
fonte
3

Para tornar a resposta fornecida por @abourget mais completa, o valor de scopeValue [field] na linha de código a seguir pode ser indefinido. Isso resultaria em um erro ao definir o subcampo:

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

Uma maneira de resolver esse problema é adicionando um atributo ng-focus = "nullSafe (field)", de modo que seu código se pareça com o seguinte:

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

Em seguida, você define nullSafe (campo) em um controlador como o abaixo:

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

Isso garantiria que scopeValue [field] não fosse indefinido antes de definir qualquer valor para scopeValue [field] [subfield].

Nota: Você não pode usar ng-change = "nullSafe (field)" para obter o mesmo resultado porque ng-change acontece depois que o ng-model foi alterado, o que geraria um erro se scopeValue [field] fosse indefinido.

Max
fonte
1

Ou você pode usar

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