Sign up ×
Stack Overflow is a community of 4.7 million programmers, just like you, helping each other. Join them, it only takes a minute:

Good Morning!

I've found several posts on stack that touch on this, but I haven't found any that accomplish what I'm trying to do exactly.

(JS Fiddle for Reference: http://jsfiddle.net/gsLXf/1/)

I have a dynamic set of questions that I am asking as part of a survey. I have figured out how to bind the responses in text and radio responses to a 'response' object that I can post back to my server. The problem I am having is with Checkboxes. As you can see in the fiddle, when I try to do

ng-model="response[question.id]"

all of my checkboxes respond as one item (which makes sense since they are all bound to the same value. However, when I use

ng-model="response[question.id][option.id]"

I pop an error because question.id hasn't been instantiated yet so option.id can't be pushed onto the object.

Ideally my response object for the referenced fiddle would look like this:

{
   "123456": "2", //radio question
   "789012": //checkbox question 
       ["5", "6"] //list of checkbox options ids selected
}

My users will be creating forms dynamically, so I have to be able to handle this very gracefully in all situations. I can't hard-code ANY object related data into a controller and I can't hand-create model objects to handle this situation.

I have considered looping through the known ID set to scaffold a response object to be filled during initialization, but it seems like overkill when Angular has such a nice way of just creating a response object on the fly (except for this situation).

What is the best way to go about doing this?

share|improve this question
    
if you put the answer id's initially in the response it's no problem: jsfiddle.net/Uh2Gc – stefchri Feb 11 '14 at 14:27
    
Thanks for the comment, but this won't work unless you can show me some way to do it dynamically based on the questions object. All of this is driven by a database and a middle tier API so I can't hand/hard code anything into the controller (like adding that object into the response object. – shanee Feb 11 '14 at 14:32
2  
Now it is dynamic: jsfiddle.net/Uh2Gc/1 – stefchri Feb 11 '14 at 14:56
    
@stefchri - Man, this is getting SO close! And that foreach is great, that will keep me from having any trouble in the future! Could you help me with one additional piece (a bit of an extension from the original question). Right now the array on the checkbox object is only capturing true/false instead of the value of that option. Any way I can get it to pick up the value (the option ID)? Thanks so much – shanee Feb 11 '14 at 15:06
1  
jsfiddle.net/Uh2Gc/2 solves your little question: ng-true-value – stefchri Feb 11 '14 at 15:14

2 Answers 2

up vote 2 down vote accepted

Here is a fiddle that does what you want: http://jsfiddle.net/2jVtH/

I have replaced the entire HTML with a directive, called cbb, used as:

<div cbb question="question" response="response"></div>

(this makes the original code cleaner too, IMHO, meaning I would do the same for the radio button)

The directive uses an isolated scope and in it puts an object that receives the check box values:

scope.model = {}

A deep $watch on this object updates an array with the values, so you get the desired response = { "789012": ["7", "8"] } format.

Directive full code:

surveyApp.directive("cbb", function() {
    return {
        restrict: "A",
        scope: {
            question: "=",
            response: "="
        },
        template:
            "<div class='text'>{{question.question}}</div>" +
            '<div class="options" ng-repeat="option in question.options">' +
                '<input type="checkbox" ng-model="model[option.id]" value="{{option.id}}"/> {{option.value}}' +
                '<tags ng-if="option.tags"></tags>' +
                '<action ng-if="option.action"></action>' +
            '</div>',
        link: function(scope, element, attrs) {
            if( !scope.response[scope.question.id] ) {
                scope.response[scope.question.id] = [];
            }
            var result = scope.response[scope.question.id];
            scope.model = {};
            scope.$watch("model", function(newval) {
                var x;
                result.splice(0);
                for( x in newval ) {
                    if( !newval.hasOwnProperty(x) ) continue;
                    if( newval[x] ) result.push(x);
                }
            }, true);
        }
    };
});
share|improve this answer
1  
Thanks @Nikos, I simplified my setup for Fiddle, but in actuality I already have several directives in play to produce this survey via templates. I'll try to take what you've got here and backfill it into my code! It looks like a sweet solution so I'll see what I can get out of it. – shanee Feb 11 '14 at 15:13
1  
Integrated beautifully! I was able to work it into my templates with no problem and it is definitely the cleanest implementation from all of the responses. Thanks for your help @Nikos – shanee Feb 11 '14 at 16:39

I try my best on existed Angular directive and html, but it still have redundant empty string in array when checkbox unchecked.

And I use the ng-init

ng-init="response[question.id] = []"

jsfiddle:

http://jsfiddle.net/P9dsR/

share|improve this answer
    
This didn't solve the full extent of my problem, but +1 for giving me a much easier way to scaffold the objects without having to loop over my questions object! – shanee Feb 11 '14 at 15:49

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.