I have View:
#model MvcApplication2.Models.HomeModel
#{
ViewBag.Title = "Home";
}
<h2>Home</h2>
<a>Witaj</a>
#Model.UserName
#using (Html.BeginForm())
{
<a>Podaj hasło</a>
#Html.PasswordFor(m => m.Password)
#Html.ValidationMessageFor(x => x.Password)
<input type="submit" />
}
and Controller
using System.Web.Mvc;
using System.Web.UI;
using MvcApplication2.Models;
namespace MvcApplication2.Controllers
{
[OutputCache(Location = OutputCacheLocation.None, NoStore = true)]
public class HomeController : Controller
{
public ActionResult Index()
{
HomeModel model = new HomeModel() { UserName = "John" };
return View(model);
}
public JsonResult CheckPassword(string password)
{
bool result = false;
if (password.Length < 4)
{
result = false;
}
else
{
result = true;
}
return Json(result, JsonRequestBehavior.AllowGet);
}
}
}
and Model:
using System.Web.Mvc;
namespace MvcApplication2.Models
{
public class HomeModel
{
public string UserName { get; set; }
[Remote("CheckPassword", "Home", ErrorMessage = "Wpisz lepsze hasło")]
public string Password { get; set; }
}
}
When should remote validation method fire? When I click input button? What I do wrong?
Are you missing unobtrusive jQuery references in your view / Razor Layout?
<script src="#Url.Content("~/Scripts/jquery-1.7.1.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
In CheckPassword method password variable is not matching with the Model Password. It should be in TitleCase :-
CheckPassword(string Password)
Related
I have two models, LoginModel and DatabaseModel. Combining them, I have created DatabaseCombinedWithOtherModel. The View, Login.cshtml is Strongly-Typed with the combined model. On running the Login.cshtml, the LoginModel returns null
I have all the necessary get and set methods
Here is the Controller class
namespace ReadingCat.Controllers
{
public class LoginController : Controller
{
private int userid;
// GET: Login
[HttpGet]
public ActionResult Login()
{
return View(new DatabaseCombinedWithOtherModel());
}
[HttpPost]
public ActionResult Login(DatabaseCombinedWithOtherModel model)
{
string realPassword = "";
string paswordFromUser = "";
string query = "SELECT password, userid FROM USERS WHERE username
= '" + model.loginModel.username + "'";
DataSet dataSet = model.databaseModel.selectFunction(query);
if (realPassword == paswordFromUser)
{
userid =
Convert.ToInt32(dataSet.Tables[0].Rows[0].ItemArray[1]);
model.loginModel.userid = userid;
return View("~/Views/Profile/Profile.cshtml",
model.loginModel);
}
else
return View();
}
}
}
Here is the Model:
namespace ReadingCat.Models
{
public class DatabaseCombinedWithOtherModel
{
public DatabaseModel databaseModel { get; set; }
public LoginModel loginModel { get; set; }
}
}
And here is the View:
#model ReadingCat.Models.DatabaseCombinedWithOtherModel
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Login</title>
<!-- Main css -->
<link rel="stylesheet" href="~/css/login.css">
</head>
<body>
<div class="login-page">
<div class="form">
<form class="register-form">
</form>
#using (Html.BeginForm("Login", "Login", FormMethod.Post))
{
<form class="login-form">
#Html.TextBox("Username", null, new { placeholder = "Username",
#class = "login.css" })
#Html.ValidationMessageFor(model => model.loginModel.username);
#Html.Password("Password", null, new { placeholder = "Password",
#class = "login.css" })
#Html.ValidationMessageFor(model => model.loginModel.password);
<div class="form-submit">
<button type="submit" value="Submit" class="submit"
id="submit" name="submit">Login</button>
</div>
<p class="message">Not registered? Create an account</p>
</form>
}
</div>
</div>
<img class="coffee-image" src="~/images/coffee.gif" alt="">
It is giving the following error
System.NullReferenceException: 'Object reference not set to an instance
of
an object.'
ReadingCat.Models.DatabaseCombinedWithOtherModel.loginModel.get returned
null.
I think this error about not fully declare DatabaseCombinedWithOtherModelthis model.
Please try this code.
private int userid
DatabaseCombinedWithOtherModel modelobj = new DatabaseCombinedWithOtherModel();
// GET: Login
[HttpGet]
public ActionResult Login()
{
return View(modelobj);
}
And also try this thing that before call the model in view firstly add some
values in objects from controller side pass return View(modelobj);and then call in view side.
Since you are not seting the loginmodel, it will be null Which will throw the exception.
Either initialize the loginModel in the otherModel's constructor or in the Login get action.
Try
namespace ReadingCat.Models
{
public class DatabaseCombinedWithOtherModel
{
public DatabaseModel databaseModel { get; set; }
public LoginModel loginModel { get; set; }
}
public DatabaseCombinedWithOtherModel()
{
loginModel = new LoginModel();
databaseModel = new DatabaseModel();
}
}
or
[HttpGet]
public ActionResult Login()
{
var vm = new DatabaseCombinedWithOtherModel()
vm.loginModel = new LoginModel();
vm.databaseModel = new DatabaseModel();
return View(vm);
}
The mentioned error goes away if #Html.Textbox is replaced with #Html.EditorFor. Then, another error arises with the DatabaseModel. It returns null. So I created a different object of DatabaseModel and worked with that.
I have a partial view which is displayed on modal window:
public ActionResult Details(string test)
{
var model = _taskService.GetDocumentTasks(test);
return PartialView("_Details", new CustomViewModel { TaskList = model.ToList() });
}
public class CustomViewModel
{
public DocumentStatus Status { get; set; }
public IList<DocumentTask> TaskList { get; set; }
}
I am able to loop TaskList in the view and draw form fields like this:
#model CustomViewModel
#using (Html.BeginForm())
{
#for (int i = 0; i < Model.TaskList.Count(); i++)
{
#Html.HiddenFor(m => m.TaskList[i].TaskId)
<div class="col-sm-6 col-md-4">
<img src="#Html.EditorFor(m => m.TaskList[i].DocumentPath)">
.. other form items here
</div>
}
<input class="btn btn-primary" type="submit" value="Save" />
}
This displays the form fields but when I click the submit button the modal which is sent to the controller is null. I have tried a few different ways of binding it but no success. The model on on the below controller is always null. Any idea how to bind the submitted form details to controller model?
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Details(CustomViewModel model)
{
}
Thanks.
This will work. I made some minor changes, so please follow this:
Controller/Model:
public class HomeController : Controller
{
public class TaskService
{
public Collection<DocumentTask> GetDocumentTasks(string test)
{
Collection<DocumentTask> aCollection = new Collection<DocumentTask>();
var documentTaska = new DocumentTask { TaskId = "patha", DocumentPath = "~/Images/w.JPG" };
var documentTaskb = new DocumentTask { TaskId = "pathb", DocumentPath = "~/Images/w.JPG" };
var documentTaskc = new DocumentTask { TaskId = "pathc", DocumentPath = "~/Images/w.JPG" };
var documentTaskd = new DocumentTask { TaskId = "pathd", DocumentPath = "~/Images/w.JPG" };
aCollection.Add(documentTaska);
aCollection.Add(documentTaskb);
aCollection.Add(documentTaskc);
aCollection.Add(documentTaskd);
return aCollection;
}
}
public class DocumentStatus { }
public class DocumentTask
{
public string TaskId { get; set; }
public string DocumentPath { get; set; }
public string UserDataToProveItWorking { get; set; }
}
public class CustomViewModel
{
public DocumentStatus Status { get; set; }
public IList<DocumentTask> TaskList { get; set; }
}
TaskService _taskService = new TaskService();
public ActionResult Overview()
{
return View();
}
public ActionResult Details(string test)
{
var model = _taskService.GetDocumentTasks(test);
return PartialView("_Details", new CustomViewModel { TaskList = model.ToList() });
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Details(CustomViewModel model)
{
return View("Overview");
}
View Overview.cshtml:
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Overview</title>
</head>
<body>
<div>
#Html.ActionLink("GetPartialView", "Details", new { test = "aTestValue"})
</div>
</body>
</html>
Partial View _Detials.cshtml
#{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Overview</title>
</head>
<body>
<div>
#Html.ActionLink("GetPartialView", "Details", new { test = "aTestValue"})
</div>
</body>
</html>
I am creating a web application where there are five steps.
Home Page1 Page 2 Review Confirmation.
In the url, it goes like localhost:22112/Home/Page1 Page 2 and so forth.
My problem is if someone copies localhost:22112/Home/Page2, then it skips everything
and jumps to page 2 directly. So, how can I stop that? I did the following but its not working properly.
Any suggestions would be really helpful.
In the controller
private bool IsFromIndexPage()
{
if (Session["IsFromIndex"] != null)
{
return true;
}
else
{
return false;
}
}
And for each page actionresult, I am writing it like this.
[HttpGet]
public ActionResult Page1()
{
if (!IsFromIndexPage())
{
return RedirectToAction("Index");
}
.....other methods..
}
[HttpPost]
public ActionResult Page1(Information model, string command)
{
if (!IsFromIndexPage())
{
return RedirectToAction("Index");
}
.....other methods..
}
[HttpGet]
public ActionResult Page2()
{
if (!IsFromIndexPage())
{
return RedirectToAction("Index");
}
.....other methods..
}
[HttpPost]
public ActionResult Page2(Information model, string command)
{
if (!IsFromIndexPage())
{
return RedirectToAction("Index");
}
.....other methods..
}
If you're using session to store the progress through the steps you should be checking your session variables to validate the request is for the given page otherwise redirect the user to the first/current completed page.
You can write a custom request handler for this to keep your session validation code separate your controller code
see this Question about how to implement the basic functionality to what you want to do
EDIT:
switch(currentStep){
case 1:
return Step1(model)
break;
case 2:
return Step2(model)
break;
default:
return new HttpNotFoundResult();
break;
}
Here is a bit different approach, on how to make a wizard with asp.net MVC using ajax.
Your url will be /Home/Wizard on every step. Since using the AjaxOnly attribute, it will not be possible to visit Step1, Step2 etc (see reference in the bottom for AjaxOnly)
Controller:
public ActionResult Wizard()
{
return View();
}
[AjaxOnly]
public ActionResult Step1()
{
return PartialView("Step1");
}
[AjaxOnly]
public PartialViewResult Step2(FormCollection coll)
{
Session["FullName"] = coll["FullName"]!= null ? coll["FullName"].ToString() : string.Empty;
return PartialView("Step2");
}
[AjaxOnly]
public PartialViewResult Confirm(FormCollection coll)
{
WizardModel model = new WizardModel() { Name = Session["FullName"].ToString(), Phone = coll["Phone"] != null ? coll["Phone"].ToString() : string.Empty };
return PartialView("Confirm", model);
}
Model for last step:
public class WizardModel
{
public string Phone { get; set; }
public string Name { get; set; }
}
Make sure you reference jquery.unobtrusive-ajax in your page/layout page
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
Wizard.cshtml
#{
ViewBag.Title = "Wizard";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Wizard - Overview</h2>
#using (Ajax.BeginForm("Step1", new AjaxOptions { HttpMethod="Get", UpdateTargetId = "wizardcontainer" }))
{
<input type="submit" value="Start wizard" />
}
<div id="wizardcontainer"></div>
Step1.cshtml
<div>
<h2>Wizard - Step 1</h2>
<br />
#using(Ajax.BeginForm("Step2", new AjaxOptions { UpdateTargetId = "wizardcontainer" }))
{
#Html.Label("FullName")
#Html.TextBox("FullName")
<input type="submit" value="Next >>" />
}
</div>
Step2.cshtml
<div>
<h2>Wizard - Step 2</h2>
#using(Ajax.BeginForm("Confirm", new AjaxOptions { UpdateTargetId = "wizardcontainer" }))
{
#Html.Label("Phone")
#Html.TextBox("Phone")
#Ajax.ActionLink("<< Previous", "Step1", new AjaxOptions { UpdateTargetId = "wizardcontainer" })
<input type="submit" value="Next >>" />
}
</div>
Confirm.cshtml
#model MvcApplication2.Controllers.WizardModel
<div>
<h2>Wizard - Final Stage</h2>
Name: #Model.Name
<br />
Phone: #Model.Phone
#Ajax.ActionLink("<< Previous", "Step2", new AjaxOptions { UpdateTargetId = "wizardcontainer" })
</div>
Look here for the AjaxOnly attribute:
http://helios.ca/2009/05/27/aspnet-mvc-action-filter-ajax-only-attribute/
This is my Model :
[Required(ErrorMessage = "Email required!")]
[Remote("EmailExists","User",ErrorMessage = "Email already")]
public virtual string Email { get; set; }
View :
#Html.TextBoxFor(x => x.Email)
#Html.ValidationMessageFor(x => x.Email)
Controller:
public ActionResult EmailExists(string Email)
{
return Json(!Email.Equals("teste#gmail.com"),
JsonRequestBehavior.AllowGet);
}
jquery.validate.min.js and jquery.validate.unobtrusive.min.js are added. And web.config is configured as well.
When I type on Email input it fires EmailExists fine. Returns true/false as well. But it nevers shows the ErrorMessage
And I get this error :
Erro: uncaught exception:
[Exception... "Cannot modify properties of a WrappedNative"
nsresult: "0x80570034 (NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN)"
location: "JS frame :: chrome://global/content/bindings/autocomplete.xml ::
onxblpopuphiding :: line 848" data: no]
Any idea?
There is nothing in your description that supposes a problem. I've created a new ASP.NET MVC 3 application using the default template, added the model:
public class MyViewModel
{
[Required(ErrorMessage = "Email required!")]
[Remote("EmailExists", "Home", ErrorMessage = "Email already")]
public string Email { get; set; }
}
updated the HomeController:
public class HomeController: Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
public ActionResult EmailExists(string Email)
{
return Json(
!Email.Equals("teste#gmail.com"),
JsonRequestBehavior.AllowGet
);
}
}
and the ~/Views/Home/Index.cshtml view:
#model AppName.Models.MyViewModel
<script type="text/javascript" src="#Url.Content("~/scripts/jquery.validate.min.js")"></script>
<script type="text/javascript" src="#Url.Content("~/scripts/jquery.validate.unobtrusive.min.js")"></script>
#using (Html.BeginForm())
{
#Html.LabelFor(x => x.Email)
#Html.TextBoxFor(x => x.Email)
#Html.ValidationMessageFor(x => x.Email)
<input type="submit" value="OK" />
}
Validation fires fine and correct error messages are shown (tested with Chrome 10.0, IE9 and FireFox 4.0). So the question now is how does your scenario differs than this one?
You just need to do this:
[Required(ErrorMessage = "Email required!")]
[Remote("EmailExists","User")]
public virtual string Email { get; set; }
and
public JsonResult EmailExists(string Email)
{
string errorMessage = "Email already";
if (!Email.Equals("teste#gmail.com"))
return Json(true, JsonRequestBehavior.AllowGet);
return Json(errorMessage, JsonRequestBehavior.AllowGet);
}
I have a domain model and a view model as follows:
Domain Model:
namespace MvcApplication1.Models
{
public enum Sex { Male, Female };
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
[Required(ErrorMessage="Please select either Female or Male.")]
public Sex? Sex { get; set; }
}
}
View Model:
namespace MvcApplication1.ViewModels
{
public class HomeCreateVM
{
public HomeCreateVM()
{
}
public HomeCreateVM(Person p)
{
Person = p;
SelectList = p.Sex.GetSelectList();
}
public Person Person { get; set; }
public SelectList SelectList { get; set; }
}
}
The auxiliary extension method is defined as follows:
namespace MvcApplication1.Models
{
public static class Utilities
{
public static SelectList GetSelectList<XXX>(this XXX? obj) where XXX : struct
{
var values = from XXX x in Enum.GetValues(typeof(XXX))
select new { Text = x.ToString(), Value = x };
return new SelectList(values, "Value", "Text", obj);
}
}
}
Controller:
public ActionResult Create()
{
var p = new Person();
return View(new HomeCreateVM(p));
}
[HttpPost]
public ActionResult Create(Person hc)// the source of problem!
{
if (ModelState.IsValid)//always false!
{
TempData["status"] = hc;
return RedirectToAction("Confirm");
}
else
return View(new HomeCreateVM(hc));
}
HomeCreateVM.cshtml:
#model MvcApplication1.ViewModels.HomeCreateVM
<div>
Name: #Html.EditorFor(model => model.Person.Name)</div>
<div>
Sex: #Html.DropDownListFor(model => model.Person.Sex, Model.SelectList, "--Select--")</div>
Create View:
#model MvcApplication1.ViewModels.HomeCreateVM
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<legend>HomeCreateVM</legend>
#Html.EditorForModel()
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Question:
There is no problem if the POST Create action method accepts a HomeCreateVM object as the argument.
However, if I change the POST Create action method argument from HomeCreateVM to Person (as shown in the code above), ModelState.IsValid always returns false.
The question is: "Is it possible to pass a ViewModel object to a Create view but only accept a DomainModel object from a POST Create action method?"
Because your view is strongly typed to the view model your form fields will look like this:
<input type="text" name="Person.Name" />
and if you want to bind correctly you need to specify the prefix:
[HttpPost]
public ActionResult Create([Bind(Prefix = "Person")]Person hc)
{
...
}