I am generating my form elements by looping over a array. So I have a array = ["name", "age"] and I loop over each item and create a textbox with the appropriate name and related data.
Therefore I creating my form element dynamically such that
<input class="input-validation-error text-box single-line" data-val="true" data-val-required="#arr[i] is required" id="#arr[i]" name="#arr[i]" type="text" value="">
<span class="field-validation-error" data-valmsg-for="#arr[i]" data-valmsg-replace="true"></span>
Instead of :
#Html.EditorFor(model => model.age)
#Html.ValidationMessageFor(model => model.age)
However, because of this, the client-side messages are not being generated. It would catch the error in the server-side validation but client-side stop working.
How can I get the client-side message to work while keeping the ability to create the form dynamically, such that in the blew line of codes the model's property-name can be provided dynamically? Is there a way?
#Html.EditorFor(model => model[#arr[i]])
#Html.ValidationMessageFor(model => model[#arr[i]])
I know that above code doesn't work but its just to emphasize what I am looking for in a solution.
You need to explicitly register validation inline or with JavaScript.
More jQuery Validate Examples (Below is a brief example)
$("#myform").validate({
rules: {
name: "required"
}
});
Inline ex:
<input id="age" name="age" required />
This works:
#model Testy20161006.Controllers.MessageViewModel
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>IndexStackOverflow900</title>
</head>
<body>
<div>
#using (Html.BeginForm())
{
int i = 0;
foreach (var arr in Model.myArray)
{
<input class="input-validation-error text-box single-line" data-val="true"
data-val-required="#arr is required" id=#arr name=#arr type="text" value="">
<br />
#Html.ValidationMessage(arr);
i++;
}
<input type="submit" value="submit" />
}
</div>
</body>
</html>
Controller/Model:
public class MessageViewModel
{
public List<string> myArray = new List<string>();
[Required]
public string name { get; set; }
[Required]
public string age { get; set; }
}
public class HomeController : Controller
{
[HttpPost]
public ActionResult IndexStackOverflow900(MessageViewModel mvm)
{
if (ModelState.IsValid)
{
}
else
{
//you can narrow it down to which field caused the error by inspecting ModelState
//List<ModelErrorCollection> errors = controller.ModelState.Select(x => x.Value.Errors)
// .Where(y => y.Count > 0)
// .ToList();
ModelState.AddModelError("name", "name is required");
ModelState.AddModelError("age", "age is required");
}
FactorCode(mvm);
return View(mvm);
}
public ActionResult IndexStackOverflow900()
{
MessageViewModel mvm = new MessageViewModel();
FactorCode(mvm);
return View(mvm);
}
public void FactorCode(MessageViewModel mvm)
{
mvm.myArray.Add("name");
mvm.myArray.Add("age");
}
Related
I am building a logistics application where warehouse items are logged in a database with CRUD functionality.
I am now stuck with an issue of editing amount of stocked inventory in a given warehouse, where I am trying to dynamically generate a list of a ViewModel (with for input), and post the edited input to a [HttpPost] method. The list of divs generates as it should in the view, but the edited input does not pass into my controller method. I managed to pass it before but edited the code and restarted the application so I know I'm close. What am I doing wrong?
Since the ViewModel inherits properties from other classes I also guess it needs to be of model type IEnumerable<> in the view.
Class:
public class LagerSaldoVM
{
public string Lager { get; set; }
public string Produkt { get; set; }
public int Saldo { get; set; }
public IEnumerable<Produkt> Produkter { get; set; }
public IEnumerable<Fardigvarulager> Lagerhus { get; set; }
}
Controller method [HttpGet]:
public IActionResult Edit(int LagerID)
{
List<LagerSaldoVM> ls = new List<LagerSaldoVM>();
string error = "";
LagerSaldoMetod lsm = new LagerSaldoMetod();
ls = lsm.getLagerSaldoFor(LagerID, out error);
ViewBag.LagerID = LagerID;
foreach (var item in ls)
{
ViewBag.Lager = item.Lager;
}
return View(ls);
}
View:
#model IEnumerable<LagerSaldoVM>
<body>
<h1 class="rubrik">Ändra saldo för #ViewBag.Lager</h1>
<div class="editBox">
<form method="post" asp-action="EditPost" asp-route-LagerID="#ViewBag.LagerID">
#foreach (var item in Model)
{
<div class="form-group">
<label asp-for="#item.Produkt" class="control-label">#item.Produkt</label>
<input asp-for="#item.Saldo" class="form-control" placeholder="#item.Saldo" />
<span asp-validation-for="#item.Saldo" class="text-danger"></span>
</div>
}
<input type="submit" value="Uppdatera lager" class="update" />
</form>
</div>
</body>
Controller method [HttpPost]:
[HttpPost]
public IActionResult EditPost(int LagerID, List<LagerSaldoVM> ls)
{
string error = "";
Debug.WriteLine(LagerID);
Debug.WriteLine(LagerID);
Debug.WriteLine(LagerID);
LagerSaldoMetod lsm = new LagerSaldoMetod();
foreach (var item in ls)
{
//lsm.updateLagerSaldo(LagerID, item.Produkt, item.Saldo, error);
Debug.WriteLine(item.Saldo);
Debug.WriteLine(item.Saldo);
}
return RedirectToAction("Index");
}
Im guessing it has something to do with binding the data in the view to the List I'm trying to pass ass argument in my Post method. I have not yet found an answer which resolves this issue using a foreach loop, preferably without LINQ, and with a dynamically loaded view.
So I seem to have fixed it by exchanging model type IEnumerable to List and using the index of the model to manipulate listed properties.
This updated view solved it:
#model List<LagerSaldoVM>
<body>
<h1 class="rubrik">Ändra saldo för #ViewBag.Lager</h1>
<div class="editBox">
<form asp-action="EditPost" asp-route-LagerID="#ViewBag.LagerID">
#for (int i = 0; i < Model.Count(); i++)
{
<div class="form-group">
<label asp-for="#Model[i].Produkt" class="control-label">#Model[i].Produkt</label>
<input asp-for="#Model[i].Saldo" class="form-control" placeholder="#Model[i].Saldo" />
<span asp-validation-for="#Model[i].Saldo" class="text-danger"></span>
</div>
}
<input type="submit" value="Uppdatera lager" class="update" />
</form>
</div>
</body>
I have a simple single page ASP.NET Core Razor app that has a "select" in it that I am populating from a Model variable. The values in the Model variable are cleared by the time that I examine them in the OnPost() method. (The variable named machineModel returns just fine but the variable named machineModels in the Model is cleared by the Post.) What is doing this?
Here's what the Index.cshtml looks like:
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
<div class="text-left">
<form enctype="multipart/form-data" method="post">
Model: <select name="machineModel" asp-for="machineModel" asp-items="Model.machineModels" required></select>
<input type="submit" asp-page-handler="Download" value="Download certificate" />
</form>
</div>
Here's what the Index.cshtml.cs looks like:
[BindProperties]
public class IndexModel : PageModel
{
public string machineModel { get; set; }
public List<SelectListItem> machineModels = new List<SelectListItem>();
// In some other function in the class the machineModels variable is filled (several times) ...
string modelStr = reader.GetAttribute("value");
int numMachineModels = machineModels.Count;
string machineModelIndexStr = numMachineModels.ToString();
machineModels.Add(new SelectListItem { Text = modelStr, Value = machineModelIndexStr, Selected = true });
// And here's the Post method ...
public IActionResult OnPostDownload()
{
// Doesn't matter what's in here, machineModels is already cleared at this point
return Page();
}
}
Thanks in advance for any help you can provide,
Rich
You do not post the value of machineModels from view to hanlder, try to add below code in your form.
<form enctype="multipart/form-data" method="post">
<div>
#{ int i = 0;}
#foreach (var x in Model.machineModels)
{
<input type="hidden" name="machineModels[#i].Text" value="#x.Text" />
<input type="hidden" name="machineModels[#i].Value" value="#x.Value" />
i++;
}
</div>
Model: <select name="machineModel" asp-for="machineModel" asp-
items="Model.machineModels" required></select>
<input type="submit" asp-page-handler="Download" value="Download
certificate" />
</form>
PageModel:
[BindProperty]
public List<SelectListItem> machineModels { get; set; } = new List<SelectListItem>();
I want to have a page on my website where I can upload files. For each file I want to have a name and a category.
[Required(ErrorMessage = "Please choose a file")]
[Display(Name = "File")]
public HttpPostedFileBase file { get; set; }
[Required(ErrorMessage = "A name is required")]
[Display(Name = "Name")]
public string name { get; set; }
[Display(Name ="Category")]
public string cat { get; set; }
This is my model. I want to have some dynamic form, what I mean is a form with a button that allows the user to add another form on the page to upload multiple files with a name and a category for each file. I've done this with Symfony2, but I have no idea how to do it with ASP.NET. Can someone help me please ?
At first create another model like following:
public class fileListModel{
IList<yourModel> fileList {get;set;}
}
Then in the razor view create form like this way:
#model fileListModel
<form>
//dynamic html(you can also use partial for this). When button will be clicked append following html using jquery $(form).append()
#{var key = [use some random id or guid]}
<input type="hidden" name="fileList.Index" value="#key" />
<input type="text" name="fileList[#key].name" value="Name" />
<input type="text" name="fileList[#key].cate" value="Category" />
<input type="file" name="fileList[#key].file" value="Upload"/>
// end dynamic html
#{ key = [use some random id or guid]}
<input type="hidden" name="fileList.Index" value="#key" />
<input type="text" name="fileList[#key].name" value="Name" />
<input type="text" name="fileList[#key].cate" value="Category" />
<input type="file" name="fileList[#key].file" value="Upload"/>
// end dynamic html
</form>
Now create a controller action method to accept the fileList:
public ActionResult upload(fileListModel fileList){
//save them to db
}
The following is a bare minimum example based on this blogpost. For demo purposes, I've named my model Foo. So whenever you read this, this should be your model with file, name and cat properties.
First, add https://www.nuget.org/packages/BeginCollectionItem/ to your project.
Then, add a partial view to your Views folder. I've named mine "_AddFile.cshtml":
#model WebApplication2.Models.Foo
#using (Html.BeginCollectionItem("files"))
{
<div class="form-horizontal">
<fieldset>
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
</div>
#Html.LabelFor(model => model.Cat, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Cat, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
</fieldset>
</div>
}
Note, the Html.BeginCollectionItem("files"), this is creating a collection, that is later grouped together and bound to your model named "files".
Our controller looks like this:
public ActionResult Index()
{
//Initialize the view with an empty default entry
var vm = new List<Foo> {
new Models.Foo {
Cat ="foo",
Name =" bar"
}
};
return View(vm);
}
//this calls your partial view and initializes an empty model
public PartialViewResult AddFile()
{
return PartialView("_AddFile", new Foo());
}
//note "files" name? The same as our collection name specified earlier
[HttpPost]
public ActionResult PostFiles(IEnumerable<Foo> files)
{
//do whatever you want with your posted model here
return View();
}
In your view, use this form:
#model IEnumerable<WebApplication2.Models.Foo>
#using (Html.BeginForm("PostFiles","Home", FormMethod.Post))
{
<div id="FileEditor">
#foreach (var item in Model)
{
Html.RenderPartial("_AddFile", item);
}
</div>
<div>
#Html.ActionLink("Add File", "AddFile", null, new { id = "addFile" }) <input type="submit" value="Finished" />
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
<script>
$(function () {
$("#addFile").click(function () {
$.ajax({
url: this.href,
cache: false,
success: function (html) { $("#FileEditor").append(html); }
});
return false;
});
})
</script>
}
The foreach loop renders a partial View for each model entry, in our case just one with a default entry.
The javascript loop then calls our PartialView and renders an empty template below the existing ones.
A call to submit, then lets you deal with your files in any way you want:
This is my form
#using (Html.BeginForm("EditPayments", "BookingPathLabelsCms"))
{
if (#Model.DisplayName == "Payment Labels")
{
<textarea id="seeit" name="seeit" rows="5" cols="10"></textarea>
<textarea id="seeitNoSelect" name="seeitNoSelect" rows="5" cols="10"></textarea>
<div class="cmsButtonContainer">
Cancel it
<input type="submit" name="Save" value="Save it"#* onmouseover="copyto();"*# />
</div>
}
}
And this is my controller action
public ActionResult EditPayments(BookingPathLabelsCmsViewModel model)
{
string txtarea = Request.Form["seeit"];
return RedirectToAction("Index");
}
Am not getting the values of textareas here,but values in the breakpoint ,see image.
Your code should looks like:
#using (Html.BeginForm("EditPayments", "BookingPathLabelsCms"))
{
if (#Model.DisplayName == "Payment Labels")
{
#Html.TextBoxFor(m => m.SeeIt)
#Html.TextBoxFor(m => m.SeeItNoSelect)
<div class="cmsButtonContainer">
Cancel it
<input type="submit" name="Save" value="Save it"#* onmouseover="copyto();"*# />
</div>
}
}
Of course, your ViewModel BookingPathLabelsCmsViewModel should have SeeIt and SeeItNoSelect properties. After that, MVC will bind correctly entered data.
First create a class with property.
public class TextAreaProperty
{
public string MyTextAreaValue { get; set; }
}
Use on the view declare like:
#model <project_name>.Models.<Class_name>
In this case:
#model MvcApplication1.Models.TextAreaProperty
Use this textArea Razor
#Html.TextAreaFor(x=> x.MyTextAreaValue)
On method post receiving parameter type TextAreaProperty
[HttpPost]
public ActionResult Index(TextAreaProperty textAreaProperty)
{
return View();
}
You will get the value from textAreProperty.
I have a problem to send my Model into my controller.
I use a button with the ajax to change the page but i need the model who is the first page to the second page.
I would like send my model in my controller but it's not work.
When i come in the page CreateAll, the renderpartial follow works to display Step1 but if i click on the step2 i would like the mainModel to be send to my controller use a partial view with submodel.
My model is:
public class CreateAllStep
{
public CreateStep1 Step1 { get; set; }
public CreateStep2 Step2 { get; set; }
public CreateAllStep(CreateStep1 step1, CreateStep2 step2)
{
this.Step1 = step1;
this.Step2 = step2;
}
}
My controller is(when the page start):
public ActionResult Create()
{
CreateStep1 step1=FillStep1();
CreateStep2 step2 = FillStep2();
CreateAllStep allStep = new CreateAllStep(step1, step2);
return View(allStep);
}
My controller is(when i click on the button, it's here where i would like send the model):
[HttpPost]
public ActionResult Create(String btn, CreateAllStep form)
{
if (Request.IsAjaxRequest())
{
if (btn != null)
{
if (btn == "Step1")
{
return PartialView("Step1",form.Step1);//not work
}
else if (btn == "Step2")
{
return PartialView("Step2");//work
}
else if(btn =="AllStep")
{
return PartialView("AllStep");
}
}
}
return View();
}
And my main view is :
#model SiteWebEmpty.Models.CreateAllStep
#{
ViewBag.Title = "Title";
}
<script type="text/javascript">
$(function () {
$('form').submit(function () {
$.post(this.action, $(this).serialize(), function (data) {
alert(data);
});
return false;
});
});
</script>
<script src="#Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
<h2>Title</h2>
#using (Ajax.BeginForm("Create", //controller action name
"CreateStep", //controller name
new AjaxOptions //ajax options that tell mvc how to perform the replacement
{
UpdateTargetId = "ViewPage", //id of div to update
HttpMethod = "Post" //how to call the controller action
}, new { id = "FormName" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Create </legend>
<button type="submit" name="btn" value="Step1" id="Step1">Step 1</button>
<button type="submit" name="btn" value="Step2" id="Step2">Step 2</button>
<button type="submit" name="btn" value="AllStep" id="AllStep">All Step</button>
<div id="ViewPage">
#Html.Partial("Step1", Model)
</div>
</fieldset>
}
My partial view is:
#model SiteWebEmpty.Models.ArticleRequest.CreateArticle.ArticleRequestDisplayCreateAllStep
<fieldset>
<legend>Step 1</legend>
#Html.LabelFor(step1 => step1.Step1.Customer)
#Html.EditorFor(step1 => step1.Step1.Customer)
#Html.ValidationMessageFor(step1 => step1.Step1.Customer)
#Html.LabelFor(articleType => articleType.Step1.ArticleType)
#Html.DropDownList("ArticleType", Model.Step1.ArticleType)
#Html.ValidationMessageFor(articleType => articleType.Step1.ArticleType)
#Html.LabelFor(model => model.Step1.LabelType)
#Html.DropDownList("LabelType", Model.Step1.LabelType)
#Html.ValidationMessageFor(model => model.Step1.LabelType)
</fieldset>
render html:
<script src="/Scripts/jquery.unobtrusive-ajax.min.js" type="text/javascript"></script>
<h2>Title</h2>
<form action="/CreateStep/Create?Length=13" data-ajax="true" data-ajax-method="Post" data-ajax-mode="replace" data-ajax-update="#ViewPage" id="FormName" method="post"> <fieldset>
<legend>Create </legend>
<button type="submit" name="btn" value="Step1" id="Step1">Step 1</button>
<button type="submit" name="btn" value="Step2" id="Step2">Step 2</button>
<button type="submit" name="btn" value="AllStep" id="AllStep">All Step</button>
<div id="ViewPage">
<fieldset>
<legend>Step 1</legend>
<label for="Customer">Customer</label>
<input class="text-box single-line" data-val="true" data-val-required="Customer is required" id="Customer" name="Customer" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Customer" data-valmsg-replace="true"></span>
<label for="ArticleType">Article Type</label>
<select data-val="true" data-val-required="ArticleType is required" id="ArticleType" name="ArticleType"><option value="127">AR1 : New Product</option>
<option value="161">AR2 : Product Modification</option>
</select>
<span class="field-validation-valid" data-valmsg-for="ArticleType" data-valmsg-replace="true"></span>
<label for="LabelType">Label Type</label>
<select data-val="true" data-val-required="LabelType is required" id="LabelType" name="LabelType"><option value="129">Private Label</option>
</select>
<span class="field-validation-valid" data-valmsg-for="LabelType" data-valmsg-replace="true"></span>
</fieldset>
</div>
</fieldset>
</form>
Thanks for your help :) !
Could you post the final HTML?
I think that your #Html.Partial("Step1", Model.Step1) will render a input text with id like Customer or ArticleType instead of Step1.Customer and Step1.ArticleType. What will bind to CreateAllStep.Customer that doesn´t exist.
If you have the Headers sent by the browsers will help too.
Update: Change your partial Step1, to accept a CreateAllStep Model and try again
Try removing your constructor on your ViewModel:
public CreateAllStep(CreateStep1 step1, CreateStep2 step2)
{
this.Step1 = step1;
this.Step2 = step2;
}
And change the controller code to:
public ActionResult Create()
{
CreateAllStep allStep = new CreateAllStep{Step1 = FillStep1(), Step2 = FillStep2()};
return View(allStep);
}
I have run into issues with constructors with parameters when databinding.