Clearing a collection with c# / asp.net model binding - c#

Is it possible to clear a collection with model bindning?
Sample model class:
public class TestModel
{
public String Name
{
get; set;
}
public List<String> Strings
{
get; set;
}
}
Sample Html:
<form>
<input type="text" name="Name" value="New name" />
<input type="text" name="Strings[]" value="New str1" />
<input type="text" name="Strings[]" value="New str2"/>
<input type="text" name="Strings[]" value="New str3"/>
<input type="submit" />
</form>
And controller code:
public IActionResult Post()
{
var model = new TestModel() { Name = "Old name" };
model.Strings = new List<String>();
model.Strings.Add("Old str 1");
model.Strings.Add("Old str 2");
this.TryUpdateModelAsync(model);
}
In this case the TryUpdateModelAsync will update both Name and Strings but what if I would like to clear the list with TryUpdateModelAsync?
changing html to:
<form>
<input type="text" name="Name" valuye="New name" />
<input type="text" name="Strings" value="" />
<input type="submit" />
</form>
will of course update Strings to a list with one empty / null value and removing input for Strings will keep the old list intact (as expected), but is there any magic trick to just clear that list or set it to null?

Related

How to send form values of array/list without correct indexing?

Let's assume that we have model and View like this:
public class Test
{
public List<string> data { get; set; }
}
<form asp-action="View">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input name="data[0]" value="dsa" />
<input name="data[1]" value="asd" />
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
[HttpPost]
public IActionResult View(Test input)
{
return View();
}
I'm sending this form to the controller's method and he sees those elements of data properly.
But if I change indexing to:
<input name="data[0]" value="dsa" />
<input name="data[2]" value="asd" />
then it is not going to work. I know that it makes sense in this way, but is there an option to disable index checking and just insert all those values into list/array with correct indexing?
So, in 2nd case data[0] would be data[0], but data[1] would be data[2].
Just for every "gap" in indexing, move Next objs to fil lthat gap.
You can use just:
<input name="data[]" value="dsa" />
<input name="data[]" value="asd" />
If you specify the index at all, though, it must be in order.

Dictionary inside IDictionary MVC

I had to alter a radio button to include a new value.
To do this I had to put a Dictionary inside an IDictionary. The key pulls in great for the IDictionary but the Dictionary int and string don't pull in at all.
I believe it's my front end code.
Can anyone see what I am doing wrong and provide an example on how to fix?
Action Parameter in Controller
IDictionary<String, Dictionary<int, String>>
View
<fieldset>
<legend class="sr-only">Actions</legend>
<input type="hidden" name="customerId" value="#account.CustomerId" />
<input type="hidden" name="yearSetupId" value="#account.YearId" />
<label class="radio-inline"><input type="radio" name="accountActions[#(account.CustomerId)].Key[#(account.Id)].Value" value="" checked="checked">Do nothing</label>
#{
var possibleActions = account.Balance < 0 ? new[] {
BalanceAdjustmentType.Zeroed, BalanceAdjustmentType.Issued }
: new[] { BalanceAdjustmentType.Under, BalanceAdjustmentType.Sent };
}
#foreach (var action in possibleActions)
{
<label class="radio-inline"><input type="radio" name="accountActions[#(account.CustomerId)].Key[#(account.YearId)].Value" value="#action.BalanceId">#action.Text</label>
}
</fieldset>
I didn't understand your controller parameter means actually.
But I supposed the "accountActions" in your view was the variable of your "controller parameter".
If so, then your could access the nested dict value by accountActions[Key][InnerKey].
<fieldset>
<legend class="sr-only">Actions</legend>
<input type="hidden" name="customerId" value="#account.CustomerId" />
<input type="hidden" name="yearSetupId" value="#account.YearId" />
<label class="radio-inline">
<input type="radio" name="#accountActions[account.CustomerId][account.Id]" value="" checked="checked">
Do nothing
</label>
#{
var possibleActions = account.Balance < 0
? new[] { BalanceAdjustmentType.Zeroed, BalanceAdjustmentType.Issued }
: new[] { BalanceAdjustmentType.Under, BalanceAdjustmentType.Sent };
}
#foreach (var action in possibleActions) {
<label class="radio-inline">
<input type="radio" name="#accountActions[account.CustomerId][account.YearId]" value="#action.BalanceId">
#action.Text
</label>
}
</fieldset>

Passing value from textbox to controller as a parameter

So in my View I need a simple textbox like so:
<input type="text" name="CVR" placeholder="Enter CVR" required />
in the same view, at the end I have the submit button:
#Html.ActionLink("Submit", "SendMail", new { studentId = Html.ViewData.Model.StudentId, companyId = Html.ViewData.Model.CompanyId, applicationId = Html.ViewData.Model.ApplicationId, companyCVR = Model.CVR})
The data works, until the "companyCVR = Model.CVR". I am having trouble changing "companyCVR" to the text the user inputs in the textbox above.
This is my controller constructor:
public ActionResult SendMail(string studentId, string companyId, int applicationId, string companyCVR)
and technically, this should work:
contract.CVR = companyCVR;
It works when I hardcoded (contract.CVR = "12345"). My problem is passing the value from the textbox to companyCVR which I am trying to pass as a parameter to the controller.
How can I solve this?
It would probably be easier if you use a <form>. Something like:
#using (Html.BeginForm("SendMail", "Contract", null, FormMethod.Post), new { id = "myForm" })
{
<input type="hidden" value=#Html.ViewData.Model.StudentId name="studentId" />
<input type="hidden" value=#Html.ViewData.Model.CompanyId name="companyId" />
<input type="hidden" value=#Html.ViewData.Model.ApplicationId name="applicationId" />
<input type="text" name="companyCVR" />
}
<input type="button" value="Submit" onclick="$('#myForm').submit();" />

Wrong hidden filed value is being passed to the Action Method

Update :
This is an SPA app.So it's having js file also for supporting the submit button.So I think problem is on it.Could you tell me how to modify it to support multiple kind of submit buttons ? At this moment I think it supports only for a single submit button.That is why it always get the first form's hidden field value I think.Thanks.
JS
var $loginForm = $('.login-form');
$loginForm.submit(function (e) {
e.preventDefault();
if (!$('.login-form').valid()) {
return;
}
abp.ui.setBusy(
null,
abp.ajax({
contentType: app.consts.contentTypes.formUrlencoded,
url: $loginForm.attr('action'),
data: $loginForm.serialize()
})
);
});
UI
VM
public class LoginViewModel
{
public string TenancyName { get; set; }
[Required]
public string UsernameOrEmailAddress { get; set; }
[Required]
public string Password { get; set; }
public bool RememberMe { get; set; }
}
CompanyLoginFormViewModel VM :
public class CompanyLoginFormViewModel
{
public LoginViewModel LoginViewModel { get; set; }
public List<TenantListDto> Tenants { get; set; }
}
*.cshtml page
#{
var companyLoginFormViewModel = TempData["CompanyLoginFormViewModel"] as CompanyLoginFormViewModel;
}
#foreach (var tenant in companyLoginFormViewModel.Tenants)
{
<form class="login-form" action="#Url.Action("Login")?returnUrl=#ViewBag.ReturnUrl" name="companyLoginForm" method="post">
<input type="hidden" name="usernameOrEmailAddress" value="#companyLoginFormViewModel.LoginViewModel.UsernameOrEmailAddress" />
<input type="hidden" name="password" value="#companyLoginFormViewModel.LoginViewModel.Password" />
<input type="hidden" name="rememberMe" value="true" />
<input type="hidden" name="companyUrl" value="true" />
<input type="hidden" name="tenancyName" value="#tenant.TenancyName" />
<div class="row margin-top-10">
<div class="col-xs-3">
<button type="submit" class="btn btn-success uppercase">#L("LogIn")</button>
</div>
</div>
</form>
}
Generated html
<form class="login-form" action="/Account/Login?returnUrl=/Application" name="companyLoginForm" method="post" novalidate="novalidate">
<input type="hidden" name="usernameOrEmailAddress" value="fake#gmail.com">
<input type="hidden" name="password" value="fake">
<input type="hidden" name="rememberMe" value="true">
<input type="hidden" name="companyUrl" value="true">
<input type="hidden" name="tenancyName" value="Asset_Management">
<div class="row margin-top-10">
<div class="col-xs-3">
<button type="submit" class="btn btn-success uppercase">Log in</button>
</div>
</div>
</form>
<form class="login-form" action="/Account/Login?returnUrl=/Application" name="companyLoginForm" method="post" novalidate="novalidate">
<input type="hidden" name="usernameOrEmailAddress" value="fake#gmail.com">
<input type="hidden" name="password" value="fake">
<input type="hidden" name="rememberMe" value="true">
<input type="hidden" name="companyUrl" value="true">
<input type="hidden" name="tenancyName" value="Associates">
<div class="row margin-top-10">
<div class="col-xs-3">
<button type="submit" class="btn btn-success uppercase">Log in</button>
</div>
</div>
</form>
<form class="login-form" action="/Account/Login?returnUrl=/Application" name="companyLoginForm" method="post" novalidate="novalidate">
<input type="hidden" name="usernameOrEmailAddress" value="fake#gmail.com">
<input type="hidden" name="password" value="fake">
<input type="hidden" name="rememberMe" value="true">
<input type="hidden" name="companyUrl" value="true">
<input type="hidden" name="tenancyName" value="ALL">
<div class="row margin-top-10">
<div class="col-xs-3">
<button type="submit" class="btn btn-success uppercase">Log in</button>
</div>
</div>
</form>
Post method
[HttpPost]
public virtual async Task<JsonResult> Login(LoginViewModel loginModel, string returnUrl = "", string returnUrlHash = "", bool companyUrl = false)
{
CheckModelState();
// removed for clarity
}
Question : Even though I have press the 2nd submit button,it always send the tenancyName as first submit button's value.That is Asset_Management.Could you tell me why ? Thanks.
Your problem is with the script.
var $loginForm = $('.login-form');
is a collection of all your forms, but
data: $loginForm.serialize(),
will only serialize the first one, so you always posting the vales of the first form. Modify the script to handle the buttons .click() event and get its associated form
$('.btn-success').click(function(e) {
e.preventDefault(); // if you makes the button type="button" this is not required
var form = $(this).closest('.login-form');
if (!form.valid()) {
return;
}
abp.ui.setBusy(
null,
abp.ajax({
contentType: app.consts.contentTypes.formUrlencoded,
url: form.attr('action'),
data: form.serialize()
})
);
});
Why do you even have <form> and <button> at all?
Why not create links in your foreach loop, something like this:
#Html.ActionLink("Login for " + tenant.Name, "LoginAction", new {Id=tenant.Id})
You can style these links all blue and pretty as you like using CSS afterwards.
Update1, you can pass parameters to your controller using the anonymous object. Do you see how I am passing Id? Your Action will need to accept id, see this answer: passing multiple parameters in #html.actionlink()
Update2, passing username and password like this is very bad practice. You are exposing secure credentials to the view. You should forward the user to the login page with username + password input boxes where the user will login.

HTML checbkox values to C# array

I have list of html checboxes in form and I need get checked checboxes values to C# array after form sent, it is possible?
<form id="form1" action="" method="post">
#foreach (var category in ViewBag.Categories)
{
<ul>
<li>
<input type="checkbox" name="Category" value=#category["UUID"] />#category["CategoryName"]<br /> //Generate >20 checkboxes
</li>
</ul>
}
<button type="submit" formmethod="post">Search</button>
</form>
First of all change following line:
<input type="checkbox" name="Category" value=#category["UUID"] />#category["CategoryName"]<br />
to:
<input type="checkbox" name="Category" value="#category["UUID"]"/>#category["CategoryName"]<br />
and now in your action you can get it from Request object or by adding a parameter of name Category to get in the action:
[HttpPost]
public ActionResult SomeAction()
{
var checkedCategories = Request.Form["Category"];
}
or:
[HttpPost]
public ActionResult SomeAction(int[] Category)
{
}
<input type="checkbox" name="Category" value=#category["UUID"] runat="server" />#category["CategoryName"]<br />
include runat="server" in the input type. you can acess in the code behind.

Categories