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?

Mike Pateras
  • 14,127
  • 29
  • 93
  • 131

5 Answers5

121

http://jsfiddle.net/DrQ77/

You can simply put javascript expression in ng-model.

Tosh
  • 35,589
  • 11
  • 64
  • 54
  • 1
    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! – Mike Pateras Sep 24 '12 at 01:24
  • 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. – colllin Oct 17 '13 at 07:34
  • 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? – Devner Nov 11 '14 at 15:07
  • I just tried to make a minimum change from the original code. If you look at Mike's revised code(http://jsfiddle.net/2AwLM/23/), he decided to get rid of it. – Tosh Nov 11 '14 at 23:57
  • Thank you SO much for this. Helped me a lot. See here: http://stackoverflow.com/questions/34081903/having-multiple-dynamic-select-boxes-angularjs/34085318#34085318 – Bird87 ZA Dec 04 '15 at 09:49
  • You saved my life. – C0ZEN Apr 24 '16 at 09:45
  • Thank You So much! Big problem have simple solution. Helped me a lot. – sundsx Apr 13 '20 at 19:37
31

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">
William Weckl
  • 2,297
  • 4
  • 22
  • 42
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.

abourget
  • 2,063
  • 16
  • 16
3

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.

Max
  • 1,002
  • 9
  • 22
1

Or you can use

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