I am working on an online registration form in HTML. Some of the fields are dynamically added using a foreach data-binding in knockout.js based on a collection of values. I have the jquery part complete so that it will add all of the text fields and collections.
The dynamic part of the form looks like this:
<div class="form_section" ID="FamilyMembers" style="display:none;" >
<div class="wFamilyMember" data-bind="foreach: $root.familyMembers" >
<h2>Family Member <span data-bind="text: ($index() + 1)"></span> Information</h2>
<div class='form_section'>
<div class="eccform_column">
<div class="eccform_label">First Name*</div>
<div class="eccform_field"><input class="fmFirstName" title="First Name" type="text" data-bind="value:FirstName" /></div>
</div>
<div class="eccform_column">
<div class="eccform_label">Last Name *</div>
<div class="eccform_field"><input class="fmLastName" title="Last Name" data-bind="value:LastName"/></div>
</div>
</div>
<div class="form_section">
<div class="eccform_column">
<div class="eccform_label">Family Role*:</div>
<div class="eccform_dropdowns"><select class='fmFamilyRole' data-bind="options: $root.familyRoles, optionsText:'roleName', optionsValue:'value', value:FamilyRole"></select> </div>
</div>
<div class="eccform_column">
<div class="eccform_label">Age Group*:</div>
<div class="eccform_dropdowns"><select class='fmAgeGroup' data-bind="options:$root.ageGroups, optionsText:'groupName', optionsValue:'value', value:AgeGroup"></select></div>
</div>
<div class="eccform_checkbox_items">
<div class="eccform_checkbox"><input type="checkbox" class="fmLunchTicket" data-bind="checked: LunchTicket">Lunch Ticket ($5 for children, $10 for adults)</div>
<div class="eccform_checkbox"><input type="checkbox" class="fmSeminarTicket" data-bind="checked: SeminarTicket" >Seminar Ticket ($40)</div>
</div>
</div>
<div class="form_section">
<div class="eccform_column_button">
<button data-bind="click: $root.removeFamilyMember">Remove</button>
</div>
</div>
</div>
<button data-bind="click: $root.addFamilyMember">Add Family Member</button>
</div>
<div class="form_section" ID="Exhibitors" style="display:none;">
<div class='wExhibitor' data-bind="foreach: $root.exhibitors">
<h2>Exhibitor <span data-bind="text: $index() + 1"></span> Information</h2>
<div class="eccform_section">
<div class="eccform_column">
<div class="eccform_label">First Name*</div>
<div class="eccform_field"><input class="exFirstName" title="First Name" data-bind="value:FirstName" /></div>
</div>
<div class="eccform_column">
<div class="eccform_label">Last Name*:</div>
<div class="eccform_field"><input class="exLastName" title="Last Name" data-bind="value:LastName" /></div>
</div>
</div>
<div class="eccform_section">
<div class="eccform_column_exhibitor">
<div class="eccform_checkbox"><input type="checkbox" class="exLunchTicket" data-bind="checked: LunchTicket">Lunch Ticket ($10)</div>
</div>
<div class="eccform_column_exhibitor">
<div class="eccform_checkbox"><input type="checkbox" class="exSeminarTicket" data-bind="checked: SeminarTicket">Seminar Ticket ($40)</div>
</div>
<div class="eccform_column_removebutton">
<button data-bind="click: $root.removeExhibitor">Remove Exhibitor</button>
</div>
</div>
</div>
<button data-bind="click:$root.addExhibitor">Add Exhibitor</button>
</div>
The problem is in that when I try to retrieve the values in C#, I don't seem to be able to read these dynamically added values. I use this code to read in the values:
List<string> oFirstNames = ConvertFromEnumerable(Request.Form.AllKeys.Where(x => x.StartsWith("exFirstName")));
List<string> oLastNames = ConvertFromEnumerable(Request.Form.AllKeys.Where(x => x.StartsWith("exLastName")));
List<string> oLunchTickets = ConvertFromEnumerable(Request.Form.AllKeys.Where(x => x.StartsWith("exLunchTicket")));
List<string> oSeminarTickets = ConvertFromEnumerable(Request.Form.AllKeys.Where(x => x.StartsWith("exSeminarTicket")));
This worked when I was using javascript to add new elements dynamically before, but it doesn't work now. It always returns a null or empty string set. I feel like I'm approaching this from the totally wrong angle, but I don't know what the right angle is. Should I be finding a way to get the data from the viewmodel instead? Any insights?
Alright, the solution was as simple as attaching the appropriate name onto the HTML DOM element. The final HTML ended up looking something like this:
<div class="form_section" ID="Exhibitors" style="display:none;">
<div class='wExhibitor' data-bind="foreach: $root.exhibitors">
<h2>Exhibitor <span data-bind="text: $index() + 1"></span> Information</h2>
<div class="eccform_section">
<div class="eccform_column">
<div class="eccform_label">First Name*</div>
<div class="eccform_field"><input id="exFirstName" runat="server" class="exFirstName" title="First Name" data-bind="value:FirstName,attr:{name: 'exFirstName_'+$index()}" /></div>
</div>
<div class="eccform_column">
<div class="eccform_label">Last Name*:</div>
<div class="eccform_field"><input id="exLastName" runat="server" class="exLastName" title="Last Name" data-bind="value:LastName, attr:{name: 'exLastName_'+$index()}" /></div>
</div>
</div>
<div class="eccform_section">
<div class="eccform_column_exhibitor">
<div class="eccform_checkbox"><input id="exLunchTicket" name="exLunchTicket" runat="server" type="checkbox" class="exLunchTicket" data-bind="checked: LunchTicket, attr: { name: 'exLunchTicket_' + $index() }">Lunch Ticket ($10)</div>
</div>
<div class="eccform_column_exhibitor">
<div class="eccform_checkbox"><input id="exSeminarTicket" name="exLunchTicket" runat="server" type="checkbox" class="exSeminarTicket" data-bind="checked: SeminarTicket, attr: { name: 'exSeminarTicket_' + $index() }">Seminar Ticket ($40)</div>
</div>
<div class="eccform_column_removebutton">
<button data-bind="click: $root.removeExhibitor">Remove Exhibitor</button>
</div>
</div>
</div>
<button data-bind="click:$root.addExhibitor">Add Exhibitor</button>
</div>
Notice this section:
attr: { name: 'exSeminarTicket_' + $index() }
.
Then, in the C# I corrected the code to look like this:
int SomeKeys = Request.Form.AllKeys.Where(x=>x.StartsWith("exFirstName")).Count();
for (int i = 0; i < SomeKeys; i++)
{
string FirstName = Request.Form["exFirstName_" + i];
string LastName = Request.Form["exLastName_" + i];
bool LunchTicket = Convert.ToBoolean(Request.Form["exLunchTicket_" + i]);
bool SeminarTicket = Convert.ToBoolean(Request.Form["exSeminarTicket_" + i]);
if (FirstName != "" && LastName != "")
lFamilyMembers.Add(new FamilyMember(FirstName, LastName, SeminarTicket, LunchTicket, AgeGroup.Twenties, FamilyRole.Exhibitor));
}