How can I set a dynamic model name in AngularJS?
Asked Answered
E

5

91

I want to populate a form with some dynamic questions (fiddle here):

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

Everything works, except the model is literally Answers["{{question.Name}}"], instead of the evaluated Answers["GenderQuestion"]. How can I set that model name dynamically?

Etymologize answered 23/9, 2012 at 15:32 Comment(0)
D
122

http://jsfiddle.net/DrQ77/

You can simply put javascript expression in ng-model.

Dyanna answered 23/9, 2012 at 21:55 Comment(5)
I swear I tried that. Thank you, very much. I actually went a different route, and just set the model to question.Answer (I'll put out an updated fiddle in a bit), which turned out to be a more direct answer (gotta get out of the jQuery mindset), but it's great to know that I can, indeed, do it the way that I originally planned for the future. Thanks again!Etymologize
In case this helps anyone else, I was having similar issues, but my problem was that I was using ng-pattern="field.pattern" when what I really wanted was pattern="{{field.pattern}}". Kind of confusing that angular usually provides a helper for dynamic attributes but this time wrote it's own client-side validation and gave it the same name.Queeniequeenly
Why did you decide to create an empty object (Answers) when you have no actual purpose for it? You seem to be using it only in the ng-model & other than this, there does not seem to be any purpose.So why not omit it altogether and make it work that way? Could you please clarify?Photomural
I just tried to make a minimum change from the original code. If you look at Mike's revised code(jsfiddle.net/2AwLM/23), he decided to get rid of it.Dyanna
Thank you SO much for this. Helped me a lot. See here: #34082403Caudal
F
32

You can use something like this scopeValue[field], but if your field is in another object you will need another solution.

To solve all kind of situations, you can use this directive:

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

Html example:

<input dynamic-model="'scopeValue.' + field" type="text">
Furnivall answered 19/8, 2015 at 13:3 Comment(2)
Works as expected.Ragen
Nice. But still wish we could just use ng-model="{{ variable }}" :)Tribromoethanol
M
13

What I ended up doing is something like this:

In the controller:

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

so in the templates I could use totally dynamic names, and not just under a certain hard-coded element (like in your "Answers" case):

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

Hope this helps.

Monteux answered 12/4, 2013 at 18:43 Comment(0)
M
2

To make the answer provided by @abourget more complete, the value of scopeValue[field] in the following line of code could be undefined. This would result in an error when setting subfield:

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

One way of solving this problem is by adding an attribute ng-focus="nullSafe(field)", so your code would look like the below:

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

Then you define nullSafe( field ) in a controller like the below:

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

This would guarantee that scopeValue[field] is not undefined before setting any value to scopeValue[field][subfield].

Note: You can't use ng-change="nullSafe(field)" to achieve the same result because ng-change happens after the ng-model has been changed, which would throw an error if scopeValue[field] is undefined.

Maihem answered 28/6, 2014 at 23:40 Comment(0)
S
1

Or you can use

<select [(ngModel)]="Answers[''+question.Name+'']" ng-options="option for option in question.Options">
        </select>
Sullins answered 13/10, 2019 at 0:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.