Post from Razor page clears Model values - when it shouldn't? - c#

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>();

Related

Values from model not being passed back to view - .NET

I have the following form in a .NET Core application, consisting of a text input field and a "Submit" button.
I'd like the text from the text input field to re-appear in the form after submission, being passed to the controller action from the form and then being passed back to the view.
However, when I test the application, although the inputted values from the view appear when they are bound to the model in the controller, when they are passed back to the view they are wiped and I receive an "Object reference set to null" exception error.
I wondered if there's something missing from my code or what the potential cause of this may be?
Any advice would be great here,
Thanks,
Robert
// This is my view, featuring a simple form
// Values from the view are successfully being passed into the Controller
// This is the exception I receive when the values are passed back to the view:
My code:
#page
#model Models.StringModel
<div class="text-left">
<form asp-controller="Home" asp-action="Alter">
<span class="form-control">
<label asp-for="Name">Alter string:</label>
#if (#Model != null)
{
<input type="text" asp-for="Name" class="changeString" value="#Model.Name"/>
} else
{
<input type="text" asp-for="Name" class="changeString"/>
}
<input class="btn btn-primary" type="submit" value="Update" action="Update" />
</span>
</form>
</div>
StringModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SplitString.Models
{
public class StringModel
{
public int ID { get; set; }
public string Name { get; set; }
}
}
HomeController.cs
using Microsoft.AspNetCore.Mvc;
using SplitString.Models;
using System.Threading.Tasks;
namespace SplitString.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Alter([Bind("Id, Name")] StringModel stringModel)
{
stringModel.ID = 1;
return View("~/Pages/Index.cshtml", stringModel);
}
}
}
Thanks,
Robert
It looks like you are using a razor page project,so that you don't need to use mvc in it.You only need to use page handler:
Index.cshtml:
#page
#model IndexModel
<div class="text-left">
<form method="post">
<span class="form-control">
<label asp-for="stringModel.Name">Alter string:</label>
#if (#Model != null)
{
<input type="text" asp-for="stringModel.Name" class="changeString" value="#Model.stringModel.Name"/>
} else
{
<input type="text" asp-for="stringModel.Name" class="changeString"/>
}
<input class="btn btn-primary" type="submit" value="Update" action="Update" />
</span>
</form>
</div>
Index.cshtml.cs:
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
[BindProperty]
public StringModel stringModel { get; set; } = new StringModel();
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
}
public void OnPost()
{
stringModel.ID = 1;
//you can do something here
}
}
result:
By looking at this part of your code:
#if (#Model != null)
{
<input type="text" asp-for="Name" class="changeString" value="#Model.Name"/>
}
I saw that you are trying to access the Name property without checking if it has any value.
If you change your code to this it shouldn't throw an exception anymore.
#if (#Model != null && !string.IsNullOrEmpty(Model.Name))
{
<input type="text" asp-for="Name" class="changeString" value="#Model.Name"/>
}
please remove #page from your .cshtml file.
If you use View Engine to render cshtml, please don't use #page.
If you want to use #page, please use razor pages.
// #page
#model Models.StringModel
<div class="text-left">
<form asp-controller="Home" asp-action="Alter">
<span class="form-control">
<label asp-for="Name">Alter string:</label>
#if (#Model != null)
{
<input type="text" asp-for="Name" class="changeString" value="#Model.Name"/>
} else
{
<input type="text" asp-for="Name" class="changeString"/>
}
<input class="btn btn-primary" type="submit" value="Update" action="Update" />
</span>
</form>
</div>

How do I pass a dynamic list of a viewmodel from view to controller?

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>

ASP.NET MVC: Create ValidationMessageFor Dynamically

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");
}

ViewBag for video upload?

I am working with sample from codeproject http://www.codeproject.com/Tips/1011040/Upload-and-Delete-Video-File-to-Microsoft-Azure-Bl
I have created an index.cshtml in the way of
that is
#model List<string>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#using (Html.BeginForm("Add", "Blob", FormMethod.Post, new
{enctype = "multipart/form-data" }))
{
<div>
<input type="file" name="pic" id="pic" />
<input type="submit" value="Upload Now" id="s1" />
</div>
}
<ul>
#foreach (var item in Model)
{
<li>
<input type="button" name="b1" id="b1"
value="Delete"
onclick="Remove('#item')" />
<video src="#item" height="200" width="200" controls />
</li>
}
</ul>
#section scripts{
<script>
function Remove(x) {
alert(x);
var uri = "/Blob/remove";
$.post(uri, { name: x }, function (y) {
window.location.href = "/blob/index";
alert(y);
});
}
</script>}
and my Controller class is :
public class BlobsController : Controller
{
//
// GET: /Blobs/
BlBlobs objbl = new BlBlobs();
public ActionResult Index()
{
//return View();
return View(objbl.GetBlobList());
}
[HttpPost]
public ActionResult Add(HttpPostedFileBase pic)
{
objbl.AddBlob(pic);
return RedirectToAction("Index");
}
[HttpPost]
public string Remove(string name)
{
objbl.DeleteBlob(name);
return "Blob Removed Successfully";
}
}
That give me pretty nice browse/upload form, but fails on upload click with 404 error. The question is - how to call the add method correctly in this index.cshtml file?
Your controller is called BlobsController so that would give you a route of /blobs/{action} with the default route, however in your view your actions are looking for a controller called blob. Either change the name of your controller
public class BlobController : Controller
{
//...
}
Or update your views to use the correct controller name.
Html.BeginForm("Add", "Blobs", FormMethod.Post, new
{enctype = "multipart/form-data" }))

Get Textarea value from posted form in MVC3

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.

Categories