How to get an array of elements of one model from the form asp.net core? - c#

I need to get an array of elements of one model from the form. Then return the same array back to shape.
Model:
public class Check
{
public int Id { get; set; }
public string Text { get; set; }
public int Number1 {get; set;}
public int Number2 {get; set;}
}
Controller:
[HttpGet]
public IActionResult Check(string tema)
{
IEnumerable<Check> less = _context.Checks.Where(e => e.Text == tema);
return View(less);
}
[HttpPost]
public IActionResult Check(IEnumerable<Check> Checks)
{
return View(Checks);
}
Form:
#model IEnumerable<Check>
#foreach (var check in Model)
{
<form asp-action="Check" asp-controller="Home" method="post">
<p>#check?.Text</p>
<p><input type="text"asp-for="#check.Number1"/></p>
#if(Number1 == Number2)
{<p>ok</p>}
<div class="form-group">
<input type="submit" value="Ok" class="btn btn-default" />
</div>
</form>
}
I have everything working for one model, and if I pass an array, I get an empty form. How to fix it?

1.If you just want to post single model, but the backend you want receive the IEnumerable<Check>, you need add name attribute(name="[0].propertyName") to override the asp-for generated name="check.propertyName"
2.For your code, it will generate multiple forms with submit button. If you want to post an array model, you need move <form> and submit button outside foreach loop.
3.If post array model from view to controller. The IEnumerable<Check> Checks is a list model, so the model binding system would find the name by [i].propertyName.
4.<p> element text cannot passed to backend by form submit. If you want to post it, try to add a hidden input.
If post array model from view to controller, change your view like below:
#model IEnumerable<Check>
#{
int i=0; //add this...
}
<form asp-action="Check" asp-controller="Home" method="post">
#foreach (var check in Model)
{
<p>
#check?.Text
<input type="text"asp-for="#check.Text" name="[#i].Text" hidden/> #* //add the hidden input*#
</p>
//add the name...
<p><input type="text"asp-for="#check.Number1" name="[#i].Number1"/></p>
#if(check.Number1 == check.Number2)
{<p>ok</p>}
i++; //add this...
}
<div class="form-group">
<input type="submit" value="Ok" class="btn btn-default" />
</div>
</form>
If you post single model, change your view like below:
#foreach (var check in Model)
{
<form asp-action="Check " asp-controller="Home" method="post">
<p>
#check?.Text
<input type="text"asp-for="#check.Text" name="[0].Text" hidden/> #* //add the hidden input*#
</p>
#*add the name...*#
<p><input type="text"asp-for="#check.Number1" name="[0].Number1"/></p>
#if(check.Number1 == check.Number2)
{<p>ok</p>}
<div class="form-group">
<input type="submit" value="Ok" class="btn btn-default" />
</div>
</form>
}

Related

Add new element from View (PartialView) to #Model and send it to Controller

I`m using ASP.NET Core 5.0 and wanna understand how to correctly add new element from View to #Model and send it to Controller
I have Request.cs (this is Model that I send to View, which contains [NotMapped] List with elements from DB)
In View I display List using PartialView using foreach loop.
foreach(var appeal in appeals)
{
#await Html.PartialAsync("/Views/Data/Modal/Partial/_FoivAppealPartial.cshtml", appeal);
}
Using add button I can add new PartialView with new FoivAppeals class
<button class="appealaddbutton" id="addfoivappeal"><i class="fas fa-plus"></i></button>
$("#addfoivappeal").click(function () {
$.ajax({
type: "POST",
url: '#Url.Action("DisplayNewFoivApeal","Data")',
success: function (html) {
$("#foivappeals").append(html);
}
});
return false;
});
This is PartialView for FoivAppeals class
#model FoivAppeals
#inject DataBaseContext db
<div class="container">
<div class="row" >
<div class="col-5">
<label for="inp" class="inp">
<input class="fields" type="date" id="ffdt" asp-for="#Model.Datetime" name="#datename"/>
<span class="label">Дата</span>
<span class="focus-bg"></span>
</label>
</div>
<div class="col-5">
<label for="inp" class="inp">
<input class="fields" asp-for="#Model.Number" placeholder=" " required name="#numbername"/>
<span class="label">Номер</span>
<span class="focus-bg"></span>
</label>
</div>
<div class="col-2 appealdeletediv">
<a style="cursor: pointer;" class="deleteFoivRow"
onclick="$(this).parent().parent().remove();">
<i class="fas fa-minus-circle appealdeletebutton"></i>
</a>
</div>
</div>
</div>
Using
$(this).parent().parent().remove();
I can delete unnecessary elements
After all the manipulations, I need to transfer new data that is currently displayed in View to Controller.
E.g. I get 1 FoivAppeals from DB to List
In View I add one more.
So, when I click sumbit button in form I wanna see 2 elements in List in Controller instead of 1.
And so the question is....
Is there a universal way to do this?
Request Model
public class RequestModel
{
public int Id {get;set;}
public string IncomeNumber {get;set;}
[NotMapped]
public List<AppealModel> Appeals {get;set;}
}
AppealModel
public class AppealModel
{
public int Id {get;set;}
public string Number {get;set;}
public string Date {get;set;}
public RequestModel {get;set;}
}
This is the answer.
I don't know how it works but it works.
https://www.findandsolve.com/articles/add-more-example-razor-page-using-aspnet-core-with-code-example-step-by-step

Posting data from View to Controller with Check Box

I have the following class:
public class Entidade
{
public string Voz { get; set; }
public bool Valor { get; set; }
}
I generate the View (GET) with the controller:
public IActionResult Entidade()
{
var model = GetVoidEntidade.Entidades();
return View(model);
}
Where GetVoidEntidade.Entidades() will generate a List of "Entidade" with 5 rows, each of them having an "Entidade" with a unique string for "Voz" and "Val" will be false.
The View:
<form asp-action="Entidade">
#{
int i = 0;
}
#foreach (var entidade in Model)
{
var name = $"Valor[{i++}]";
<input type="checkbox" value="#entidade.Valor" class="form-check-input" name=#name />#entidade.Voz<br />
}
<div class="form-group">
<input type="submit" value="Próximo" class="btn btn-success align-content-end float-md-right" />
</div>
</form>
The POST controller is just there so that I can, trough the debugger pick the return:
[HttpPost]
public IActionResult Entidade([FromBody]IEnumerable<Entidade> model)
{
var boh = Request.Form.Files;
return View();
}
When I run it and try to execute the POST I get a message of error 405: Method Not Allowed, and sure enough, don't get the data.
How can I receive the data from the Check Box into the controller?
[FromBody] means you're sending something like application/json or application/xml. An HTML form sends as x-www-form-urlencoded or multipart/form-data. Remove the [FromBody] attribute from the action param. That solves your immediate problem, but afterwards, you're just going to get an empty model because your input names are not correct.
First, you need to use a for rather than a foreach, as you need to index the list. Second, the name you're generating currently isn't even remotely correct. It would need to be something like: [N].Valor where N is the index. Third, you should just use the tag helper so you don't even need to think about it. In other words, you code should be:
<form asp-action="Entidade">
#for (var i = 0; i < Model.Count; i++)
{
<input asp-for="#Model[i].Valor" type="checkbox" class="form-check-input" />
#entidade.Voz<br />
}
<div class="form-group">
<button type="submit" class="btn btn-success align-content-end float-md-right">Próximo</button>
</div>
</form>
name element should be same for all item in the same group
#foreach (var entidade in Model)
{
//var name = $"Valor[{i++}]";
<input type="checkbox" value="#entidade.Valor" class="form-check-input" name="StaticName" />#entidade.Voz<br />
}

IFormFile always null (ASP.NET Core with MVC/Razor)

I have an ASP.NET Core MVC app attempting to upload an IFormFile. However, the IFormFile is always null. None of the other solutions I've found have solved this issue. What am I doing wrong?
Model
public class EmailForm
{
[Display(Name = "Add a picture")]
[DataType(DataType.Upload)]
[FileExtensions(Extensions = "jpg,png,gif,jpeg,bmp,svg")]
public IFormFile SubmitterPicture { get; set; }
}
Controller
public async Task<ActionResult> Contribute([Bind("SubmitterPicture")] EmailForm model)
{
if (ModelState.IsValid)
{
//Do some stuff
}
}
View
<form method="post" asp-action="Contribute" asp-antiforgery="true" enctype="multipart/form-data" >
<div class="form-group" >
<div class="col-md-2">#Html.LabelFor(m => m.SubmitterPicture)</div>
<div class="col-md-4">
<input type="file" name="SubmitterPicture" id="SubmitterPicture" />
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Contribute" />
</div>
</div>
<form method="post" enctype="multipart/form-data">
</form>
enctype="multipart/form-data"
The multipart form data is the key.
You can alternatively get the file from the HttpContext.Request.Form.Files and get rid of the IFormFile interface in your model. I recommend this method as I believe that files have nothing to do with data models.
The example would be:
public IActionResult Index()
{
//Please note that if no form data is posted
// HttpContext.Request.Form will throw an exception
if (HttpContext.Request.Form.Files[0] != null) {
var file = HttpContext.Request.Form.Files[0];
using (FileStream fs = new FileStream("Your Path", FileMode.CreateNew, FileAccess.Write, FileShare.Write)) {
file.CopyTo(fs);
}
}
return View();
}
If this method also fails, that means there is something wrong with the multipart request.
After sitting with the same problem for hours I found the solution.
The Problem:
Submitting a single input in a form.
The Conclusion:
Basically in your html form this won't work if you only have one input besides the submit button. My property was no longer null as soon I added another property on my viewModel and another input in my form.
I hope this helps someone else.
Razor Page HTML:
#page
#model SomeModel
<form method="post" enctype="multipart/form-data">
<div class="form-group">
<div class="col-md-10">
<p>Upload one or more files using this form:</p>
<input asp-for="Input.SingleInput" type="file" class="form-control" />
<input asp-for="Input.AnotherInput" class="form-control" />
</div>
</div>
<div class="form-group">
<div class="col-md-10">
<input type="submit" value="Upload" />
</div>
</div>
</form>
Razor Page Code Behind:
public class SomeModel: PageModel
{
[BindProperty]
public SomeViewModel Input { get; set; }
public async Task OnGetAsync()
{
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
// This won't be null.
var showMeSomething = Input.SingleInput;
return RedirectToPage();
}
}
SomeViewModel:
public class SomeViewModel
{
public IFormFile SingleInput{ get; set; }
public string AnotherInput{ get; set; }
}
I found that by providing names on my html form, I was able to map to parameters (ASP.NET Core 2.1):
Client side:
<form method="post" enctype="multipart/form-data" action="...">
<input type="file" name="myFile" required />
<input type="password" name="myPass" required />
<input type="submit" value="post" />
</form>
Server side:
[HttpPost()]
public async Task<IActionResult> Post(IFormFile myFile, [FromForm]string myPass)
{
//...
}
Similarly, if anyone working in .Net Core 2.1 and are using asp-for inorder bind the model for your input elements, then do not give name & id properties for that <input> elements. Ideally, the InputTagHelper upon the rendering on the browser generates the name and id properties with their values. If you give the same value for name & id w.r.t Model class, then everything works fine. Else, system doesn't know to which model property it should bind.
Better approach is not to give id and name on the <input>
Below is the sample code.
<form id="uploadForm" enctype="multipart/form-data" name="uploadForm" asp-action="UploadExcel" method="post">
<div class="form-group form-group-lg form-group-sm row ">
<div class="col-sm-12 col-md-10 col-lg-10 uploadDiv" style="display: flex !important">
<label asp-for="FileName" class="col-sm-12 col-md-10 col-lg-10" style="font-size: 15px; max-width: fit-content ">File Name :</label>
<input asp-for="FileName" class="form form-control fileName"
type="text"
placeholder="Enter your file name" />
<!--File upload control-->
<label asp-for="FileName" class="col-sm-12 col-md-10 col-lg-10" style="font-size: 15px; max-width: fit-content ">Attachment :</label>
<input asp-for="File" required class="form-control" type="file" placeholder="File Name" />
</div>
</div>
<div class=" form-group form-group-lg form-group-sm row">
<span asp-validation-for="FileName" class="text-danger"></span>
</div>
<div class=" form-group form-group-lg form-group-sm row">
<small>Please upload .xls or .xlxs or json or xml formatted files only</small>
</div>
<div class="form-group form-group-lg form-group-sm row">
<div class="col-sm-12 col-md-10 col-lg-10">
<input type="submit" class="btn btn-primary" name="submit" id="fileUploadButton" value="Upload" />
<input type="reset" class="btn btn-Gray" name="result" id="resetButton" value="Reset" />
</div>
</div>
<a asp-action="DownloadTemplate" asp-controller="Download" title="Click to download template">Import Batch Transactions Template</a>
</form>
Model.cs
public class ExcelUploadViewModel
{
/// <summary>
/// Gets or Sets the FileName
/// </summary>
[Required(ErrorMessage = "FileName is required")]
public string FileName { get; set; }
[Required(ErrorMessage = "File is required")]
[DataType(DataType.Upload)]
[FromForm(Name = "File")]
public IFormFile File { get; set; }
}
Upon Submit
Thank you.
Your code looks perfectly fine and it should work as long as you are using the same version of code you posted on the question. I have a very strong feeling that you are getting false for the ModelState.IsValid expression and hence seeing the some sort of unexpected behavior.
The FileExtensions data annotation is supposed to be used with String type properties, not with IFormFile type properties. Because of this reason, the IsValid returns false.
So remove that from your view model.
public class EmailForm
{
[Display(Name = "Add a picture")]
[DataType(DataType.Upload)]
public IFormFile SubmitterPicture { get; set; }
}
Here is a relevant GH issue and explanation from one of the team member, for your reference.
FileExtensions Data annotation invalid ModelState #5117
I had this issue and the fix was to make sure the element of "input type="file"" had an id and name set. Then, in the controller, set the IFormFile parameter name to be exactly the same, case as well.
G-luck
Adding the below to my controller fixed the issue for me. It was because my file was large. Adjust the limits as needed.
[RequestFormLimits(ValueLengthLimit = int.MaxValue, MultipartBodyLengthLimit = int.MaxValue)]

Trying to get data from a textbox in a form inside view to controller in asp.netmvc

I am trying to get data from a textbox in a form inside view to controller in asp.net-mvc.
My requirement is to compare Token_No. with some id and
Password with some existing password. How to get get those values at controller side on button click inside if condition.
I am not using razor syntax so how to accomplish this situation without using razor syntax and strongly bind with model.
Code in view
<form name="ctl00" id="ctl00" action="HomeController/Index" method="post" data-dpmaxz-fid="1">
<div class="input-group">
<span class="input-group-addon"> <i>Token_No.</i></span>
<div class="form-group is-empty">
<input name="txtToken" class="form-control" id="txtToken" type="text" placeholder="Token No..." data-dpmaxz-eid="5">
<span class="material-input"></span>
</div>
</div>
<div class="input-group"> <span class="input-group-addon">
<i class="material-icons">lock_outline</i> </span>
<div class="form-group is-empty">
<input name="txtPswd" class="form-control" id="txtPswd" type="password" placeholder="Password..." data-dpmaxz-eid="5">
<span class="material-input"></span>
</div>
</div>
<div class="footer text-center">
<input name="btn_Login" class="btn btn-primary" id="btn_Login" type="submit" value="Login" data-dpmaxz-eid="6">
</div>
</form>
Code in Controller
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(EmpDetails model)
{
if (model.token_no.="197418")
{
}
return View();
}
}
you can generate the view using controller method & create a strongly typed view. then you can choose a model class & select scaffold template as what you want..you can select create,edit,delete..etc as you want.then you can access the text box values using form collection.
[HttpPost]
public ActionResult authenticateUser(FormCollection formCollection)
{
int token = int.Parse(formCollection["txtToken"]);
string pswd = formCollection["txtPswd"];
return View();
}

Input form not binding values to the returned model

I have Controller that looks like this:
public class PollController : BaseController
{
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public Task<IActionResult> AddVotingRecord(VotingRecordBindingModel model)
{
throw new NotImplementedException();
}
}
My view is:
#model IEnumerable<SmartStoreNetCore.Web.Models.ViewModels.PollViewModel>
<div class="list-group-item text-muted title">COMMUNITY POLL</div>
#foreach (var poll in Model)
{
<form asp-controller="Poll" asp-action="AddVotingRecord" method="post">
<div class="list-group">
<div class="list-group-item title">#Html.Raw(poll.Name)</div>
#foreach (var pollAnswer in poll.PollAnswers)
{
<div class="list-group-item text-muted">
<input asp-for="#pollAnswer.VotingRecordBindingModel.PollAnswerId"
type="radio"
value="#pollAnswer.Id"/>
#Html.Raw(pollAnswer.Name)
</div>
}
<div class="list-group-item title">
<input class="btn btn-warning btnCustom" type="submit" value="VOTE"/>
</div>
</form>
}
Where PollViewModel contains collection of PollAnswersViewModel where is the VotingRecordBindingModel which has property PollAnswerId.
The code above generates the right HTML and the values are the corect ids.
I am getting the VotingRecordBindingModel returned to the Controller but its property PollAnswerId is always 0.
I can't figure out what is going wrong.
EDIT:
I tried to add GET method AddVotingRecord() that returns new VotingRecordBindingModel() but without result.

Categories

Resources