validation problem with MVC 1 - c#

Edit.aspx:
<th>Calendar<input id="datepicker" name="datepicker" type="text" class="input-box"/></th>
Controller action:
// POST: /Studenti/Edit/5
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int[] rb, int id, string datepicker)
{
List<nastava_prisustvo> nastava = new List<nastava_prisustvo>();
if (String.IsNullOrEmpty(datepicker))
ModelState.AddModelError("datepicker", "First name is required");
try
{
if (ModelState.IsValid)
{
string poruka = "";
for (int i = 1; i <= rb.Length; i++)
{
string name = "chk" + i;
string selID = Request.Form[name];
if (selID == "on")
{
nastava.Add(new nastava_prisustvo
{
br_indexa = int.Parse(Request.Form["id_stud" + i]),
id_predmet = id,
datum = System.DateTime.Parse(Request.Form["datepicker"])
});
}
}
return View("show", nastava);
}
}
catch(Exception ex){
ModelState.AddModelError("*", "An unexpected error occurred.");
}
return View("show", nastava);
}
}
How to validate datepicker fiel? How stop posting data if date is not selected and show appropriate message. I use ASP>NET MVC 1 and read this http://www.superexpert.com/Blog/archive/2008/09/09/asp-net-mvc-tip-42-use-the-validation-application-block.aspx but did not solve my problem

I would stick to server side validation. Try this:
DateTime datePosted;
if (!DateTime.TryParse(Request.Form["datepicker"], out datePosted))
{
this.ModelState.AddModelError("datepicker", "Invalid Date!");
}
if (this.ModelState.IsValid)
{
return View("show", nastava);
}
else
{
// return your GET edit action here
return Edit(5);
}
Your Edit view will automatically be passed any validation errors and you can display them with a validation summary.
<%= Html.ValidationSummary() %>

If you want to validate the date before posting back you're going to have to use client side validation. xVal is a popular plugin for this.
For server side validation you're going to want to look at model binding and model validation.

ASP.NET MVC does not use the same sort of controls you're used to using in ASP.NET. Basically you have to do most things manually in JavaScript code, whereas in ASP.NET it generates that code automatically.
I'd use JQuery or something to validate the data in the control on the button click.

since you have validate on the client with your own Javascript, try the jQuery UI data picker:
http://jqueryui.com/demos/datepicker/
Then, most simply, on the server side, you can take the string value "datepicker" Convert it in a try/catch.
var dtDatePicker = new DateTime();
try
{
dtDatePicker = Convert.ToDateTime(datepicker);
// worked, carry on
}
catch
{
// didn't work, invalid date.
}

Related

Pass ID from URL to Database is going null

My view looks like this
My url link http://localhost:63897/UploadImages?id=1361. 1361 is my pr_id. I need to pass the id which is 1361 from url to database, but it is going null.
Here is my controller code:
public ActionResult UploadImages(int id) {
ViewBag.prid = id;
return View();
}
[HttpPost]
public ActionResult UploadImages([Bind(Include = "id,photo_url,photo_caption,photo_credit,pr_id")] Photo photos, HttpPostedFileBase photo_file)
{
if (ModelState.IsValid)
{
if (photo_file != null && photo_file.FileName != null && photo_file.FileName != "")
{
try
{
string path = Path.Combine(Server.MapPath("~/Images/Releases"), Path.GetFileName(photo_file.FileName));
photo_file.SaveAs(path);
string f1 = path.Substring(path.LastIndexOf("\\"));
string[] split = f1.Split('\\');
string newpath = split[1];
string imagepath = "~/Images/Releases/" + newpath;
photos.photo_url = imagepath;
_db.Photos.Add(photos);
_db.SaveChanges();
}
catch (Exception ex)
{
ViewBag.Message = "ERROR:" + ex.Message.ToString();
}
return RedirectToAction("List");
}
}
return View();
}
View :
#Html.HiddenFor(model => model.pr_id, new { #Value = ViewBag.id })
Your view bag dictionary item's key is prid. But in your view code you are using a different key.
Use ViewBag.prid. Also use the Hidden helper method.
#Html.Hidden("pr_id", new { #value = ViewBag.prid })
Or just write plain HTML and set the value attribute value.
<input type="hidden" name="pr_id" value="#ViewBag.prid" />
Check the view source of the page to confirm the correct value attribute is set to the hidden input element with name pr_id
Assuming you fixed the wrong ViewBag key name, your existing approach will basically generate the below markup
<input Value="23" name="pr_id" type="hidden" value="0" />
Remember, Value != value
This is one major reason i do not use dynamic stuff like ViewBag. You make a silly mistake like this and there are no warnings/errorrs from the IDE/compiler. It just silently fails :( If you use a strongly typed view model, the compiler will complain when you make a silly typo.
Also do not use the *For method and try to manually overwrite the value/id/name etc. The helpers are designed to set the value/name/id attribute values properly. Consider using a view model and use these For methods with them. That will be less code.
If your view model has a property called pr_id, set that property value in your GET action, send that view model to the view and in the view(which is strongly typed to this view model), simply call HiddenFor method on that property
#Html.HiddenFor(a=>a.pr_id);

Pass last insert id to toastr - Asp.Net MVC 4

I am new to MVC and trying to pass the last created Id (once the save button has been clicked in the form).
Can anyone please tell me if it is possible to pass this value to the toastr display, and how this can be done, so once the save button is pressed it returns that Id number?
Additionally to my comment, here's a more complex answer.
Roughly it contains the following items:
Views: CreateItem, NewItemHandler
Controllers: ItemHandler
Javascript: site.js and jQuery
The CreateItem view is the dialog where the user enters their item values. In my case a simple form with two input fields and the mandatory submit button.
#{
ViewBag.Title = "CreateItem";
}
<h2>CreateItem</h2>
<form id="newItemForm">
Item name: <input id="itemname" type="text" name="fname"><br>
Item weight: <input id="itemweight" type="text" name="lname"><br>
<input type="submit" value="Submit">
</form>
The JavaScript should stop the redirection when clicking on submit, this is done by returning false within $("newItemForm").submit(...). Furthermore we no need to tell the server that it needs to create our item, so we have to create our own submit request, which I did with jQuery.post():
$('#newItemForm').submit(function () {
sendPostAndShowResult();
return false;
});
function sendPostAndShowResult() {
var name = $("#itemname").text();
var weight = $("#itemweight").text();
$.post("/Item/NewItemHandler",
{ "name": name, "weight": weight }
).done(function (data) {
alert("The ID of your new item is: " + $.trim(data)); //replace with toast
})
.fail(function () {
alert("Error while processing the request!");
});
}
Just a hint: I didn't use toast here, since I never used it, but I guess it shouldn't be too difficult to adapt.
The final piece of the puzzle is the NewItemHandler, which creates the item, figures out the ID and returns the value:
The View is quite easy. Since we don't need a Layout, it has been set to "".
#{
Layout = "";
}
#Html.Raw(Session["ItemID"])
As you see, we just need to get the "ItemID" into our Session object, this is done by the Controller.
[HttpPost]
public ActionResult NewItemHandler(string name, string weight)
{
int id = GenerateNewItem(name, weight);
Session["ItemID"] = id;
return View();
}
EDIT: I tried to adapt this approach to your solution:
You need to remove the return RedirectToAction() with return View(); in your Controller. This then returns (Save.cshtml) a response, with the ID in an ohterwise empty file (Layout = "").
Your Save.cshtml is empty I guess, so replace it with
#{
Layout = "";
}
#Html.Raw(Session["ItemID"])
In your controller the Save Method should look remotely like this.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Save(BidstonHwrc bidstonhwrc)
{
_context.BidstonHwrc.Add(bidstonhwrc);
try
{
_context.SaveChanges(); //either all changes are made or none at all
}
catch (Exception e)
{
Console.WriteLine(e);
}
int id = bidstonhwrc.Id;
Session["ItemID"] = id;
return View();
}
In your MCN Form you need to give your <form> tag an ID, via Razor:
#using (Html.BeginForm("Save", "BidstonHwrc",FormMethod.Post, new { id = "SaveBidstonHwrc" }))
The javascript code should look like this, simply adapt the IDs:
$('#SaveBidstonHwrc').submit(function () {
sendPostAndShowResult();
return false;
});
function sendPostAndShowResult() {
//foreach Model/ViewModel Property one line e.g.
var Id = $("Id").text();
var McnNumber = $("McnNumber").text();
$.post("/BidstonHwrc/Save",
{ "Id": Id, "McnNumber": McnNumber }
).done(function (data) {
alert("The ID of your new item is: " + $.trim(data)); //replace with toast
$(location).attr('href', '/Home/Index') //Redirect to Home
})
.fail(function () {
alert("Error while processing the request!");
});
}
I uploaded a project that should represent your solution a bit.
You can download it here (28MB): Project download

Still need to TryUpdateModel when posting through Ajax

just wondering if there is any point validating a view model using TryUpdateModel(), when calling a Post method through Ajax. I have the following code which is called via a jquery Ajax post:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult SubmitForm(ViewModel viewModel)
{
var valid = TryUpdateModel(viewModel);
if (valid)
{
var service = new Service();
var result = _tmpRepository.ExecuteService(viewModel));
return Json(new { Valid = valid, Response = result });
}
return Json(new { Valid = valid });
}
The way I see it, I'm using unobtrusive validation with data annotation on my view model. So the only way that validation wouldn't occur is if javascript was disabled and if javascript is disabled my Ajax request isn't going to do a fat lot!
You still need to validate any data server side. There's nothing stopping someone from submitting the request themselves, or modifying the parameters of the ajax call in the browser. Client side validation should only be a convenience for the user.
You can avoid the TryUpdateModel() though:
[AcceptVerbs(HttpVerbs.Post)]
public JsonResult SubmitForm(ViewModel viewModel)
{
if (ModelState.IsValid)
{
var service = new Service();
var result = _tmpRepository.ExecuteService(viewModel));
return Json(new { Valid = valid, Response = result });
}
return Json(new { Valid = valid });
}

MVC - Can I set a model attribute in the view and get the value form a script?

This is the Controller that returns the above-mentioned view:
public PartialViewResult Day()
{
Day model = new Day();
return PartialView("_Day", model);
}
[HttpPost]
public PartialViewResult Day(Day model)
{
return PartialView("_Day", model);
}
I wrote both the Get and the Post method after I read this question. Note that I haven't set the model in the Controller.
Then I have my View:
#model Timing.Models.Day
#{
Timing.Models.Day d = new Timing.Models.Day();
d.UserId = "some value";
}
This piece of code is working fine: when I go to retrieve, or display, d.UserId I get the right value.
Furthermore I have a script in the same view.
<script>
function getUserId() {
//some code that gets a string
return userId;
}
</script>
And also this script is working right.
I looked for a solution and this is the best I've been able to find:
#{
Timing.Models.Day d = new Timing.Models.Day();
d.UserId = Page.ClientScript.RegisterStartupScript(this.GetType(), "Getuser", "getUserId()", true);
}
But it's not working because of something that is null (I wasn't able to understand what was null though).
Is there a way to solve this problem?
For those who ask, I'm using a script because the value I want to set is the one of an html element (this is my related question).
Thanks!
Declare your variable you want in javascript
#{
var useridForjs = model.property // access model and set useridForjs
}
to use in javascript
<script>
function getUserId() {
//some code that gets a string
var myserversidevarvalue = '#useridForjs'; // here myserversidevarvalue will store value..
return userId;
}
</script>
Make it easy on yourself and inject your id into a hidden form field, easily obtainable by javascript. Add this to your view:
Html.HiddenFor(model.userId)
Then access it like this:
function getUserId() {
return $('#userId').val();
}

How can I maintain ModelState with RedirectToAction?

How can I return the result of a different action or move the user to a different action if there is an error in my ModelState without losing my ModelState information?
The scenario is; Delete action accepts a POST from a DELETE form rendered by my Index Action/View. If there is an error in the Delete I want to move the user back to the Index Action/View and show the errors that are stored by the Delete action in the ViewData.ModelState. How can this be done in ASP.NET MVC?
[AcceptVerbs(HttpVerbs.Post | HttpVerbs.Delete)]
public ActionResult Delete([ModelBinder(typeof(RdfUriBinder))] RdfUri graphUri)
{
if (!ModelState.IsValid)
return Index(); //this needs to be replaced with something that works :)
return RedirectToAction("Index");
}
Store your view data in TempData and retrieve it from there in your Index action, if it exists.
...
if (!ModelState.IsValid)
TempData["ViewData"] = ViewData;
RedirectToAction( "Index" );
}
public ActionResult Index()
{
if (TempData["ViewData"] != null)
{
ViewData = (ViewDataDictionary)TempData["ViewData"];
}
...
}
[EDIT] I checked the on-line source for MVC and it appears that the ViewData in the Controller is settable, so it is probably easiest just to transfer all of the ViewData, including the ModelState, to the Index action.
Use Action Filters (PRG pattern) (as easy as using attributes)
Mentioned here and here.
Please note that tvanfosson's solution will not always work, though in most cases it should be just fine.
The problem with that particular solution is that if you already have any ViewData or ModelState you end up overwriting it all with the previous request's state. For example, the new request might have some model state errors related to invalid parameters being passed to the action, but those would end up being hidden because they are overwritten.
Another situation where it might not work as expected is if you had an Action Filter that initialized some ViewData or ModelState errors. Again, they would be overwritten by that code.
We're looking at some solutions for ASP.NET MVC that would allow you to more easily merge the state from the two requests, so stay tuned for that.
Thanks,
Eilon
In case this is useful to anyone I used #bob 's recommended solution using PRG:
see item 13 -> link.
I had the additional issue of messages being passed in the VeiwBag to the View being written and checked / loaded manually from TempData in the controller actions when doing a RedirectToAction("Action"). In an attempt to simplify (and also make it maintainable) I slightly extended this approach to check and store/load other data as well. My action methods looked something like:
[AcceptVerbs(HttpVerbs.Post)]
[ExportModelStateToTempData]
public ActionResult ChangePassword(ProfileViewModel pVM) {
bool result = MyChangePasswordCode(pVM.ChangePasswordViewModel);
if (result) {
ViewBag.Message = "Password change success";
else {
ModelState.AddModelError("ChangePassword", "Some password error");
}
return RedirectToAction("Index");
}
And my Index Action:
[ImportModelStateFromTempData]
public ActionResult Index() {
ProfileViewModel pVM = new ProfileViewModel { //setup }
return View(pVM);
}
The code in the Action Filters:
// Following best practices as listed here for storing / restoring model data:
// http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx#prg
public abstract class ModelStateTempDataTransfer : ActionFilterAttribute {
protected static readonly string Key = typeof(ModelStateTempDataTransfer).FullName;
}
:
public class ExportModelStateToTempData : ModelStateTempDataTransfer {
public override void OnActionExecuted(ActionExecutedContext filterContext) {
//Only export when ModelState is not valid
if (!filterContext.Controller.ViewData.ModelState.IsValid) {
//Export if we are redirecting
if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult)) {
filterContext.Controller.TempData[Key] = filterContext.Controller.ViewData.ModelState;
}
}
// Added to pull message from ViewBag
if (!string.IsNullOrEmpty(filterContext.Controller.ViewBag.Message)) {
filterContext.Controller.TempData["Message"] = filterContext.Controller.ViewBag.Message;
}
base.OnActionExecuted(filterContext);
}
}
:
public class ImportModelStateFromTempData : ModelStateTempDataTransfer {
public override void OnActionExecuted(ActionExecutedContext filterContext) {
ModelStateDictionary modelState = filterContext.Controller.TempData[Key] as ModelStateDictionary;
if (modelState != null) {
//Only Import if we are viewing
if (filterContext.Result is ViewResult) {
filterContext.Controller.ViewData.ModelState.Merge(modelState);
} else {
//Otherwise remove it.
filterContext.Controller.TempData.Remove(Key);
}
}
// Restore Viewbag message
if (!string.IsNullOrEmpty((string)filterContext.Controller.TempData["Message"])) {
filterContext.Controller.ViewBag.Message = filterContext.Controller.TempData["Message"];
}
base.OnActionExecuted(filterContext);
}
}
I realize my changes here are a pretty obvious extension of what was already being done with the ModelState by the code # the link provided by #bob - but I had to stumble on this thread before I even thought of handling it in this way.
Please don't skewer me for this answer. It is a legitimate suggestion.
Use AJAX
The code for managing ModelState is complicated and (probably?) indicative of other problems in your code.
You can pretty easily roll your own AJAX javascript code. Here is a script I use:
https://gist.github.com/jesslilly/5f646ef29367ad2b0228e1fa76d6bdcc#file-ajaxform
(function ($) {
$(function () {
// For forms marked with data-ajax="#container",
// on submit,
// post the form data via AJAX
// and if #container is specified, replace the #container with the response.
var postAjaxForm = function (event) {
event.preventDefault(); // Prevent the actual submit of the form.
var $this = $(this);
var containerId = $this.attr("data-ajax");
var $container = $(containerId);
var url = $this.attr('action');
console.log("Post ajax form to " + url + " and replace html in " + containerId);
$.ajax({
type: "POST",
url: url,
data: $this.serialize()
})
.done(function (result) {
if ($container) {
$container.html(result);
// re-apply this event since it would have been lost by the form getting recreated above.
var $newForm = $container.find("[data-ajax]");
$newForm.submit(postAjaxForm);
$newForm.trigger("data-ajax-done");
}
})
.fail(function (error) {
alert(error);
});
};
$("[data-ajax]").submit(postAjaxForm);
});
})(jQuery);
Maybe try
return View("Index");
instead of
return Index();

Categories

Resources