1

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.

11
  • 2
    It sounds like you need a view model which has a List<string> property. What you'd need for your <input /> tags is a name of recipients[n] for each recipient in the form, starting with 0. The MVC model binder will pickup on the array indexing style and use that to bind to the model's recipents property (List<string>). I think if you were to post with multiple recipients inputs without the indexing I think that would also bind it as well Commented Nov 9, 2016 at 19:09
  • Ahh, very interesting. Do you mean like: <input name = "recipient[0]"> ? Commented Nov 9, 2016 at 19:11
  • 1
    I just did a quick example, inputs with either <input name="recipients" /> or "index" inputs <input name="recipients[0]" /> <input name="recipients[1]" /> will work. You just need to make sure you use the same casing in your view model. You'll also need to update your view to use the model and have that populate the inputs using @Html.InputFor (or TextBoxFor) to preserve the post's values Commented Nov 9, 2016 at 19:16
  • 1
    For dynamically adding collection items that are complex objects, refer this answer - the answer you have accepted will not work because you need to generate a collection of an object containing at least 2 properties (Recipient and Amount) Commented Nov 9, 2016 at 21:55
  • Thank you Stephen. In the example you linked, am I correct in thing that AdminProductDetailModel would be a ArrayList collection declared like: class AdminProductDetailModel : ArrayList { public string fkConfigChoiceCategoryColorId; etc. } ? Commented Nov 10, 2016 at 18:20

1 Answer 1

1

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

// Controller
public ActionResult SetWillRecipients()
{
    // Added this to force at least 1 text box into the view
    WillRecipientsViewModel model = new WillRecipientsViewModel
    {
        Recipients = new List<string> { "" };
    };
    return View(model);
}

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

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

// View
@model WillRecipientsViewModel
@using(Html.BeginForm("SetWillRecipients"))
{
    @Html.DisplayFor(model => model.Recipients)
    for (int i = 0; i < Model.Recipients.Count; i++)
    {
        // Generate the textboxes for any values that were previously submitted
        Html.InputFor(model => model.Recipients[i])
    }
}

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

Updated

I included some more code to exemplify how you can pull the previous recipients into the interface

Sign up to request clarification or add additional context in comments.

3 Comments

Ok, now I'm confused about something else. Can javascript on the page alter the @html elements? It doesn't seem like it since they render as html on the browser end. And related: how can I have the changes the javascript makes to the form (the new form fields) be retained after post?
re:@Html rendered elements: by the time the page hits the browser, the elements are already standard HTML, javascript has 0 problems interfacing with, and modifying those tags. I'll update my answer on getting the form fields into the view
Thanks so much. I owe you. I still don't quite understand how the changes javascript makes to the page will persist after post. Doesn't the page get reloaded?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.