We help IT Professionals succeed at work.

jQuery/Ajax - retrieve list of inputs and send as collection on parent object to MVC controller

Sailing_12
Sailing_12 asked
on
High Priority
86 Views
Last Modified: 2020-02-10
What is the best way to retrieve a variable list of text inputs and values and send via jQuery AJAX to MVC controller as a virtual ICollection on a parent object?

My markup will look something like this:  (but the number of inputs will be dynamic)

<div class="questionPanel">

.<!-- ..other markup and elements here  -->

<input name="item.answerChoiceText" class="form-control txtAnswerChoice text-box single-line" type="text" value="Red" >
<input name="item.answerChoiceText" class="form-control txtAnswerChoice text-box single-line" type="text" value="Blue" >
<input name="item.answerChoiceText" class="form-control txtAnswerChoice text-box single-line" type="text" value="Green" >
</div>

Open in new window


Then my jQuery AJAX call:  ( $(this) refers to the .questionPanel div)

$.ajax({
	type: 'POST',
	cache: false,
	async: false,
	url: '/Question/Insert',
	dataType: 'json',
	data: {
		questionId: $(this).attr('questionid'),
		questionText: $(this).find('.txtQuestionText').val(),
		questionType: $(this).find('.selQuestionType').val(),N
		showCommentsBox: $(this).find('.ckShowCommentsBox').prop('checked'),
		answersRequired: $(this).find('.txtAnswersRequired').val(),
		isActive: $(this).css('display') == 'none' ? false : true,
		order: $(this).css('display') == 'none' ? 0 : orderCount,
		surveyId: $('#surveyBuilderWrapper').attr('surveyid'),
		AnswerChoice: ????????	NEED TO SEND LIST HERE  ??????
	},
	success: function (result) {
	},
	error: function (xhr, ajaxOptions, error) {
	}

Open in new window


And my MVC controller is expecting a Question object, which contains a collection of Answer Choices:

public JsonResult Insert(Question question, int order, int surveyId)
{
        // process the insert here..
}


public partial class Question
{
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
	public Question()
	{
		AnswerChoice = new HashSet<AnswerChoice>();
	}

	public int questionId { get; set; }

	[Required]
	[StringLength(500)]
	public string questionText { get; set; }

	public int questionType { get; set; }

	public bool showCommentsBox { get; set; }

	public int? answersRequired { get; set; }

	public bool isActive { get; set; }

	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
	public virtual ICollection<AnswerChoice> AnswerChoice { get; set; }

}

Open in new window


Thanks!
Comment
Watch Question

Jeffrey Dake Senior Director of Technology
CERTIFIED EXPERT

Commented:

If I understand what you are trying to do here you could call seralize on the section you are looking for to get the name values you are looking for.  https://api.jquery.com/serialize/

Sailing_12Pirate

Author

Commented:
No, that doesn't work - needs to be an array list of objects.

This works but wondering if there is a more elegant way to do this inline with jQuery instead of constructing the whole array first.

var answerChoices = [];
$(this).find('.txtAnswerChoice').each(function () {
	obj = {};
	obj['answerChoiceText'] = $(this).val();
	answerChoices.push(obj);
});

$.ajax({
	type: 'POST',
	cache: false,
	async: false,
	url: '/Question/Insert',
	dataType: 'json',
	data: {
		questionId: $(this).attr('questionid'),
		questionText: $(this).find('.txtQuestionText').val(),
		questionType: $(this).find('.selQuestionType').val(),
		showCommentsBox: $(this).find('.ckShowCommentsBox').prop('checked'),
		answersRequired: $(this).find('.txtAnswersRequired').val(),
		isActive: $(this).css('display') == 'none' ? false : true,
		order: $(this).css('display') == 'none' ? 0 : orderCount,
		surveyId: $('#surveyBuilderWrapper').attr('surveyid'),
		AnswerChoice: answerChoices

	},
	success: function (result) {

	},
	error: function (xhr, ajaxOptions, error) {

	}
});

Open in new window

Scott FellDeveloper & EE Moderator
CERTIFIED EXPERT
Fellow
Most Valuable Expert 2013

Commented:

Jeffery's suggestion is a good one and will work. All you need to do is remove dataType: json and make sure the field names match what your controller is expecting. In other words change


<input name="item.answerChoiceText"


to


<input name="questionId"


Personally, I prefer to use id's changing

questionId: $(this).attr('questionid'),

to

questionId: $('# txtQuestionText').val(),

leakim971Multitechnician
CERTIFIED EXPERT
Distinguished Expert 2019

Commented:
what your controller is waiting for is  : Question question, int order, int surveyId
when you are only sending :
	data: {
		questionId: $(this).attr('questionid'),
		questionText: $(this).find('.txtQuestionText').val(),
		questionType: $(this).find('.selQuestionType').val(),
		showCommentsBox: $(this).find('.ckShowCommentsBox').prop('checked'),
		answersRequired: $(this).find('.txtAnswersRequired').val(),
		isActive: $(this).css('display') == 'none' ? false : true,
		order: $(this).css('display') == 'none' ? 0 : orderCount,
		surveyId: $('#surveyBuilderWrapper').attr('surveyid'),
		AnswerChoice: answerChoices

	},

Open in new window


I would like to try the following :

	data: {
           question : {
		questionId: $(this).attr('questionid'),
		questionText: $(this).find('.txtQuestionText').val(),
		questionType: $(this).find('.selQuestionType').val(),
		showCommentsBox: $(this).find('.ckShowCommentsBox').prop('checked'),
		answersRequired: $(this).find('.txtAnswersRequired').val(),
		isActive: $(this).css('display') == 'none' ? false : true,
		AnswerChoice: answerChoices
           },
           order : {
		$(this).css('display') == 'none' ? 0 : orderCount
           },
           surveyId : {		
               surveyId: $('#surveyBuilderWrapper').attr('surveyid'),
           }
	},

Open in new window

Sailing_12Pirate

Author

Commented:
Scott, that response makes no sense to me - why would I name the inputs questionId when they represent one or more answers to a parent question?

However I did try it and it failed - I did not get the answers passed through to my controller method on the Question object.

<input name="questionId" class="form-control txtAnswerChoice text-box single-line" type="text" value="Vanilla" choiceid="">
<input name="questionId" class="form-control txtAnswerChoice text-box single-line" type="text" value="Chocolate" choiceid="">


$.ajax({
	type: 'POST',
	cache: false,
	async: false,
	url: '/Question/Insert',
	dataType: 'json',
	data: {
		questionId: $(this).attr('questionid'),
		questionText: $(this).find('.txtQuestionText').val(),
		questionType: questionType,
		showCommentsBox: $(this).find('.ckShowCommentsBox').prop('checked'),
		answersRequired: $(this).find('.txtAnswersRequired').val(),
		isActive: $(this).hasClass('removed') == true ? false : true,
		
		AnswerChoice: $(this).find('.txtAnswerChoice').serialize(),
				
		order: $(this).hasClass('removed') == true ? 0 : orderCount,
		surveyId: $('#surveyBuilderWrapper').attr('surveyid')
	},
	success: function (result) {
	},
	error: function (xhr, ajaxOptions, error) {
	}
});

Open in new window


Your second suggestion looks like it is assigning the value typed in a textbox to an Id...  ???? also makes no sense.

I am purposefully not using ids because there will be repetitive question panels that are being iterated, and Ids will not exist yet when the user has added a new question/answerset that has not yet been saved.
Sailing_12Pirate

Author

Commented:
leakim971, your response does not address my question at all - I have not problem getting the order and surveyId passed through with the way I posted the code, I am looking for input on how to pass the answerChoices as a related one-to-many.
Scott FellDeveloper & EE Moderator
CERTIFIED EXPERT
Fellow
Most Valuable Expert 2013

Commented:

I think I read your question wrong.  


Are you just looking to put the data from the three item.answerChoiceText inputs 

<div class="questionPanel">
    .<!-- ..other markup and elements here  -->
    <input name="item.answerChoiceText" class="form-control txtAnswerChoice text-box single-line" type="text" value="Red" >
    <input name="item.answerChoiceText" class="form-control txtAnswerChoice text-box single-line" type="text" value="Blue" >
    <input name="item.answerChoiceText" class="form-control txtAnswerChoice text-box single-line" type="text" value="Green" >
</div>


into the one AnswerChoice: ????????NEED TO SEND LIST HERE  ??????


How are you expecting the data? Red, Blue, Green?  or just one item?


In other words you want 


data: {
questionId: $(this).attr('questionid'),
questionText: $(this).find('.txtQuestionText').val(),
questionType: $(this).find('.selQuestionType').val(),N
showCommentsBox: $(this).find('.ckShowCommentsBox').prop('checked'),
answersRequired: $(this).find('.txtAnswersRequired').val(),
isActive: $(this).css('display') == 'none' ? false : true,
order: $(this).css('display') == 'none' ? 0 : orderCount,
surveyId: $('#surveyBuilderWrapper').attr('surveyid'),
AnswerChoice: 'Red, Green'
},


Scott FellDeveloper & EE Moderator
CERTIFIED EXPERT
Fellow
Most Valuable Expert 2013

Commented:

I apologize for the top part of the answer. There are some bugs in the comment box. That is supposed to be your code


<div class="questionPanel">

.<!-- ..other markup and elements here  -->

<input name="item.answerChoiceText" class="form-control txtAnswerChoice text-box single-line" type="text" value="Red" >
<input name="item.answerChoiceText" class="form-control txtAnswerChoice text-box single-line" type="text" value="Blue" >
<input name="item.answerChoiceText" class="form-control txtAnswerChoice text-box single-line" type="text" value="Green" >
</div>

Sailing_12Pirate

Author

Commented:
Yes, all of them (well, all that do not have the class '.removed' but we're not there yet) - there is a one-to-many relationship between Question and AnswerChoice, I am looking to send all of the AnswerChoices the user has added in the UI on the Question object to the controller.

Hard-coding this works:

$.ajax({
	type: 'POST',
	cache: false,
	async: false,
	url: '/Question/Insert',
	dataType: 'json',
	data: {
		questionId: $(this).attr('questionid'),
		questionText: $(this).find('.txtQuestionText').val(),
		questionType: $(this).find('.selQuestionType').val(),
		showCommentsBox: $(this).find('.ckShowCommentsBox').prop('checked'),
		answersRequired: $(this).find('.txtAnswersRequired').val(),
		isActive: $(this).css('display') == 'none' ? false : true,
		order: $(this).css('display') == 'none' ? 0 : orderCount,
		surveyId: $('#surveyBuilderWrapper').attr('surveyid'),
		AnswerChoice: [
                                              {answerChoiceText:"Blue"},
                                              {answerChoiceText:"Red"}
                                           ]

	},

Open in new window


Likewise, building an array like this works:

var answerChoices = [];
$(this).find('.txtAnswerChoice').each(function () {
	obj = {};
	obj['answerChoiceText'] = $(this).val();
	answerChoices.push(obj);
});

	data: {
		questionId: $(this).attr('questionid'),
		questionText: $(this).find('.txtQuestionText').val(),
		questionType: $(this).find('.selQuestionType').val(),
		showCommentsBox: $(this).find('.ckShowCommentsBox').prop('checked'),
		answersRequired: $(this).find('.txtAnswersRequired').val(),
		isActive: $(this).css('display') == 'none' ? false : true,
		order: $(this).css('display') == 'none' ? 0 : orderCount,
		surveyId: $('#surveyBuilderWrapper').attr('surveyid'),
		AnswerChoice: answerChoices

	},

Open in new window


But I'm thinking that jQuery has something built in to do this for me without having to manually build my array - I just don't know what it is.
Sailing_12Pirate

Author

Commented:
Maybe this helps - the user in this scenario is not 'answering' a question - the users is constructing a multiple choice question for others to answer.
Developer & EE Moderator
CERTIFIED EXPERT
Fellow
Most Valuable Expert 2013
Commented:

I see what you are doing, I have created a form to create a survey before too. 


I think the way you have it is good. You could use makeArray   https://api.jquery.com/jquery.makearray/ and that will take  your collection of  $("input.item.answerChoiceText").


Yes, you have an extra line or two with your example, but that does leave you room to do any client side error checking before you send it to the server for final error checking.

Sailing_12Pirate

Author

Commented:
Thanks.

Explore More ContentExplore courses, solutions, and other research materials related to this topic.