Check if the default value in the form has changed - asp.net-mvc

I have an edit form which has a label and current values in textbox, I want to check if the values in the form has been changed when the form is submitted.
Here is the form
<fieldset>
<legend>Module <small>Edit</small></legend>
#using (Html.BeginForm("Edit", "Module"))
{
#Html.ValidationSummary(true)
#Html.HiddenFor(m=>m.Id)
for(var i = 0; i < Model.Properties.Count(); i++)
{
<label class="label">#Model.Properties[i].Name</label>
<div class="input-block-level">#Html.TextBoxFor(model => Model.Properties[i].Value, new { #value = Model.Properties[i].Value })</div>
}
<div class="form-actions" id="buttons">
<button type="submit" class="btn btn-primary" id="Submit">Save changes</button>
#Html.ActionLink("Cancel", "ModuleList", null, new { #class = "btn " })
</div>
}
</fieldset>
this results to
How can i check if the form has been changed? My httppost method of controller currently look like this
[HttpPost]
public ActionResult Edit(EditModule module)
{
if (ModelState.IsValid)
{
_repository.SaveModuleEdits(module);
Information("Module was successfully edited!");
return RedirectToAction("ModuleList", "Module", new {area = "Hardware"});
}
Error("Edit was unsuccessful, if the problem persists please contact admin!");
return RedirectToAction("ModuleList", "Module", new { area = "Hardware" });
}
}

It is fairly straight forward on the client side if you using something like Knockout. Here is an article that describes how to use Knockout for change tracking. This article uses a Knockout add-on called KoLite to make it even simpler.

One way to check if a value has changed from its original state (server side), is through HMAC mechanism.
Basically it generates a hash based on a string and secret key, and this hash is sent along with the form as a hidden field (http get), if the value is changed by the customer then the recalculation of the hash (http post) will be different from what is stored in the hidden field, then you know that someone change the value of that field.
This may be a little overworked but is one of the safest methods.
https://security.stackexchange.com/questions/20129/how-when-do-i-use-hmac
How to generate HMAC-SHA1 in C#?

Related

Asp.net mvc5 hidden for does not pass data to controller

I try to pass some hidden data to my controller by using the hiddenFor, I know the value I want gets to the view, but after submiting the form the value stays null when it arrives in the controller. The data in EditorFor is passed correctly to the controller.
// View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
// Some working editorFor fields. Data from these gets successfully received
// The name is correctly displayed in the paragraph
<p>#Model.name</p>
// This data is not received in the controller
#Html.HiddenFor(x => x.name)
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
}
// Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Product product, HttpPostedFileBase image)
{
product.name = "a name";
return View(product);
}
I also tried using a normal named hidden, but this also didn't return a value.
Someone an idea what I missed?
You can pass the hidden fields automatically, if you have a form, using for example the razor helper
#using (Html.BeginForm("CreateTable", "Home", FormMethod.Post, null){ #HiddenFor(i => i.PropertyName) }
and the hidden fields must be inside of form, otherwise you will "lost" them.
Update following your updated question: Try remove the HiddenField and change <p>#Model.name</p>
to
#Html.LabelFor(i => i.Name)
I did focus on the incorrect thing, the problem was that I changed the model in the controller after the postback. But this only changes the model en does not changes the ModelState, which the form data uses.
//This is updated after model changes.
<p>#Model.name</p>
//For this you need to update the ModelState
#Html.HiddenFor(x => x.name)
In the controller you need to use ModelState.Remove(property name). (Or clear the complete ModelState)
//After removal of the property the ModelState will update to the new model value.
product.name = "a name";
ModelState.Remove("name");
return View(product);
In this article it's explained, https://weblog.west-wind.com/posts/2012/Apr/20/ASPNET-MVC-Postbacks-and-HtmlHelper-Controls-ignoring-Model-Changes.

ASP.NET MVC - Determine if field has an error in Razor view

I'm working on an input form in ASP.NET MVC. My input form looks like this:
#using (Html.BeginForm("Result", "Contact", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { role="form" }))
{
<h4>What do you want to tell us?</h4>
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group label-floating">
<label class="control-label" for="Subject">Subject</label>
<input class="form-control" id="Subject" name="Subject" type="text">
</div>
<div class="form-group">
<input type="submit" value="Send" class="btn btn-primary btn-raised" />
</div>
#Html.AntiForgeryToken()
}
My model behind this form looks like this:
public class ContactModel
{
[Required(ErrorMessage="Please enter the subject.")]
[Display(Name="Subject")]
public string Subject { get; set; }
}
I want to conditionally apply classes and structure based on whether or not the Model is valid. I also want to do it per field. My question is, in Razor, how do determine if the "Subject" property is valid, or if it has errors? Thanks!
While ValidationMessageFor is the standard approach for displaying validation errors, the following code does exactly what you asked for and can be useful in rare circumstances:
#if (!ViewData.ModelState.IsValidField("Subject"))
{
//not valid condition
}
As was said in comments and by #PeterB - for displaying validation messages per-input should be used Html.ValidationMessageFor method somewhere near with your input on a view. But I want to notice: you have a model but do not use it in your form. More of this, you have data annotations for client and server validation and labeling and don't use them on your view too.
Please check this approach for your view:
#model /*your.possible.namespace.*/ContactModel
...
#using (Html.BeginForm("Result", "Contact", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { role="form" }))
{
<h4>What do you want to tell us?</h4>
<div class="form-group label-floating">
#Html.LabelFor(m => m.Subject, new { #class = "control-label" })
#Html.TextBoxFor(m => m.Subject, new { #class = "form-control", #id = "Subject" })
#Html.ValidationMessageFor(m => m.Subject)
</div>
}
This snippet should display the error message that you described in ErrorMessage property of Required data annotation after posting a form. You can find useful to enable an unobtrusive validation.
You can find an article about validation in ASP.NET MVC guides.
P.S.: And don't forget to perform a server-side validation in controller (for example).
In the Controller you can use ModelState.IsValid to see if there are errors, and also ModelState["nameOfField"] to see if a specific field has errors.
In the View you can use Html.ValidationSummary() (show all errors, if any) or Html.ValidationMessageFor() (show error for specific control, if any). Examples are plenty, both on & off StackOverflow.

ValidationMessageFor not showing up in boostrap modal

Inside my bootstrap modal is a form. When I click the submit button , the validation message error is not showing up. (I intend to enter/input invalid data). I reload the boostrap modal if there is detected error , and I do this in the controller in order to keep the modal open. Is there a different approach here? I wonder why the error message doesn't show up.
Model:
[Display(Name="Email:")]
[EmailAddress(ErrorMessage="Invalid Email Address!")]
public string Email { get; set; }
View:
#Html.LabelFor(e => e.Email, new { #class = "control-label col-md-2" })
<div class="col-md-2">
#Html.EditorFor(e => e.Email, new {htmlAttributes = new { #class = "form-control", required = "required" } })
#Html.ValidationMessageFor(model => model.Email)
</div>
Controller:
//for submit button
if (ModelState.IsValid)
{
//Proceed to Saving
}
//Keep modal open to show validation message
TempData["HasError"] = "YES";
return RedirectToAction("Index");
//Index action
[HttpGet]
public ActionResult Index()
{
//send this in frontend to know if bootstrap modal will reload
ViewBag.Status = TempData["HasError"];
return View();
}
Frontend / js
<script>
$(function (){
var x = '#status';
//show the modal of registration form since there is a validation message error
if (x == "YES") {
var tab = 'SignupFrm';
$('#' + tab).addClass("tab-pane fade in active");
$('.nav-tabs a[href="#' + tab + '"]').tab('show');
$('#mLogin').modal('show');
}
})
</script>
From your comments, you have not included the scripts for client side validation, so first step is to include them (after your jquery 2.1.4 min.js script)
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
or better (assuming your have the default bundles in your BundleConfig.cs file)
#Scripts.Render("~/bundles/jqueryval")
Assuming you have not turned off client side validation, then this will now display the validation error message(s) when you click the form's submit button and cancel the submit (your controller POST method will not be hit).
However, you still need to handle it in the server in case a malicious user by-passes it. Currently, if your model is not valid, you do a redirect to the GET method, which means that ModelState is lost. Instead you need to return the view, and to ensure the modal is reopened, you can add a ViewBag property or just check for ModelState errors in the view). Your POST method should be (note I am assuming the model is Person.cs)
[HttpPost]
public ActionResult Index(Person model) // modify class name to suit
{
if (!ModelState.IsValid)
{
ViewBag.HasErrors = true;
return View(model);
}
// save and redirect
}
Then in your script
$(function () {
if ('#ViewBag.HasErrors') {
// display the modal
}
)};
An alternative to adding a ViewBag property would be to use the following
$(function () {
if ('#ViewContext.ViewData.ModelState.Values.SelectMany(v => v.Errors).Any()') {
// display the modal
}
)};
Side note: You have added required = "required" to your textbox suggesting you want the Email to be required. Adding this attribute does not give you server side validation and adding the jquery validation scripts means it will be ignored (the novalidate="novalidate" attribute is added to your <form> element). Remove it and instead add the RequiredAttribute to your property so that you get both client and server side validation.
[Required(ErrorMessage="Please enter an email address")]
[EmailAddress(ErrorMessage="Invalid Email Address!")]
public string Email { get; set; }
You might want to check if the generated input's "id" and (I think) "name" attributes match the validation's <span> "data-valmsg-for" attribute.
I had a case where I needed custom values for these had the same issue for validation message not showing on client and that was my solution:
#Html.ValidationMessage("newMarkValue", new { #class = "text-danger" })
#Html.EditorFor(modelItem => item.NewMark, null, "newMarkValue", new { htmlAttributes = new { #class = "form-control" } })
Renders the validation <span> as:
<span class="field-validation-valid text-danger" data-valmsg-for="newMarkValue" data-valmsg-replace="true"></span>
And the input, according to the property's Range attribute [Range(2,6, ErrorMessage="Range 2 - 6."] as:
<input class="form-control text-box single-line" data-val="true" data-val-number="The field NewMark must be a number." data-val-range="Range 2 - 6." data-val-range-max="6" data-val-range-min="2" data-val-required="The NewMark field is required." id="newMarkValue" name="newMarkValue" type="number" value="0">
I tried everything from above but nothing seems to work for me.
I tried this , just wrote the script part in my partial view and everything worked.

ASP.NET MVC: Using EditorFor in a loop - ModelBinding to a single entity

I think I may be a little confused :-\
I have a basic scenario whereby I need to render out a list of forms. I have a basic ViewModel that contains a collection of entities. I have an EditorFor template for the entity in question that renders a basic set of inputs.
It looks as follows:
#for (var i = 0; i < Model.Count(); i++)
{
using (Html.BeginForm("Update", "VehicleLog", new { id = Model[i].id }, FormMethod.Post, new { #class = "vehicle-update-form std-form", data_ajaxpost = "true", data_refreshcontainer = "#vehicle-ajax-holder" }))
{
#Html.EditorFor(model => model[i])
<div class="buttons right">
<input type="submit" value="Save" name="action" class="btn-ok btn" />
<input type="submit" value="Delete" name="action" class="btn-warning btn" />
</div>
}
}
I have a for loop that then dumps out these editor template. So far so good.
My issue is that I wish to use UpdateModel in my POST action to update the entity. However, the data being sent to the controller takes the following format:
My incomplete action looks as follows:
[HttpPost]
public ActionResult Update(int id, FormCollection data, string action)
{
var response = new JsonResponse();
if (action == "Update")
{
var v = UnitOfWork.VehicleRepository.GetById(id);
UpdateModel(v);
}
return Json(response);
}
My issue is that the EditorFor helpers is (and rightly so) prefixing names with array indexes, eg [0].name. As such I am unable to use the UpdateModel method to update an entity.
Any advice appreciated!

ASP.NET MVC ActionLink and post method

Can anyone tell me how can I submit values to Controller using ActionLink and POST method?
I don't want to use buttons.
I guess it has something with jquery.
If you're using ASP MVC3 you could use an Ajax.ActionLink(), that allows you to specify a HTTP Method which you could set to "POST".
You can't use an ActionLink because that just renders an anchor <a> tag.
You can use a jQuery AJAX post.
Or just call the form's submit method with or without jQuery (which would be non-AJAX), perhaps in the onclick event of whatever control takes your fancy.
You can use jQuery to do a POST for all your buttons. Just give them the same CssClass name.
Use "return false;" at the end of your onclick javascript event if you want to do a server side RedirectToAction after the post otherwise just return the view.
Razor Code
#using (Html.BeginForm())
{
#Html.HiddenFor(model => model.ID)
#Html.ActionLink("Save", "SaveAction", "MainController", null, new { #class = "saveButton", onclick = "return false;" })
}
JQuery Code
$(document).ready(function () {
$('.saveButton').click(function () {
$(this).closest('form')[0].submit();
});
});
C#
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SaveAction(SaveViewModel model)
{
// Save code here...
return RedirectToAction("Index");
//return View(model);
}
#Aidos had the right answer just wanted to make it clear since it is hidden inside a comment on his post made by #CodingWithSpike.
#Ajax.ActionLink("Delete", "Delete", new { id = item.ApkModelId }, new AjaxOptions { HttpMethod = "POST" })
Here was an answer baked into the default ASP.NET MVC 5 project I believe that accomplishes my styling goals nicely in the UI. Form submit using pure javascript to some containing form.
#using (Html.BeginForm("Logout", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
<a href="javascript:document.getElementById('logoutForm').submit()">
<span>Sign out</span>
</a>
}
The fully shown use case is a logout dropdown in the navigation bar of a web app.
#using (Html.BeginForm("Logout", "Account", FormMethod.Post, new { id = "logoutForm", #class = "navbar-right" }))
{
#Html.AntiForgeryToken()
<div class="dropdown">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
<span class="ma-nav-text ma-account-name">#User.Identity.Name</span>
<i class="material-icons md-36 text-inverse">person</i>
</button>
<ul class="dropdown-menu dropdown-menu-right ma-dropdown-tray">
<li>
<a href="javascript:document.getElementById('logoutForm').submit()">
<i class="material-icons">system_update_alt</i>
<span>Sign out</span>
</a>
</li>
</ul>
</div>
}
ActionLink will never fire post. It always trigger GET request.
Use the following the Call the Action Link:
<%= Html.ActionLink("Click Here" , "ActionName","ContorllerName" )%>
For submitting the form values use:
<% using (Html.BeginForm("CustomerSearchResults", "Customer"))
{ %>
<input type="text" id="Name" />
<input type="submit" class="dASButton" value="Submit" />
<% } %>
It will submit the Data to Customer Controller and CustomerSearchResults Action.
This is taken from the MVC sample project
#if (ViewBag.ShowRemoveButton)
{
using (Html.BeginForm("RemoveLogin", "Manage"))
{
#Html.AntiForgeryToken()
<div>
#Html.Hidden("company_name", account)
#Html.Hidden("returnUrl", Model.returnUrl)
<input type="submit" class="btn btn-default" value="Remove" title="Remove your email address from #account" />
</div>
}
}
Use this link inside Ajax.BeginForm
#Html.ActionLink(
"Save",
"SaveAction",
null,
null,
onclick = "$(this).parents('form').attr('action', $(this).attr('href'));$(this).parents('form').submit();return false;" })
;)
My Solution to this issue is a fairly simple one. I have a page that does a customer search one by the whole email and the other by a partial, the partial pulls and displays a list the list has an action link that points to a actionresult called GetByID and passes in the id
the GetByID pulls the data for the selected customer then returns
return View("Index", model);
which is the post method
This has been a difficult problem for me to solve. How can I build a dynamic link in razor and html that can call an action method and pass a value or values to a specific action method? I considered several options including a custom html helper. I just came up with a simple and elegant solution.
The view
#model IEnumerable<MyMvcApp.Models.Product>
#using (Html.BeginForm()) {
<table>
<thead>
<tr>
<td>Name</td>
<td>Price</td>
<td>Quantity</td>
</tr>
</thead>
#foreach (Product p in Model.Products)
{
<tr>
<td>#p.Name</td>
<td>#p.Price.ToString()</td>
<td>#p.Quantity.ToString()</td>
</tr>
}
</table>
}
The action method
public ViewResult Edit(Product prod)
{
ContextDB contextDB = new ContextDB();
Product product = contextDB.Products.Single(p => p.ProductID == prod.ProductId);
product = prod;
contextDB.SaveChanges();
return View("Edit");
}
The point here is that Url.Action does not care whether the action method is a GET or a POST. It will access either type of method. You can pass your data to the action method using
#Url.Action(string actionName, string controllerName, object routeValues)
the routeValues object. I have tried this and it works. No, you are not technically doing a post or submitting the form but if the routeValues object contains your data, it doesnt matter if its a post or a get. You can use a particular action method signature to select the right method.
I have done the same issue using following code:
#using (Html.BeginForm("Delete", "Admin"))
{
#Html.Hidden("ProductID", item.ProductID)
<input type="submit" value="Delete" />
}
This is my solution for the problem.
This is controller with 2 action methods
public class FeedbackController : Controller
{
public ActionResult Index()
{
var feedbacks =dataFromSomeSource.getData;
return View(feedbacks);
}
[System.Web.Mvc.HttpDelete]
[System.Web.Mvc.Authorize(Roles = "admin")]
public ActionResult Delete([FromBody]int id)
{
return RedirectToAction("Index");
}
}
In View I render construct following structure.
<html>
..
<script src="~/Scripts/bootbox.min.js"></script>
<script>
function confirmDelete(id) {
bootbox.confirm('#Resources.Resource.AreYouSure', function(result) {
if (result) {
document.getElementById('idField').value = id;
document.getElementById('myForm').submit();
}
}.bind(this));
}
</script>
#using (Html.BeginForm("Delete", "Feedback", FormMethod.Post, new { id = "myForm" }))
{
#Html.HttpMethodOverride(HttpVerbs.Delete)
#Html.Hidden("id",null,new{id="idField"})
foreach (var feedback in #Model)
{
if (User.Identity.IsAuthenticated && User.IsInRole("admin"))
{
#Html.ActionLink("Delete Item", "", new { id = #feedback.Id }, new { onClick = "confirmDelete("+feedback.Id+");return false;" })
}
}
...
</html>
Point of interest in Razor View:
JavaScript function confirmDelete(id) which is called when the link generated with #Html.ActionLink is clicked;
confirmDelete() function required id of item being clicked. This item is passed from onClick handler confirmDelete("+feedback.Id+");return false; Pay attention handler returns false to prevent default action - which is get request to target. OnClick event for buttons could be attached with jQuery for all buttons in the list as alternative (probably it will be even better, as it will be less text in the HTML page and data could be passed via data- attribute).
Form has id=myForm, in order to find it in confirmDelete().
Form includes #Html.HttpMethodOverride(HttpVerbs.Delete) in order to use the HttpDelete verb, as action marked with the HttpDeleteAttribute.
In the JS function I do use action confirmation (with help of external plugin, but standard confirm works fine too. Don't forget to use bind() in call back or var that=this (whatever you prefer).
Form has a hidden element with id='idField' and name='id'. So before the form is submitted after confirmation (result==true), the value of the hidden element is set to value passed argument and browser will submit data to controller like this:
Request URL:http://localhost:38874/Feedback/Delete
Request Method:POST Status Code:302 Found
Response Headers
Location:/Feedback
Host:localhost:38874
Form Data X-HTTP-Method-Override:DELETE id:5
As you see it is POST request with X-HTTP-Method-Override:DELETE and data in body set to "id:5". Response has 302 code which redirect to Index action, by this you refresh your screen after delete.
I would recommend staying pure to REST principles and using an HTTP delete for your deletes. Unfortunately HTML Specs only has HTTP Get & Post. A tag only can a HTTP Get. A form tag can either do a HTTP Get or Post. Fortunately if you use ajax you can do a HTTP Delete and this is what i recommend. See the following post for details: Http Deletes
Calling $.post() won't work as it is Ajax based. So a hybrid method needs to be used for this purpose.
Following is the solution which is working for me.
Steps:
1. Create URL for href which calls the a method with url and parameter
2. Call normal POST using JavaScript method
Solution:
In .cshtml:
View
Note: the anonymous method should be wrapped in (....)()
i.e.
(function() {
//code...
})();
postGo is defined as below in JavaScript.
Rest are simple..
#Url.Action("View") creates url for the call
{ 'id': #receipt.ReceiptId } creates parameters as object which is in-turn converted to POST fields in postGo method. This can be any parameter as you require
In JavaScript:
(function ($) {
$.extend({
getGo: function (url, params) {
document.location = url + '?' + $.param(params);
},
postGo: function (url, params) {
var $form = $("<form>")
.attr("method", "post")
.attr("action", url);
$.each(params, function (name, value) {
$("<input type='hidden'>")
.attr("name", name)
.attr("value", value)
.appendTo($form);
});
$form.appendTo("body");
$form.submit();
}
});
})(jQuery);
Reference URLs which I have used for postGo
Non-ajax GET/POST using jQuery (plugin?)
http://nuonical.com/jquery-postgo-plugin/
jQuery.post() will work if you have custom data. If you want to post existing form, it's easier to use ajaxSubmit().
And you don't have to setup this code in the ActionLink itself, since you can attach link handler in the document.ready() event (which is a preferred method anyway), for example using $(function(){ ... }) jQuery trick.
Came across this needing to POST from a Search (Index) page to the Result page. I did not need as much as #Vitaliy stated but it pointed me in the right direction. All I had to do was this:
#using (Html.BeginForm("Result", "Search", FormMethod.Post)) {
<div class="row">
<div class="col-md-4">
<div class="field">Search Term:</div>
<input id="k" name="k" type="text" placeholder="Search" />
</div>
</div>
<br />
<div class="row">
<div class="col-md-12">
<button type="submit" class="btn btn-default">Search</button>
</div>
</div>
}
My Controller had the following signature method:
[HttpPost]
public async Task<ActionResult> Result(string k)

Resources