How to handle repeating form fields in ASP MVC - c#

I have a form which asks users for their personal info and their family members.
fields of the family members section is repeating.
my question is what is best practice to handle these kind of repeating forms?
I currently use AJAX to repeat forms but how to collect data from these repeating fields?
since some one asked for how I repeat form, I do it like this:
AJAX Call
$(document).on('click', '.btn-add-item', function (e) {
e.preventDefault();
var $results = $('#results');
$.ajax({
url: '/AJAX/AddFamilyForm',
type: 'post',
success: function (data) {
$(data).appendTo($results);
afterAJAX();
}
});
});
C# code
[HttpPost]
public PartialViewResult AddFamilyForm()
{
if (!Request.IsAjaxRequest()) return null;
return PartialView("_FamilyForm");
}

This is some skeleton code on how to get this to work with proper model-binding in MVC. You'll need to write some JS to be able to delete/add new rows.
Model
public class MyModel
{
public FamilyMembers[] FamilyMembers { get; set; }
}
View
<button id="addNewFamilyMember" type="button">Add</button>
#if (Model.FamilyMembers != null)
{
for (int i = 0; i < Model.FamilyMembers.Length; i++)
{
<tr>
<td>
<button type="button">Delete</button>
#Html.Hidden("FamilyMembers.Index", i)
</td>
<td>
#Html.TextBoxFor(m => Model.FamilyMembers[i].Relation)
</td>
<td>
#Html.TextBoxFor(m => Model.FamilyMembers[i].FullName)
</td>
</tr>
}
}
Below is the code for adding a new member. It creates html dynamically and is able to bind to the posted model because of naming conventions. time gives each added row a unique id so all the data stays together.
JS (using Jquery)
var hidden = '#Html.Hidden("FamilyMembers.Index", "{id}")';
var relationHtml = '#Html.TextBox("FamilyMembers[{id}].Relation")';
var fullNameHtml = '#Html.TextBox("FamilyMembers[{id}].FullName")';
$("#addNewFamilyMember").on("click", function () {
var time = Date.now();
var deleteHtml = "<button type='button'>Delete</button>";
$("#familyMembers-table").find("tbody")
.append($("<tr><td>" + hidden.replace("{id}", time) + deleteHtml + "</td>" +
"<td>" + relationHtml.replace("{id}", time) + "</td>" +
"<td>" + fullNameHtml.replace("{id}", time) + "</td></tr>"));
});

One of the solution could be combination of hidden field and control name.
Steps:
Use a hidden field to keep the count the number of row.
Create controls with name like text_relation_1 for first row and text_relation_2 for second row and so on
Generate other controls in same way.
Increase and decrease the hidden field value so that when values post you can know the number of rows added by the user
On your action use FormCollection and loop though hidden field number and get the values from FormCollection
Like suppose I created 3 rows then I can create a action like below
public ActionResult SomeActionMethod(FormCollection formCollection, string hid)
{
for(int i=1;i<hid;i++)
{
var relationId="text_relation_"+i;
var firstrealtion=formCollection[relationId];
...
}
}

You don't need any extra Ajax requests for this, since you can use established and standard <form> features.
Just append [] to the name of the added forms and you'll end up with an array rather than a single value in your HTTP request once the form is submitted:
<input type="text" name="relation[]" /><input type="text" name="fullname[]" />
<input type="text" name="relation[]" /><input type="text" name="fullname[]" />
<input type="text" name="relation[]" /><input type="text" name="fullname[]" />
In this example you'd end up with an array relation and an array fullname, both containing your datasets.

Related

Creating dropdowns according to the database

There are different properties for 3 (x,y,z) properties in my database.
I created a dropdown and according to the selection I will make in the dropdown, I want to bring the properties belonging to this property in the database with another dropdown.
(For example I have x, y,z properties. X has ( a,b) , y has (c,d) , z has ( e,f) properties. When I choose the x property in dropdown I want to see a and b properties in the next dropdown. If I choose y property, c and d properties will show up in the next dropdown.)
Beginning of My Code
<form id="CareModal">
#Html.HiddenFor(model => model.PersonID)
#Html.Hidden("DoesAddictionExist", false)
#Html.DropDownList("AddictionStatusID", (IEnumerable<SelectListItem>)ViewBag.AddictionStatus, null, new { #class = "form-control", #onchange = "AddictionChanged(this.value)" })
</form>
Someone said I should use Ajax but I don't know how.
Wherever you want the second dropdown to be put:
<select class="form-control" data-val="true" id="AN ID" name="A NAME (NOT NEEDED)"></select>
In your AddictionChanged method, make an ajax call to the controller.
Something like:
var json = '{dropdownId: ' + //Value passed into function + '}';
$.ajax({
url: '#Url.Action("// Method", "// Controller")',
type:'POST',
data: json,
success: function(result){
// Do whatever
//Create a markup for a select
var markup = "<option Value='0'>Select option</option>";
//Populate the markup
for (var i = 0; i < result.length; i++) {
markup += "<option Value=" + result[i].Value + ">" + result[i].Text + "</option>";
}
//Populate dropdown with value
$("#//DROPDOWNLIST ID").html(markup).show();
}
});
So this calls the controller (that has the parameter dropdownId), passes it an id (whatever you are passing to this function). You, in your controller, would call a service/ db (Not sure how you project is structured) and return a list to this ajax/js function. The code inside the success creates the markup and inserts it inside a dropdown.

ActionLink to pass a input value from a different table cell to controller method

I'm working in MVC.
I have a table with a bunch of rows. One cell contains an 'Order' button, and a different cell contains a numeric input where the user can specify the quantity of the item he wants to buy? How do I pass through the quantity value in my ActionLink from the input cell? The issue here is not in finding a text value, but rather finding the text value of a specific row
Table Cells
<td><input id="itemQuantity" type="number" value="1" min="1" max="#item.QuantityInStock" size="10" class="universalWidth"/></td>
<td>#Html.ActionLink("Order", "OrderTuckShop", new { id = item.IngredientId, quanity = ???}, new { #class = "btn btn-success universalWidth" })</td>
Method Call
public ActionResult OrderTuckShop(Guid? id, int quantity)
You cannot add the value of the textbox in your ActionLink() method because its razor code, and that is parsed on the server before its sent to the client. In order to respond to client side events you need to use javascript/jquery.
Create a manual link and add the IngredientId as a data- attribute and add an additional class name to use as a selector
<td>
Order
</td>
In addition, remove the id="itemQuantity" in the input (duplicate id attributes are invalid html) and add a class name as a selector
<input class="quantity" type="number" ... />
Then include the following script to read the value of the input and make a redirect
var baseUrl = '#Url.Action("OrderTuckShop")';
$('.order).click(function() {
var id = $(this).data('id');
var quantity = $(this).closest('tr').find('.quantity').val();
location.href = baseUrl + '?id=' + id + '&quanity=' + quanity;
});
However, the name of the method suggests you perhaps should be making a POST, not a GET?

ASP.Net MVC adding dynamic EditorFor elements

I have a small project with an EditorTemplate.
I show some items which are initially in a List<T> but I want to be able
to add Items when the user presses a Button.
normally I add the items to the View like this
#for (int i = 0; i < Model.Models.Count; i++)
{
#Html.EditorFor(model => model.Models[i], "_AddArticleFullQuantity");
}
When I want to add items dynamically I tried to
create a button which uses ajax to call the server
<button id="addButton" type="button" class="btn btn-default btn-block" onclick="m_GUIRequests.AddArtikelToDiv()">add Article</button>
GUIRequests.prototype.AddArtikelToDiv = function ()
{
this.Request.CallAjax("/NewItemDelivery/GetPartialView_AddArticleFullQuantity", "", GUIRequests.AddToView);
}
GUIRequests.AddToView = function (html) {
$("#addedItems").append(html);
}
The button makes an ajax call to my controller which will do the following
public ActionResult GetPartialView_AddArticleFullQuantity()
{
WrongItemsReceivedModel model = new WrongItemsReceivedModel();
ModelContainer<WrongItemsReceivedModel> container = (ModelContainer<WrongItemsReceivedModel>)TempData["ModelContainer"];
container.Add(model);
return PartialView("~/views/Shared/EditorTemplates/_AddArticleFullQuantity.cshtml", container.Models[0]);
}
And in the end I get what I expected it will show me my template BUT the items initially shown from the List are numbered
So normally I have elements like:
<input class="form-control col-md-6 text-box single-line" data-val="true" data-val-required="MESSAGE" id="Models_0__ModelNumberID" name="Models[0].ModelNumberID" onchange="m_GUIRequests.SetWrongItemsReceivedValues()" type="text" value="">
But I get this:
<input class="form-control col-md-6 text-box single-line" data-val="true" data-val-required="MESSAGE" id="ModelNumberID" name="ModelNumberID" onchange="m_GUIRequests.SetWrongItemsReceivedValues()" type="text" value="">
I think its because I add one with the EditorFor "command" but the other one is added as PartialView.
Is there any way how I can add an EditorFor element so that my logic won't break ?
For editing a variable length list in ASP.NET MVC I would recommending reading the following article. It presents a very clean approach to implement this. On the server you will not need any TempData for persistence and also it illustrates the usage of a nice little helper that will allow you to generate the proper input field names.
As far as your question is concerned, you are correct that the reason why you get wrong input names is because when you return the partial view directly from the controller action, it no longer has the parent context of the Editor Template. There are some ways to circumvent this but it's very hacky and I would rather recommend the approach presented in the article.
Normally i would go for steven sanderson's blog post as Darrin mention as it has become as one of the the industry's standard. Yes partial view is a pain in your scenario.
In your scenario where you would want to keep editor template logic and dynamic added elements linked i would go and generate same name as editor for requries as below
This is my code just to give you the example.
$("#addItemdynamically").click(function () {
$.ajax({
url: '#Url.Action("GetNewGuid", "NewWebForms")',
cache: false,
success: function (newguid) {
id = newguid;
var html = '<tr class="editorRow">' +
'<td><input type="radio" id="Selected" name="Selected" value=' + id + ' /></td>' +
'<td><input type="hidden" name=\OptionsList.Index value=' + id + ' /></td>' +
'<td><input type="text" name=\OptionsList[' + id + '].Text /></td>' +
'<td><input type="hidden" name=\OptionsList[' + id + '].guid value=' + id + ' /></td>' +
'<td> delete</td>' +
'</tr>'
$("#editorRows tbody").append(html);
$("form").removeData("validator");
$("form").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse("form");
}
});
return false;
});
Basically my new guid is getting newly generated guid from the server side and appending it to the row which is generated by pressing add new item button.
you can generate int digit if you like in here but that also require some other hack.

javascript to dynamically add a textbox

I am trying to dynamically add a text box based on the selection of dropbox. When the user selects 'other' a text box gets generated asking them to explain the other. The user can dynamically add mutiple dropboxes, resulting in multiple text boxes if other is selected value in dropbox. Every generated dropbox has a unique name which gets read and placed in id of dynamically generated textbox.
The problem i am facing is that when there are multiple dropboxes and the first dropbox selection is something other that 'Other' and second dropbox value is other, the text box generated is placed in front of the first dropbox where it should be placed in front of the dropbox relevant to it.
The html code is as follows for the dropbox:
<div id="container">
<label id="rightlabel"for="dropbox1"></label>
<span>
<select id="frequency" onclick="getData(this, name)" name="dropbox[4fb103e3-06e7-4c88-8836-73b855968478]"></select>
<span class="field-validation-valid" data-valmsg-replace="true" data-valmsg-for="dropbox[4fb103e3-06e7-4c88-8836-73b855968478]"></span>
</span>
<div id="hiddenothertexbox"></div>
</div>
Javascript is as follows:
function getData(title, i) {
var value = title.options[title.selectedIndex].value;
var y = i.replace(/-/g, '')
$('#hiddenother').attr('id', y);
if (value == 'Other') {
str = '<label id="leftlabel">if other, please specify</label><span><input id="textboxid" class="text-box single-line" type="text" value="" name="textbox ></input></span>';
$('#'+y).html(str);
}
else {
str = '';
$('#'+y).html(str);
}
}
teh javascript gets the names of the dropbox and replaces the id of 'hiddenanothertextbox' with that id so its unique. I have an idea of the problem, i think its because when the user does not click teh first dropbox the id of 'hiddenanothertextbox' does not change for the first dropbox and when another dropbox is added and the value is changed, the hiddenanothertextbox for first dropox value changes adding it in front of first not second. I am struggling to achieve teh required result.
UPDATED JAVASCRIPt
function getData(title, i) {
var value = $(title).val();
var y = i.replace(/-/g, '');
$('#hiddenother').attr('id', y);
if (value == 'Other') {
str = '<label id="leftlabel">if other, please specify</label><span><input id="textboxid" class="text-box single-line" type="text" value="" name="textbox"" ></input></span>';
$(title).after(str);
}
else {
$(title).nextUntil('#textboxid').remove();
}
}
Working Demo
Use this code snippet.
var flag = 0;
function getData(title, i) {
var value = $(title).val();
var y = i.replace(/-/g, '');
$('#hiddenother').attr('id', y);
if (value == 'Other' ) {
if(flag == 0){
flag=1;
str = '<label id="leftlabel">if other, please specify</label><span><input id="textboxid" class="text-box single-line" type="text" value="" name="textbox"></input></span>';
$(title).after(str);
}
}
else {
flag=0;
$(title).nextUntil('#textboxid').remove();
}
}
Html :
<select id="frequency" onchange="getData(this, name)" name="dropbox[4fb103e3-06e7-4c88-8836-73b855968478]">
Changes :
var value = $(title).val() gives you the correct value
Use onchange="getData(this, name)" instead of onclick=".."
In variable str : name="textbox" did not have ending "
use .after() or .append() instead of .html()

How to perform a simple multiple selection in a table ASP.NET MVC4

Here is what my view looks like:
#model Affiliate
<div class="box paint color_16">
<div class="title">
<h4><i class="icon-tasks"></i><span>#Model.CompanyName's Commissions</span> </h4>
</div>
<div class="content top ">
<div class="subtitle">
#Html.ActionLink("Void", "DeleteInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
#Html.ActionLink("Create Invoice", "CreateInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
#Html.ActionLink("Pay", "PayInvoice", new { commList = "??", affId = Model.Id }, new { #class = "btn" })
</div>
<table class="table table-striped table-hover">
<tr>
<h3>Commissions</h3>
</tr>
<tr>
<td></td>
<td>Amount</td>
<td>Status</td>
<td>Action</td>
</tr>
#foreach (var item in Model.Commissions)
{
<tr>
#if (item.Status == ViewBag.PaymentStatus || ViewBag.PaymentStatus == "All")
{
<td>#Html.CheckBox("commId", new { value = item.Id })</td>
<td>#Html.DisplayFor(x => item.PayoutAmount)</td>
<td>#Html.DisplayFor(x => item.Status)</td>
}
</tr>
}
</table>
</div>
What I want to be able to do is when I hit an actionlink on the top, grab all the items from the table that are checked, and pass that list of id's to the controller logic. I am assuming a viewmodel may be the solution, something like this:
public Affiliate affilite { get; set; }
public List<int> selectedItems { get; set; }
etc.
But how to I get the selected Items into that VM selectedItems container?
Based off your comments, you don't seem to be looking for the most "correct" answer, but rather just a quick and dirty "how would I do this" answer. If you just want to pass the list, you could setup your controller action like this:
public ActionResult MyAction(int[] id)
{
...
}
Or, you seem to indicate it is strongly typed to a view model with a property that contains a List (I would shorten the name of the property, you'll see why in a second).
In javascript, the easiest thing to do would be to use jQuery to bind a click event on your hyperlink that gets the list of items that are checked and appends that to the query string.
$("#myLink").click(function()
{
var url = "site.com/action?";
var ids = $(".table").find("input:checked");
ids.each(function()
{
url += "id=" + $(this).val() + "&"
});
window.location = url;
});
Basically, you want to create one long query string with the action parameter's name repeated over and over, which identifies an array. It looks something like this (id is for int[] id in MyAction):
id=15&id=20&id=25&id=30&....
And then once the query string is built, redirect the user to that url. MVC should then be able to bind that to an array and you're all set.
That's basically the idea, anyway; the syntax and the javascript I wrote could be way off so don't copy my code and expect it to work as is - I wrote that off the top of my head. If your action is bound to a viewmodel, then you need to set the parameter in the query string to the name of the property of your model:
selectedids=1&selectedids=2&selectedids=3...
Or, if the array is a property of an object, which is a property of the model...
model.selectedids=1&model.selectedids=2&model.selectedids=3...
You'll just need to play around with it some.
Use html checks inside form tag ( you could use helpers too) and post the model to a post action.
MVC will serialize the model automatically

Categories

Resources