Validation of #Html.Textbox in MVC4 - c#

I use an update action to update based on the input from the #Html.Textbox.
#using (Html.BeginForm("Update", "Shopping", new { UserID = Request.QueryString["UserID"] }, FormMethod.Post, new { id = "myForm" }))
{
#Html.ValidationSummary()
#Html.Hidden("id", #Request.QueryString["UserID"] as string)
#Html.Hidden("productid", item.ProductID as string)
#Html.TextBox("Quantity", item.Quantity)
#Html.ValidationMessage("Quantity", "*")
#Html.Hidden("unitrate", item.Rate)
<input type="submit" value="Update" />
}
and In My Model class
[Required(ErrorMessage = "Quantity is required.")]
[Display(Name = "Quantity")]
[Range(2, 100, ErrorMessage = "There is not enough inventory for the product to fulfill your order.")]
public int? Quantity { get; set; }
The problem is I m not getting the validation message when the textbox is empty.
But when I use #Html.TextBoxFor
#Html.TextBoxFor(modelItem => item.Quantity)
#Html.ValidationMessageFor(modelitem => item.Quantity)
I am getting the validation message. and my update action is not working.
Here I have two options.
1. How to pass the textbox name "qty" in #Html.TextboxFor ?? (or)
2. How to get the validation message in #Html.Textbox() using #Html.ValidationMessage()
Any suggestions ..
EDIT :
My Update Action
[HttpPost]
public ActionResult Update(string id, string productid, int Quantity, decimal unitrate)
{
if (ModelState.IsValid)
{
int _records = UpdatePrice(id, productid, Quantity, unitrate);
if (_records > 0)
{
return RedirectToAction("Index1", "Shopping", new { UserID = Request.QueryString["UserID"] });
}
else
{
ModelState.AddModelError("","Can Not Update");
}
}
return View("Index1");
}

you have the answer in your question, when you use
#Html.TextBoxFor(modelItem => item.Quantity)
#Html.ValidationMessageFor(modelitem => item.Quantity)
you get the error message becasue the MVC model validation works on the name attributes as #Mystere Man said in the comments you are defying all the conventions and conventions is what MVC is all about, either change the property name in your model or use it as it is in the view if you want to leverage the MVC's model validation.
Not entirely relevant but a good read.

Related

Storing data between requests in dbcontext

I have a page with server side rendering using razor, where you can add a couple of elements from different lists, fill some fields and create a request from it on submit.
Each time an item is added/taken from any list, I send a post with submit button to a specific action, e.g. "CustomerSelected". I do this, because I need to recreate additional view components for the added item. In these methods I would like to add added objects to the db context, so on submit I can just say SaveChanges() and not have to assemble everything in the same method. But in .net core db context is per request and it is advisable to keep it that way. In this case how can I store these temporary entity objects between requests so later if someone decides to submit them I can say SaveChanges() or discard them otherwise?
I would like to have something like this:
public IActionResult CustomerAdded(int customerId)
{
var customer = _context.Customers.First(c => c.IdCustomer == customerId);
var message = _context.Messages.First(m => m.IdMessage = _idMessage);
message.Customers.Add(customer);
return View();
}
public IActionResult ItemAdded(int itemId)
{
var item = _context.Items.First(c => c.IdItem == itemId);
var message = _context.Messages.First(m => m.IdMessage = _idMessage);
message.Items.Add(item);
return View();
}
public IActionResult Submit()
{
_context.SaveChanges();
return View();
}
If this is not possible then I was thinking about adding individual elements in each method and save them there and onsubmit I would build the last final element. But if someone closes their browser without submitting then I have incomplete data laying in my database. I would have to run some kind of job to delete those and it seems to be too much for such a simple task.
It's not good idea to use server resources to track changes in such scenarios. In scenarios like shopping basket, list or batch editing it's better track changes at client-side.
Your requirement to get Views generated at server-side doesn't mean you need to track changes in DbContext. Get the index view and create view from server, but track changes on client. Then to save, post all data to the server to save changes based on the tracking info that you have.
The mechanism for client-side change tracking depends to the requirement and the scenario, for example you can track changes using html inputs, you can track changes using cookie, you can track changes using javascript objects in browser memory like angular scenarios.
Here is this post I'll show an example using html inputs and model binding. To learn more about this topic, take a look at this article by Phill Haack: Model Binding To A List.
Example
In the following example I describe a list editing scenario for a list of customers. To make it simple, I suppose:
You have a list of customers which you are going to edit at client. You may want to add, edit or delete items.
When adding new item, the row template for new row should come from server.
When deleting, you mark an item as deleted by clicking on a checkbox on the row.
When adding/editing you want to show validation errors near the cells.
You want to save changes at the end, by click on Save button.
To implement above scenario Then you need to create following models, actions and views:
Trackable<T> Model
This class is a model which helps us in client side tracking and list editing:
public class Trackable<T>
{
public Trackable() { }
public Trackable(T model) { Model = model; }
public Guid Index { get; set; } = Guid.NewGuid();
public bool Deleted { get; set; }
public bool Added { get; set; }
public T Model { get; set; }
}
Customer Model
The customer model:
public class Customer
{
[Display(Name ="Id")]
public int Id { get; set; }
[StringLength(20, MinimumLength = 1)]
[Required]
[Display(Name ="First Name")]
public string FirstName { get; set; }
[StringLength(20, MinimumLength = 1)]
[Required]
[Display(Name ="Last Name")]
public string LastName { get; set; }
[EmailAddress]
[Required]
[Display(Name ="Email Name")]
public string Email { get; set; }
}
Index.cshtml View
The Index view is responsible to render List<Trackable<Customer>>. When rendering each record, we use RowTemplate view. The same view which we use when adding new item.
In this view, we have a submit button for save and a button for adding new rows which calls Create action using ajax.
Here is Index view:
#model IEnumerable<Trackable<Customer>>
<h2>Index</h2>
<form method="post" action="Index">
<p>
<button id="create">New Customer</button>
<input type="submit" value="Save All">
</p>
<table class="table" id="data">
<thead>
<tr>
<th>
Delete
</th>
<th>
#Html.DisplayNameFor(x => x.Model.FirstName)
</th>
<th>
#Html.DisplayNameFor(x => x.Model.LastName)
</th>
<th>
#Html.DisplayNameFor(x => x.Model.Email)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
await Html.RenderPartialAsync("RowTemplate", item);
}
</tbody>
</table>
</form>
#section Scripts{
<script>
$(function () {
$('#create').click(function (e) {
e.preventDefault();
$.ajax({
url: 'Create',
method: 'Get',
success: function (data) {
$('#data tbody tr:last-child').after(data);
},
error: function (e) { alert(e); }
});
});
});
</script>
}
RowTemplate.cshtml View
This view is responsible to render a customer record. In this view, we first render the Index in a hidden, then set a prefix [index] for the fields and then render the fields, including index again, added, deleted and model id:
Here is RowTemplate View:
#model Trackable<Customer>
<tr>
<td>
#Html.HiddenFor(x => x.Index)
#{Html.ViewData.TemplateInfo.HtmlFieldPrefix = $"[{Model.Index}]";}
#Html.HiddenFor(x => x.Index)
#Html.HiddenFor(x => x.Model.Id)
#Html.HiddenFor(x => x.Added)
#Html.CheckBoxFor(x => x.Deleted)
</td>
<td>
#Html.EditorFor(x => x.Model.FirstName)
#Html.ValidationMessageFor(x => x.Model.FirstName)
</td>
<td>
#Html.EditorFor(x => x.Model.LastName)
#Html.ValidationMessageFor(x => x.Model.LastName)
</td>
<td>
#Html.EditorFor(x => x.Model.Email)
#Html.ValidationMessageFor(x => x.Model.Email)
</td>
</tr>
CustomerController
public class CustomerController : Controller
{
private static List<Customer> list;
}
It will have the following actions.
[GET] Index Action
In this action you can load data from database and shape it to a List<Trackable<Customer>> and pass to the Index View:
[HttpGet]
public IActionResult Index()
{
if (list == null)
{
list = Enumerable.Range(1, 5).Select(x => new Customer()
{
Id = x,
FirstName = $"A{x}",
LastName = $"B{x}",
Email = $"A{x}#B{x}.com"
}).ToList();
}
var model = list.Select(x => new Trackable<Customer>(x)).ToList();
return View(model);
}
[GET] Create Action
This action is responsible to returning new row template. It will be called by a button in Index View using ajax:
[HttpGet]
public IActionResult Create()
{
var model = new Trackable<Customer>(new Customer()) { Added = true };
return PartialView("RowTemplate", model);
}
[POST] Index Action
This action is responsible for receiving the tracked item from client and save them. The model which it receives is List<Trackable<Customer>>. It first strips the validation error messages for deleted rows. Then removes those which are both deleted and added. Then checks if model state is valid, tries to apply changes on data source.
Items having Deleted property as true are deleted, items having Added as true and Deleted as false are new items, and rest of items are edited. Then without needing to load all items from database, just using a for loop, call db.Entry for each item and set their states and finally save changes.
[HttpPost]
public IActionResult Index(List<Trackable<Customer>> model)
{
//Cleanup model errors for deleted rows
var deletedIndexes = model.
Where(x => x.Deleted).Select(x => $"[{x.Index}]");
var modelStateDeletedKeys = ModelState.Keys.
Where(x => deletedIndexes.Any(d => x.StartsWith(d)));
modelStateDeletedKeys.ToList().ForEach(x => ModelState.Remove(x));
//Removing rows which are added and deleted
model.RemoveAll(x => x.Deleted && x.Added);
//If model state is not valid, return view
if (!ModelState.IsValid)
return View(model);
//Deleted rows
model.Where(x => x.Deleted && !x.Added).ToList().ForEach(x =>
{
var i = list.FindIndex(c => c.Id == x.Model.Id);
if (i >= 0)
list.RemoveAt(i);
});
//Added rows
model.Where(x => !x.Deleted && x.Added).ToList().ForEach(x =>
{
list.Add(x.Model);
});
//Edited rows
model.Where(x => !x.Deleted && !x.Added).ToList().ForEach(x =>
{
var i = list.FindIndex(c => c.Id == x.Model.Id);
if (i >= 0)
list[i] = x.Model;
});
//Reditect to action index
return RedirectToAction("Index");
}
What about dynamic form(s) with javascript and using type="hidden" or visibility
and then sending everything at once
Or using TempData with redirects and reusing that data in other views(form) as input type="hidden"
Flow:
Form1 ->
Controller's Method saves data in TempData and Redirects to Form2 View / Or ViewData and return Form2 View? ->
Form2 has TempData inserted into the form under hidden inputs ->
Submit both at once
Cookie !
public class HomeController : Controller
{
public string Index()
{
HttpCookie cookie = Request.Cookies["message"];
Message message = null;
string json = "";
if (cookie == null)
{
message = new Message();
json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(message);
cookie = new HttpCookie("message", json);
}
Response.Cookies.Add(cookie);
return json;
}
public string CustomerAdded(int id)
{
HttpCookie cookie = Request.Cookies["message"];
Message message = null;
string json = "";
if (cookie == null || string.IsNullOrEmpty(cookie.Value))
{
message = new Message();
json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(message);
cookie = new HttpCookie("message", json);
}
else
{
json = cookie.Value;
message = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Message>(json);
}
if (message.Customers == null) message.Customers = new List<int>();
if (message.Items == null) message.Items = new List<int>();
if (!message.Customers.Contains(id))
{
message.Customers.Add(id);
}
json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(message);
cookie = new HttpCookie("message", json);
Response.Cookies.Add(cookie);
return json;
}
public string ItemAdded(int id)
{
HttpCookie cookie = Request.Cookies["message"];
Message message = null;
string json = "";
if (cookie == null || string.IsNullOrEmpty(cookie.Value))
{
message = new Message();
json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(message);
cookie = new HttpCookie("message", json);
}
else
{
json = cookie.Value;
message = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Message>(json);
}
if (message.Customers == null) message.Customers = new List<int>();
if (message.Items == null) message.Items = new List<int>();
if (!message.Items.Contains(id))
{
message.Items.Add(id);
}
json = new System.Web.Script.Serialization.JavaScriptSerializer().Serialize(message);
cookie = new HttpCookie("message", json);
Response.Cookies.Add(cookie);
return json;
}
public string Submit()
{
HttpCookie cookie = Request.Cookies["message"];
Message message = null;
string json = "";
if (cookie == null || string.IsNullOrEmpty(cookie.Value))
{
return "no data";
}
else
{
json = cookie.Value;
message = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<Message>(json);
}
Response.Cookies["message"].Value = "";
Response.Cookies["message"].Expires = DateTime.Now.AddDays(-1);
return "Submited";
}
}
Example links
http://localhost:58603/Home/CustomerAdded/1
http://localhost:58603/Home/CustomerAdded/2
http://localhost:58603/Home/Submit

How to make edit and delete POST Request in same page in my ASP.Net MVC 5 application

I am a beginner in ASP.Net MVC Web Development. And I am stuck at one place where I need to have POST request For EDIT and Delete Requests in the same page.
Behavior:
I have a page with many rows shown in tabular format, Now for each row I wanted to have "Delete", "Edit", and "Details" Button. I have done that successfully. But I read that we should never have a "GET" Request for Deleting and Editing a resource.
Issue:
I know how to make a POST Form using #Html.BeginForm(....FormMethod.POST,..), But I am confused in my case because I have many rows with each row having "Edit" and "Delete".
Below is my Attempt:
Parent View:
#model IEnumerable<Bridge.Models.Resume>
#using Bridge.ViewModels
<table class="table">
foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.datetime)
</td>
#Html.Partial("_TableButtonsPartial", new SmallButtonViewModel
{
ResumeId = item.ResumeId
})
</tr>
}
</table>
TableButtonPartial View:
#model Bridge.ViewModels.SmallButtonViewModel
#using Bridge.ViewModels
<td style="width: 120px;">
<div class="btn-group" role="group">
#Html.Partial("_SmallButtonPartial",
new SmallButtonViewModel
{
Action = "Edit",
ButtonType = "btn-primary",
Glyph = "pencil",
Text = "Edit button",
ReferralId = Model.ReferralId,
})
#Html.Partial("_SmallButtonPartial",
new SmallButtonViewModel
{
Action = "Details",
ButtonType = "btn-success",
Glyph = "list",
Text = "Detail button",
ResumeId = Model.ResumeId,
})
#Html.Partial("_SmallButtonPartial",
new SmallButtonViewModel
{
Action = "Delete",
ButtonType = "btn-danger",
Glyph = "trash",
Text = "Delete button",
ResumeId = Model.ResumeId,
})
</div>
</td>
SmallButtonPartial View
#model Bridge.ViewModels.SmallButtonViewModel
<a type="button" class="btn #Model.ButtonType btn-sm"
href="#Url.Action(Model.Action)#Model.ActionParameters">
<span class="glyphicon glyphicon-#Model.Glyph">
</span><span class="sr-only">#Model.Text</span>
</a>
SmallButton ViewModel
public class SmallButtonViewModel
{
public string Action { get; set; }
public string Text { get; set; }
public string Glyph { get; set; }
public string ButtonType { get; set; }
public int? ResumeId { get; set; }
public string ActionParameters
{
get
{
var param = new StringBuilder("?");
if (ResumeId != null & ResumeId > 0)
param.Append(string.Format("{0}={1}&", "resumeId", ResumeId));
return param.ToString().Substring(0, param.Length - 1);
}
}
}
Controller
public FileContentResult Details(int? resumeId)
{
var temp = _context.Resumes.Where(f => f.ResumeId == resumeId).SingleOrDefault();
var fileRes = new FileContentResult(temp.Content.ToArray(), temp.ContentType);
fileRes.FileDownloadName = temp.FileName;
return fileRes;
}
// Todo: Need to make it a POST Request
public ActionResult Delete(int? resumeId)
{
var r = _context.Resumes.Where(c => c.ResumeId == resumeId);
_context.Resumes.RemoveRange(r);
_context.SaveChanges();
return RedirectToAction("ResumeCenter");
}
My Thinking
I want to be able to do something like below in SmallButtonView :
#model Bridge.ViewModels.SmallButtonViewModel
#Html.BeginForm(..,FormMethod.POST,..) // but I am not sure how to achieve it?
You would start by making sure the Delete action could only be reached by a POST request, using the HttpPostAttribute:
[HttpPost]
public ActionResult Delete(int? resumeId)
{
var r = _context.Resumes.Where(c => c.ResumeId == resumeId);
_context.Resumes.RemoveRange(r);
_context.SaveChanges();
return RedirectToAction("ResumeCenter");
}
Once you add that attribute, any attempts to issue a GET request to that action will result in a 404 (Not Found) response.
Then, replace this:
#Html.Partial("_SmallButtonPartial",
new SmallButtonViewModel
{
Action = "Delete",
ButtonType = "btn-danger",
Glyph = "trash",
Text = "Delete button",
ResumeId = Model.ResumeId,
})
with something like:
#using(Html.BeginForm("delete", "your controller name", FormMethod.Post, new { #class="delete-form" }))
{
<input type="hidden" name="ResumeId" value="#Model.ResumeId" />
<button type="submit" class="btn btn-danger">
<span class="glyphicon glyphicon-trash"></span>
Delete
</button>
}
You can have as many forms on a page as you like, as long as they are not nested - this generates a form on each row. I added a class here (delete-form) that you could use to hook into a jQuery event, if you wanted to handle this via AJAX instead of allowing the form to submit.
Any other actions, like "view" or "edit", would still just be GET requests, which you can leave as anchors, or use forms with the method set to GET (the end result is the same).

ASP.NET MVC beginform for complex property [duplicate]

TL;DR: How do I handle form data that is being submitted with nonstandard names for the data?
The stats:
MVC 5
ASP.NET 4.5.2
I am bringing in two different models:
public async Task<ActionResult> Index() {
var prospectingId = new Guid(User.GetClaimValue("CWD-Prospect"));
var cycleId = new Guid(User.GetClaimValue("CWD-Cycle"));
var viewModel = new OnboardingViewModel();
viewModel.Prospecting = await db.Prospecting.FindAsync(prospectingId);
viewModel.Cycle = await db.Cycle.FindAsync(cycleId);
return View(viewModel);
}
One called Prospecting, the other called Cycle. The Prospecting one is working just fine, as nothing else on the page needs it except one small item.
The Cycle has a mess of separate forms on the page, each needing to be separately submittable, and editing just one small part of the Cycle table. My problem is, I don't know how to submit the correct data to the backend. I am also not entirely sure how to "catch" that data.
The bright spot is that apparently the front end is properly reflective of what is in the db. As in, if I manually change the db field to a true value, the checkbox ends up being selected on refresh.
My current form is such:
#using(Html.BeginForm("UpdatePDFResourceRequest", "Onboarding", FormMethod.Post, new { enctype = "multipart/form-data" })) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<fieldset>
#Html.LabelFor(Model => Model.Cycle.PDFResourceLibrary, htmlAttributes: new { #class = "control-label" })
#Html.CheckBoxFor(Model => Model.Cycle.PDFResourceLibrary, new { #class = "form-control" })
#Html.ValidationMessageFor(Model => Model.Cycle.PdfResourceLibrary, "", new { #class = "text-danger" })
<label class="control-label"> </label><button type="submit" value="Save" title="Save" class="btn btn-primary glyphicon glyphicon-floppy-disk"></button>
</fieldset>
}
But the resulting HTML is such:
<input id="Cycle_PDFResourceLibrary" class="form-control" type="checkbox" value="true" name="Cycle.PDFResourceLibrary" data-val-required="'P D F Resource Library' must not be empty." data-val="true">
As you can see, the name= is Cycle.PDFResourceLibrary and I don't know how to catch this on the backend.
My model for that specific form is:
public class PDFResourceRequestViewModel {
[DisplayName("PDF Resource Library Request")]
public bool PDFResourceLibrary { get; set; }
[DisplayName("Date Requested")]
[DataType(DataType.Date)]
public DateTime PDFResourceLibraryDate { get; set; }
[DisplayName("Notes")]
public string PDFResourceLibraryNotes { get; set; }
}
(not the overall model for that table, though)
And the method used to handle the form submission is:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> UpdatePDFResourceRequest(PDFResourceRequestViewModel model) {
var id = new Guid(User.GetClaimValue("CWD-Cycle"));
Cycle cycle = await db.Cycle.FindAsync(id);
if(cycle == null) {
return HttpNotFound();
}
try {
cycle.CycleId = id;
cycle.PDFResourceLibrary = model.PDFResourceLibrary;
cycle.PDFResourceLibraryDate = DateTime.Now;
cycle.PDFResourceLibraryNotes = model.PDFResourceLibraryNotes;
db.Cycle.Add(cycle);
await db.SaveChangesAsync();
return RedirectToAction("Index");
} catch { }
return View(model);
}
Now, I know that the method is wrong, for one I am editing just three values out of dozens in that table, so I need to be using something like this method. Problem is, the form is getting submitted with the name= of Cycle.PDFResourceLibrary and it is not being matched up on the back end.
Help?
You can use the [Bind(Prefix="Cycle")] attribute to 'strip' the prefix so that name="Cycle.PDFResourceLibrary" effectively becomes name="PDFResourceLibrary" and will bind to your PDFResourceRequestViewModel
public async Task<ActionResult> UpdatePDFResourceRequest([Bind(Prefix="Cycle")]PDFResourceRequestViewModel model)

Why is the input specified in Begin.Form being ignored?

Below is a very simple MVC triplet, implemented to try the things out.
It seems to work except the bit new { id = Model.StatusCode } in View, which is supposed to take the current value of Model.StatusCode and pass it as the parameter to the controller. It always pass '200', the initial value of the StatusCode (numeric value of 'Model.StatusCode'), although I change the value in the input field before hitting Submit button. How can I change my code to fix it?
I need to pass the parameter to the controller as string id since thsi controller's action is also used in routing.
Model
public class ErrorModel
{
[DisplayName("Status Code")]
public string StatusCode { get; set; }
public ErrorModel(string statusCode)
{
HttpStatusCode code;
if (! Enum.TryParse(statusCode, out code))
{
code = HttpStatusCode.NotFound;
}
StatusCode = ((int)code).ToString();
}
public ErrorModel(HttpStatusCode statusCode = HttpStatusCode.OK)
{
StatusCode = ((int)statusCode).ToString();
}
}
View
#using WebApplication1.Models
#model WebApplication1.Models.ExcelModel
#using (Html.BeginForm("StatusCode", "Error",
new { id = Model.StatusCode }, FormMethod.Post, null))
{
<p>
<div class="editor-label">
#Html.LabelFor(model => model.StatusCode)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.StatusCode)
</div>
<input type="submit" value="Submit" />
</p>
}
Controller
public class ErrorController : Controller
{
static readonly List<HttpStatusCode> ErrorCodes = new List<HttpStatusCode>(){
HttpStatusCode.Unauthorized, HttpStatusCode.Forbidden, HttpStatusCode.NotFound, HttpStatusCode.InternalServerError};
public ActionResult StatusCode(string id)
{
ViewBag.Message = "";
if ((id == null) || (ErrorController.AreEqual(HttpStatusCode.OK, id)))
{
return View("StatusCode", new ErrorModel(HttpStatusCode.OK));
}
foreach (HttpStatusCode errorCode in ErrorCodes)
{
if (ErrorController.AreEqual(errorCode, id))
{
return View("HttpError", new ErrorModel(errorCode));
}
}
ViewBag.Message = "Exception " + id
+ #" is not supported, see https://msdn.microsoft.com/en-us/library/system.net.httpstatuscode(v=vs.110).aspx for further details";
return View("HttpError", new ErrorModel(HttpStatusCode.InternalServerError));
}
static private bool AreEqual(HttpStatusCode httpCode, string statusCode)
{
return (statusCode == ((int)httpCode).ToString());
}
}
The overload you are using Html.BeginForm will append the routevalues to the form action attribute value. So the markup of your form will be like
<form action="Error/StatusCode/200">
<input type="text" name="StatusCode" />
</form>
When the form is posted, Model binder/Route engine will map the 200 value to the Id parameter because it matches our default route configuration {controllername}/{actionname}/{id}. That is the reason you are getting the initial value but not the updated value from form.
What you should do is use change the name of the input field rendered by Html.EditorFor helper method to match to the action parameter name (Id) . so that it will overwrite the values in the Url. You can use a different overload of the EditorFor method for that. You can totally get rid of the routevalues param from your BeginForm call since your form is going to post a field with name Id.
#using (Html.BeginForm("StatusCode", "Home" ,FormMethod.Post, null))
{
#Html.EditorFor(model => model.Name,null,"Id")
<input type="submit" />
}
This will render the input field with the name property value "Id" and when you post the form, The current value of this field will be mapped to your action method parameter.

I have got trouble to send id from view to controler

#model IEnumerable<Evidencija.Models.Vozilo>
#{
ViewBag.Title = "PokreniIzvjestaj";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>PokreniIzvjestaj</h2>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>Vozilo</legend>
<p>
#Html.DropDownList("Vozila", Model.Select(p => new SelectListItem { Text = p.VoziloID.ToString(), Value = p.VoziloID.ToString() }), "Izaberi vozilo")
</p>
<input type="submit" value="Dodaj stavku" />
</fieldset>
}
I want to send id of table vozilo to controler with dropdownlist.
Controler accepts vozilo as a parameter but it is ollways zero.
How can I solve this without using viewmodel.
[HttpPost]
public ActionResult PokreniIzvjestaj(Vozilo v)
{
ReportClass rpt = new ReportClass();
rpt.FileName = Server.MapPath("~/Reports/Vozilo.rpt");
rpt.Load();
//ReportMethods.SetDBLogonForReport(rpt);
//ReportMethods.SetDBLogonForSubreports(rpt);
// rpt.VerifyDatabase();
rpt.SetParameterValue("#VoziloId",v.VoziloID);
Stream stream = null;
stream = rpt.ExportToStream(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat);
return File(stream, "application/pdf", "Vozilo.pdf");
//PortableDocFormat--pdf format
//application/pdf -- vezan za pdf format, ako je drugi tip mjenja se u zavisnosti od izabranog
//naziv.pdf -- naziv dokumenta i izabrana ekstenzija
}
[HttpGet]
public ActionResult PokreniIzvjestaj()
{
var vozila = db.Voziloes.ToList();
return View(vozila);
}
There are two method from controler.
You currently binding your drop down to a property named Vozilo. A <select> post back single value (in your case the VoziloID or the selected option. Your POST method then tries to bind a complex object Vozilo to an int (assuming VoziloID is typeofint) which of course fails and the model isnull`. You could solve this changing the method to
[HttpPost]
public ActionResult PokreniIzvjestaj(int Vozilo)
The parameter Vozilo will now contain the value of the selected VoziloID.
However it not clear why you want to "solve this without using viewmodel" when using a view model is the correct approach
View model
public class VoziloVM
{
[Display(Name = "Vozilo")]
[Required(ErrorMessage = "Please select a Vozilo")]
public int? SelectedVozilo { get; set; }
public SelectList VoziloList { get; set; }
}
Controller
public ActionResult PokreniIzvjestaj()
{
var viziloList = db.Voziloes.Select(v => v.VoziloID);
VoziloVM model = new VoziloVM();
model.VoziloList = new SelectList(viziloList)
model.SelectedVozilo = // set a value here if you want a specific option selected
return View(model);
}
[HttpPost]
public ActionResult PokreniIzvjestaj(VoziloVM model)
{
// model.SelectedVozilo contains the value of the selected option
....
}
View
#model YourAssembly.VoziloVM>
....
#Html.LabelFor(m => m.SelectedVozilo)
#Html.DropDownListFor(m => m.SelectedVozilo, Model.VoziloList, "-Please select-")
#Html.ValidationMessageFor(m => m.SelectedVozilo)
....

Categories