Robert M. Robert M. - 27 days ago 7
Javascript Question

An ASP.NET MVC with a view with a dynamic number of inputs

Ok, this is a complicated question and I'm so new at this, maybe I don't even know how to ask it properly.

I'm building an app using ASP.NET MVC. I have a view that contains a form that has an textbox input that asks for a user to name a recipient to their estate for a will. Below the input, I want a button ("Add another recipient") that adds another input to the form.

Here's how I've mocked this up using Javascript (Click "Yes" and then click "Cash" to see what I'm talking about): https://jsfiddle.net/cinerobert/409k27ot/7/

<p>Would you like to leave specific gift in your will?</p>
<p>
<button onclick="q1AnswerYes()">Yes</button>
<button onclick="q1AnswerNo()">No</button>
</p>
<br />
<!-- If answer to Q1 is Yes show this block -->
<div id="q1AnswerYesBlock" style="display:none; text-align:center;
margin:auto;">
<div>What specific gifts would you like to leave?</div>
<hr>
<div>
<button onclick="q2AnswerCash('q2AnswerCashDisp')">Cash</button>
<button>Car</button>
<button>Real Estate</button>
<button>Jewelry</button>
<button>Other</button>
</div>
<div id="q2AnswerCashDisp">
<!-- Recipient forms get added here -->
</div>
<div style="text-align:center;
margin:auto; display:inline">
<button id="addCashRecipient" style="display:none" onclick="addCashRecipientForm('q2AnswerCashDisp')">Add another recipient</button>
</div>
</div>

<!-- If answer to Q1 is No show this block -->
<div id="q1AnswerNoBlock" style="display:none">
<p>Some other crap</p>
</div>
<div id="testFormHTML" style="display:none">
<form id="testFormId" method="get" style="border-syle:none">
<fieldset id="fieldsetid" style="border-style:none">
<div class="formPrompt">
Amount:
</div>
<input type="text">
<div class="formPrompt">
Recipient:
</div>
<input onblur="submit()" type="text" name="recipient">
<input style="display:none" class="testFormParamInputId" name="testFormParam" value="">
<br>
<br>
</fieldset>
</form>
</div>

<script>
var cashRecipientNum = 1;
function q1AnswerYes() {
document.getElementById("q1AnswerYesBlock").style.display = "block";
document.getElementById("q1AnswerNoBlock").style.display = "none";
}

function q1AnswerNo() {
document.getElementById("q1AnswerYesBlock").style.display = "none";
document.getElementById("q1AnswerNoBlock").style.display = "block";
}

function q2AnswerCash(blockId) {
if (cashRecipientNum == 1) {
addForm(blockId, "testForm", cashRecipientNum++);
document.getElementById("addCashRecipient").style = "display:inline";
};
}

function addCashRecipientForm(blockId) {
addForm(blockId, "testForm", cashRecipientNum++);
}

// Function to add a new form with unique action argument to the document.
function addForm(blockId, formArg, numInstance) {
var formHTML = formArg + "HTML";
var formId = formArg + "Id";
var formParamInputId = formArg + "ParamInputId";
var newFormId = formArg + "Id" + numInstance;
var div = document.getElementById(formId),
clone = div.cloneNode(true); // true means clone all childNodes and all eventhandlers
clone.id = newFormId;
clone.style = "border-style:none";
document.getElementById(blockId).appendChild(clone);
var x = document.getElementById(newFormId).getElementsByClassName(formParamInputId);
x[0].value = numInstance;

console.log(x[0].class);
console.log("here");
}
</script>


What I can't wrap my head around is this:


  • since I'm using a hardcoded html form (using tags) rather than html helpers (like @Html.TextBoxFor) each input isn't bound to anything in the model. I can read the inputs in using FormCollection, but when the form submits I don't know how to repost the form with the values the user entered still in place.

  • if I use html helpers (@Html.TextBoxFor) and bind each input to a property in the model, then I don't understand how to allow the form to add an unlimited number of input fields



I've searched around for examples of dynamic views, but the examples I've found have to do with a view that responds to changes in the model. I haven't found one that involves adding an unlimited number of input fields based on a user action.

I know this is kind of a shaggy dog of a question, but if someone could help point me in the right direction I would be very grateful. Thank in advance for being patient with a newbie.

Answer

The scenario you're describing screams View Model. Even a simple one like this would help

public class WillRecipientsViewModel
{
    public List<string> Recipients { get; set; }
}

Then your controller would need the corresponding action of your form

public ActionResult SetWillRecipients()
{
    return View();
}

[HttpPost]
public ActionResult SetWillRecipients(WillRecipientsViewModel model)
{
    // Business logic

    // Go back to the view if an error occurs
    return View(model);
}

This will bind the values in your form to the Model object which will make things easier. Then you just need to make sure your view is using <input type="text" name="Recipients" /> to capture the data in the model object in your post. I would also recommend doing your best to have the input name attributes closely match the casing of the view models to avoid and conflicts