I have a textbox on my MVC 4 view, and I would like to let the user press Enter on changing the text and call a controller's action method. Thanks.
Here is a part of my view:
#using (Html.BeginForm())
{
<div class="editor">
#Html.LabelFor(m => m.FolderPath)
#Html.TextBoxFor(m => m.FolderPath, new { #Id = "FolderPath", #style="width:500px;" })
#Html.ValidationMessageFor(m => m.FolderPath)
</div>
And a part of the controller:
[HttpPost]
[MultipleButton(Name = "action", Argument = "Refresh")]
public ActionResult Refresh(EdiFileModel ediFileModel)
{
if (Directory.Exists(ediFileModel.FolderPath))
{
FolderPath = ediFileModel.FolderPath;
}
else
{
ModelState.AddModelError("FolderPath", "This folder does not exist!");
}
ediFileModel = Load();
return View("Index", ediFileModel);
}
Add a submit button...
<input type="submit" value="Submit"/>
inside your form.
Related
I am trying to append an item to the SQL table but when I press the save button nothing happens. The reset button works but not the Save. Nothing is showing in the database either. Basically, I don't understand why the buttons act as if there was no code attached to it. No Error message or crash shows up either.
View
#model BudgetAmazon.ViewModel.ItemViewModel
#{
ViewBag.Title = "Item";
}
<h2>Index</h2>
<script type="text/javascript">
$(document).ready(function () {
$("#btnSave").click(function () {
SaveItem();
});
$("#btnReset").click(function () {
ResetItem();
});
});
function ResetItem() {
$("#CategoryId").val("1");
$("#ItemCode").val("");
$("#ItemName").val("");
$("#Description").val("");
$("#ItemPrice").val("0");
$("#ImagePath").val("");
}
function SaveItem() {
var formData = new FormData();
formData.append("CategoryId", $("#CategoryId").val());
formData.append("ItemCode", $("#ItemCode").val());
formData.append("ItemName", $("#ItemName").val());
formData.append("Description", $("#Description").val());
formData.append("ItemPrice", $("#ItemPrice").val());
formData.append("ImagePath", $("#ImagePath").get(0).files[0]);
$.ajax({
async: true,
type: 'POST',
contentType: false,
processDate: false,
data: formDate,
url: '/Item/Index',
success: function (data) {
if (data.success) {
alert(data.Message);
ResetItem();
}
},
error: function (data) {
alert('There is a problem adding items.');
}
});
}
</script>
<div class="container">
<div class="col-md-4">
<div class="form-group">
Category :
#Html.DropDownListFor(model => model.CategoryId, new SelectList(Model.CategorySelectListItem, "Value", "Text"),
new { #class = "form-control"})
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Item Code :
#Html.TextBoxFor(model => model.ItemCode, new { #class = "form-control", autocomplete = "Off"})
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Item Name :
#Html.TextBoxFor(model => model.ItemName, new { #class = "form-control", autocomplete = "Off" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Description :
#Html.TextBoxFor(model => model.Description, new { #class = "form-control", autocomplete = "Off" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Item Price :
#Html.TextBoxFor(model => model.ItemPrice, new { #class = "form-control", autocomplete = "Off" })
</div>
</div>
<div class="col-md-4">
<div class="form-group">
Image Path :
#Html.TextBoxFor(model => model.ImagePath, new { type = "file", #class = "form-control"})
</div>
</div>
<div>
<input type="button" value="Save" name="save" class="btn btn-primary" id="btnSave"/>
<input type="button" value="Reset" name="reset" class="btn btn-danger" id="btnReset"/>
</div>
</div>
Controller
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using BudgetAmazon.Models;
using BudgetAmazon.ViewModel;
namespace BudgetAmazon.Controllers
{
public class ItemController : Controller
{
private BudgetAmazonEntities3 objBudgetAmazonEntities;
public ItemController()
{
objBudgetAmazonEntities = new BudgetAmazonEntities3();
}
// GET: Item
public ActionResult Index()
{
ItemViewModel objItemViewModel = new ItemViewModel();
objItemViewModel.CategorySelectListItem = (from objCat in objBudgetAmazonEntities.Categories select new SelectListItem()
{
Text = objCat.CategoryName,
Value = objCat.CategoryId.ToString(),
Selected = true
});
return View(objItemViewModel);
}
[HttpPost]
public JsonResult Index(ItemViewModel objItemViewModel)
{
string NewImage = Guid.NewGuid() + Path.GetExtension(objItemViewModel.ImagePath.FileName);
objItemViewModel.ImagePath.SaveAs(Server.MapPath("~/Images/" + NewImage));
Item objItem = new Item();
objItem.ImagePath = "~/Images/" + NewImage;
objItem.CategoryId = objItemViewModel.CategoryId;
objItem.Description = objItemViewModel.Description;
objItem.ItemCode = objItemViewModel.ItemCode;
objItem.ItemId = Guid.NewGuid();
objItem.ItemName = objItemViewModel.ItemName;
objItem.ItemPrice = objItemViewModel.ItemPrice;
objBudgetAmazonEntities.Items.Add(objItem);
objBudgetAmazonEntities.SaveChanges();
return Json(new {Success = true, Message = "Item is added Successfully."}, JsonRequestBehavior.AllowGet);
}
}
}
ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace BudgetAmazon.ViewModel
{
public class ItemViewModel
{
public Guid ItemId { get; set; }
public int CategoryId { get; set; }
public string ItemCode { get; set; }
public string ItemName { get; set; }
public string Description { get; set; }
public decimal ItemPrice { get; set; }
public HttpPostedFileBase ImagePath { get; set; }
public IEnumerable<SelectListItem> CategorySelectListItem { get; set; }
}
}
I think the problem is you have made a typo mistake while giving the forma data. Try changing "formDate" to "formData". I have attached the picture to show where to.
enter image description here
As far as I know, your url may require something like .html file extension. Try to revise like url: "/Item/index.html, and see if the request success or not.
I have a class in my project which looks like this:
public class MyClass
{
[Required]
public string keyword { get; set; }
public string saleRange { get; set; }
}
This is how the action looks like:
[HttpPost]
[ActionName("Analyze")]
public ActionResult Analyze(MyClass obj)
{
if (!ModelState.IsValid)
{
return Json("error");
}
else
{
return Json("ok");
}
}
And in jQuery:
$(document).on("click",".btnAnalyze",function() {
var data =
{
keyword: $(".keywordInput").val(),
saleRange: "1"
};
console.log($(".keywordInput").val());
$.post("/Controller/Analyze",data).done(function(){
if (data == "error") {
alert("all fields are required");
}
});
});
As you can see I'm mapping a JS object to a C# class object which I pass to my action. Now what the problem here is, even if I don't supply anything for the keyword property (ie. it's null or ""), the ModelState still shows it's property IsValid as true, but it should be set on false if I don't pass anything as keyword parameter.
This is first part of the problem, the other question I have here is how would I, If I have 10 textbox field, of which all are required. And if user enters only 1/10 of those, I would like to send a message for the first next field user has to validate in order to proceed, like "texbox #2 is required, please enter something" instead of a just general message like "all fields are required" or something like that ?
Can someone help me out with this ?
Why does the modelstate shows the it's valid even though after I dont' supply keyword parameter in the object?
#T.Jung here Is a sample of input:
<input placeholder="Enter keywords" class="form-control keywordInput" type="text" style="width:80%">
It's basically just a plain input field in html..
You can use form validation, which will perform data annotation validation on the client side and you do not have to go to the server side.
$('#btnAnalyze').on('click', function (evt) {
if ($('#YourformId').valid()) {
//Perform AJAX call
}
This way if any of the validations do not work, the form.valid() will throw the data annotation errors. So you can add Required to those fields in the models and it will ensure data is not sent if validation fails.
I would do the following:
Make sure your script bundle includes the following 2 files after your jquery and in this order:
jquery.validate.js
jquery.validate.unobtrusive.js
Create your partial / view for your form:
#model MyClassObject
<form action="/action" method="post" id="form">
<div class="row">
#Html.ValidationMessageFor(m => m.Param1)
#Html.LabelFor(m => m.Param1, new { #class = "label" })
#Html.TextBoxFor(m => m.Param1, new { #class = "textbox" })
</div>
<div class="row">
#Html.ValidationMessageFor(m => m.Param2)
#Html.LabelFor(m => m.Param2, new { #class = "label" })
#Html.TextBoxFor(m => m.Param2, new { #class = "textbox" })
</div>
<div class="row">
#Html.ValidationMessageFor(m => m.Param3)
#Html.LabelFor(m => m.Param3, new { #class = "label" })
#Html.TextBoxFor(m => m.Param3, new { #class = "textbox" })
</div>
<input type="submit" value="submit">
</form>
Do you server side validation
[HttpPost]
[ActionName("action")]
public ActionResult action(MyClassObject obj)
{
if(!ModelState.IsValid)
{
return action(); // this is your original action before you do the post
}
else
{
// do your processing and return view or redirect
return View(); // or redirect to a success page
}
}
Add the error message to your class:
public class MyClassObject
{
[Required(ErrorMessage = "Please enter a keyword")]
public string keyword { get; set; }
public string saleRange { get; set; }
}
Your js validation will be plumbed in now but you will need to stop the form from posting and check the validation before doing your ajax request:
var form = document.getElementById('form'), // get a normal js form (I do this just so I can pass in the method and action dynamically - seems better than using .attr() to get them
jqForm = $(form);
jqForm.on('submit', function(e) {
e.preventDefault();
if (jqForm.valid()) {
var formData = jqForm.serializeArray(); // I use serializeArray so I can add extra values if I need to, you can use serialize()
// formData.push({ name: this.name, value: this.value }); - optional for adding extra data
$.ajax({
url: form.action, // this can be just a specific json url if you just need to get some json - it doesn't have to be the same url as the fallback (no js url)
type: form.method,
data: formData,
success: function(result) {
// do success stuff!
}
});
}
})
You should stringify the JavaScript Object in the Jquery post
JSON.stringify(data)
If you want to do the Validation on server side:
Contoller:
[HttpPost]
[ActionName("Analyze")]
public ActionResult Analyze(Model viewModel)
{
if(viewModel.PropertyName.IsNullOrEmpty()){
{
ModelState.AddModelError("PropertyName", "The Field InputFieldName neeeds to be filled!");
}
if (ModelState.IsValid)
{
//Do what ever you want with your Inofrmation
//return a redirct or anything else
}
//If you got here some fields are not filled
return View(viewModel);
}
View:
#Html.TextBoxFor(x => x.PropertyName)
#Html.ValidationMessageFor(m => m.PropertyName, "", new { #class = "text-danger" })
//#class = "text-danger" only if you're using bootstrap
I do all of my validation this way.
I tried to minimize huge problem to a small one so I created the new sample web project; mvc-empty in VS. I created one view named „Index” in Home controller. Index view code:
#model WebApplication16.ViewModels.Home.IndexVM
#{
ViewBag.Title = "Index";
}
#Html.Partial("~/Views/Home/_Orders.cshtml", Model.Orders)
#section scripts{
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script>
$("#Type").change(function () {
$('#order-current > img').remove();
var currentOrder = "#Type_" + $("#Type").find('option:selected').text();
var $img = $(currentOrder).clone();
$img.removeClass("hidden");
$("#order-current").append($img);
$("#ajax-form").submit();
});
</script>
}
Home controller code:
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
IndexVM dataVM = new IndexVM();
GetControlsDataSource(dataVM.Orders);
return View(dataVM);
}
private static void GetControlsDataSource(OrdersVM dataVM)
{
List<SelectListItem> typeControlDataSource = new List<SelectListItem>();
foreach (var en in Enum.GetValues(typeof(TypeEnum)))
{
SelectListItem item = new SelectListItem();
item.Text = en.ToString();
item.Value = ((int)en).ToString();
typeControlDataSource.Add(item);
}
dataVM.TypeControlDataSource = typeControlDataSource;
}
[HttpPost]
public ActionResult Pay(IndexVM dataVM)
{
GetControlsDataSource(dataVM.Orders);
if (ModelState.IsValid)
{
dataVM.Orders.Info = "Info bla bla bla";
return PartialView("~/Views/Home/_Orders.cshtml", dataVM.Orders);
}
else
{
return View(dataVM);
}
}
}
There is also a partial view named “_Orders”, which is rendered on the Index view.The code of _Orders partial view:
#model WebApplication16.ViewModels.Home.OrdersVM
#using (Ajax.BeginForm("Pay", "Home", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "result",
}, new { id = "ajax-form" }))
{
<div id="result">
<div id="order-current">
</div>
<div>
#Html.TextBoxFor(x => x.Name, new { #class = "form-control", style = "margin-top:10px;", id = "Name" })
#Html.ValidationMessageFor(x => x.Name)
</div>
<div>
#Html.DropDownListFor(x => x.Type, Model.TypeControlDataSource, new { #class = "form-control", style = "margin-top:10px;", id = "Type", })
#Html.ValidationMessageFor(x => x.Type)
</div>
<div>
<p>#Model.Info</p>
</div>
<button type="submit" class="btn btn-primary" name="ok"> OK</button>
</div>
}
<div id="orders-container">
<img id="Type_I" src="~/App_Images/Type_I.png" class="img-responsive hidden" />
<img id="Type_II" src="~/App_Images/Type_II.png" class="img-responsive hidden" />
<img id="Type_III" src="~/App_Images/Type_III.png" class="img-responsive hidden"/>
</div>
Index model is described by class IndexVM:
public class IndexVM
{
public IndexVM()
{
this.Orders = new OrdersVM();
}
public OrdersVM Orders { get; set; }
}
_Orders model is described by class OrdersVM:
public class OrdersVM
{
[Required]
public string Name { get; set; }
public string Info { get; set; }
[Required]
public TypeEnum Type { get; set; }
public List<SelectListItem> TypeControlDataSource { get; set; }
}
public enum TypeEnum
{
I,
II,
III
}
After change of value in DropDownListFor control with id=”Type”, the picture from hidden field should be injected by jquery code located in Index view into container with id=”order-current” and after that operation the ajax-form should be submitted. It works properly but after calling
return PartialView("~/Views/Home/_Orders.cshtml", dataVM.Orders);
from the HomeController, the field Info is updated properly but the injected picture from the “order-current” div container is gone. I tried to see what’s wrong in Google Chrome using F12 button and there are no errors but appeared an infinite loop in “browserLink” script. I can’t explain why.
All I want is to see the injected picture in container with id=”order-current after submitting the ajax-form. How to do this and what I did wrong?
Thanks to my friend I finally solved the problem. After updating the “result” container, all events added by jQuery to controls located in this container are unpinned. That’s why it crashes.
The way to make it work correctly is to create a function and pin it to the event OnComplete of AjaxBeginForm. After each update of the result container via ajax, this function is called. I also made a small mistake in Home controller because I inserted a wrong view model class. After all changes, it looks like this;
Home controller code:
public class HomeController : Controller
{
[HttpGet]
public ActionResult Index()
{
IndexVM dataVM = new IndexVM();
GetControlsDataSource(dataVM.Orders);
return View(dataVM);
}
private static void GetControlsDataSource(OrdersVM dataVM)
{
List<SelectListItem> typeControlDataSource = new List<SelectListItem>();
foreach (var en in Enum.GetValues(typeof(TypeEnum)))
{
SelectListItem item = new SelectListItem();
item.Text = en.ToString();
item.Value = ((int)en).ToString();
typeControlDataSource.Add(item);
}
dataVM.TypeControlDataSource = typeControlDataSource;
}
[HttpPost]
public ActionResult Pay(OrdersVM dataVM)
{
GetControlsDataSource(dataVM);
if (ModelState.IsValid)
{
dataVM.Info = "Info bla bla bla";
return PartialView("~/Views/Home/_Orders.cshtml", dataVM);
}
else
{
return View(dataVM);
}
}
}
Index view:
#model WebApplication16.ViewModels.Home.IndexVM
#{
ViewBag.Title = "Index";
}
#Html.Partial("~/Views/Home/_Orders.cshtml", Model.Orders)
<div id="orders-container">
<img id="Type_I" src="~/App_Images/Type_I.png" class="img-responsive hidden" />
<img id="Type_II" src="~/App_Images/Type_II.png" class="img-responsive hidden" />
<img id="Type_III" src="~/App_Images/Type_III.png" class="img-responsive hidden" />
</div>
#section scripts{
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<script>
function imageOnChangeEvent() {
$("#ajax-form").submit();
}
function OnCompleteAjaxForm() {
$('#order-current > img').remove();
var currentOrder = "#Type_" + $("#Type").find('option:selected').text();
var $img = $(currentOrder).clone();
$img.removeClass("hidden");
$("#order-current").append($img);
}
</script>
}
_Orders partial view code:
#model WebApplication16.ViewModels.Home.OrdersVM
<div id="result">
#using (Ajax.BeginForm("Pay", "Home", new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "result",
OnComplete = "OnCompleteAjaxForm()"
}, new { id = "ajax-form" }))
{
<div id="order-current">
</div>
<div>
#Html.TextBoxFor(x => x.Name, new { #class = "form-control", style = "margin-top:10px;", id = "Name" })
#Html.ValidationMessageFor(x => x.Name)
</div>
<div>
#Html.DropDownListFor(x => x.Type, Model.TypeControlDataSource, new { #class = "form-control", style = "margin-top:10px;", id = "Type", onchange = "imageOnChangeEvent()" })
#Html.ValidationMessageFor(x => x.Type)
</div>
<div>
<p>#Model.Info</p>
</div>
<button type="submit" class="btn btn-primary" id="button_ok" name="ok"> OK</button>
}
</div>
how to bind Kendo ui dropdownlist with model and how to send that selected value to controller with a button Click
for other operations....
could you please explain be in detail...am a fresher...
thanks in advance
View:
#model Contract
#{
var vendors = ViewData["Lookups"] as List<Vendor>;
}
#using (Html.BeginForm("Create", "Contract", FormMethod.Post, new { id = "contractDetailsForm" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Contract</legend>
<div>
#{
Html.Kendo().DropDownListFor(model => model.VendorId).Name("VendorId")
.BindTo(new SelectList(vendors, "ID", "DisplayName", "Nothing Selected"))
.HtmlAttributes(new { #style = "width:250px;" })
.Value(Model.VendorId.ToString())
.Render();
}
</div>
<div>
<button type="submit" class="k-button k-button-icontext">
<span class="k-icon k-insert"></span>Create Contract</button>
</div>
</fieldset>
}
Controller:
[HttpPost]
public ActionResult Create(Contract contract)
{
if (ModelState.IsValid)
{
contract.ID = Guid.NewGuid();
_repository.Add(contract);
_repository.SaveChanges();
//return or redirect to another View ...
}
return View("Create", contract);
}
write select event for the dropdown list like
function onSelect(e) {
var DropDownval = $("#QuestionType").val();
}
put this value in the session or in a variable and pass it to the controller.
I have a view with two partials in it that use ajax to post their forms to actions. The onsuccess callback redirects the user to another url. However I don't want this onsuccess function to be called when the modelstate is not valid. I've tried returning a 400 level error from my controller to trigger the onfailure function but it causes some weird behavior and the validation errors don't display. Here's my code
Action:
[AllowAnonymous]
[DisableCache]
public ActionResult Login(string returnUrl ="/") //This is the view that contains the two other partial views.
{
var path = VirtualPathUtility.ToAbsolute(returnUrl);
var url = new Uri(Request.Url, path).AbsoluteUri;
ViewBag.ReturnUrl = url;
return View();
}
[AllowAnonymous]
public ActionResult _LoginPartial()
{
return PartialView(new LoginModel());
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult _LoginPartial(LoginModel model)
{
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
{
return PartialView();
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
Response.StatusCode = 400;
return PartialView(model);
}
Login View:
<hgroup class="title">
<h1>#ViewBag.Title.</h1>
</hgroup>
#section CustomScripts {
#if (ViewData.ModelState.IsValid){
<script type ="text/javascript">
function OnSuccess() {
var returnUrl = #Html.Raw(Json.Encode(ViewBag.ReturnUrl))
window.location = returnUrl;
}
function OnFailure() {
alert("Fail");
}
</script>
}
}
<section id="loginForm">
#{
if(!WebSecurity.IsAuthenticated){
<h2>Use a Creative Works account to log in.</h2>
#Html.Action("_LoginPartial")
#Html.ActionLink("Forgot your password?", "ResetPassword")
}
}
</section>
<section id ="RegisterForm">
#{
if(!WebSecurity.IsAuthenticated){
<span>Don't have an account? Make one!</span>
#Html.Action("RegisterPartial", new { returnUrl = ViewBag.ReturnUrl })
}
}
</section>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
_LoginPartial view
#model Cwo.Domain.Entities.LoginModel
#{
ViewBag.Title = "LoginPartial";
}
#using (Ajax.BeginForm("_LoginPartial",
new AjaxOptions(){UpdateTargetId = "loginForm",
InsertionMode = InsertionMode.Replace, OnSuccess = "OnSuccess", OnFailure = "OnFailure"
})) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Log in Form</legend>
<ol>
<li>
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
#Html.ValidationMessageFor(m => m.UserName)
</li>
<li>
#Html.LabelFor(m => m.Password)
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(m => m.Password)
</li>
<li>
#Html.CheckBoxFor(m => m.RememberMe)
#Html.LabelFor(m => m.RememberMe, new { #class = "checkbox" })
</li>
</ol>
<input type="submit" value="Log in" />
</fieldset>
}
The formatting got kinda messed up pasting in here, sorry about that. Instead of an alert("fail") response I want the validation errors to display. If there's a better way than returning a 400 level error from the action please teach me!
I think another way to word you problem is you don't want the contents of the onsuccess function to be executed? if this is the case then move your #if (ViewData.ModelState.IsValid){ ... } to inside the OnSuccess function. if state is invalid that function would have no guts so nothing gets done.
personally I like keeping javascript separate from razor...even more, the javascript should reside in its own js file. in this case you could use razor to set a bool javascript variable to a function or object constructor, which would then do your success work only if variable was true.