Reputation: 652
So I am trying to populate a model, which needs a userId. So right now, my ActionResult
Index
method just returns a view which prompts for the user to enter their userId
. I need to grab this value and then create the viewmodel and then pass it in back to the view so that way I can do stuff like @Model.blah
. I was wondering how I can do that, whether it'd be having two different action result methods, or in general how to populate models when the information you need must be queried first before constructing the viewmodel
.
Here is my controller:
public ActionResult Index()
{
// Ask for UserID
return View("~/Views/FingerprintTool/Index.cshtml");
}
public ActionResult Index(int userId)
{
var response = _driver.ListFingerprints(userId);
var model = new FingerprintToolModel()
{
Fingerprints = response.Fingerprints
};
return View(model);
}
And here is my html:
model Models.Tools.FingerprintToolModel
<head>
<script type="text/javascript" src="~/Scripts/FingerprintTool.js"></script>
</head>
<body>
<h1>Fingerprint Tool</h1>
<form id="userIdForm" method="post">
Type in your UserId: <input name="userId" type="number" id="formUserId"/>
<input type="submit"/>
</form>
@if (Model != null)
{
<h1>SUCCESS</h1>
}
</body>
I also have a Javascript file that deals with the submit button be clicked and whatnot.
Here is the Js:
window.onload = function() {
$("#userIdForm")
.submit(function(e) {
e.preventDefault();
if ($("#formUserId").length == 0) {
alert("Invalid UserId");
} else {
listUserFingerprints();
}
});
}
function listUserFingerprints() {
// what to do here
}
Upvotes: 2
Views: 9632
Reputation: 3180
First, update your form. You can use a simple HTML form, or the @Html.BeginForm()
helper, like so:
@using Html.BeginForm()
{
@Html.AntiForgeryToken()
<label for="userId">Enter your User ID:</label>
<input type="number" name="userId" id="userId" />
<input type="submit" />
}
By default, your Html.BeginForm
creates all the necessary elements and form action etc. If you prefer, you can still use standard HTML. Both do the same job.
Note the AntiForgeryToken
. This is so useful when using POST form data (and even get, if you really must).
In your Controller, you can then check against this AntiForgeryToken
, to protect against malicious posts or data being injected - see below.
Create another method in your Controller, with the same name as your existing one; decorate it with the [HttpPost]
attribute.
If you are using an AntiForgeryToken
, you need to decorate the method with [ValidateAntiForgeryToken]
also.
Like this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(int userId)
{
// MVC's DefaultModelBinder is smart enough to map your form
// post values to objects of the correct type, given the name in the form
// Get the data from your repository etc.
var model = GetUser(userId);
// Then return this model to the view:
return View(model);
}
Notice the parameter we are looking for in the method signature matches the name
attribute of the input
in your form.
MVC's DefaultModelBinder is able to make the connection between the two and map the value(s) of any parameters to form values.
You can also check if your model
is null
(for example, that userId doesn't exist) and then return an error to the page if so.
I like to use validation errors, but you can also use ViewBag
or any other kind of method.
You can do a check and add an error, like this:
// Get the data from your repository etc.
var model = GetUser(userId);
if (model == null)
{
ModelState.AddModelError("", "The user ID you entered cannot be found. Please try again");
}
// Then return this model to the view:
return View(model);
This will add a "generic" model error to the view data, which you can then process in the view. More on that, below.
In order to support your view displaying your model
, you need to insert an @model
statement at the top of your cshtml
file.
Like so:
@model MyNameSpace.Models.User
This tells the view engine what Model type
to expect from the Controller. In this case I have used User
, but it would be whatever your class is called.
Be sure to use the fully-qualified namespace of your class in order to access it.
Then, in your HTML code, you can access the properties of your model using @Model.YourProperty
.
Like this:
...
<div>@Model.Username</div>
<div>@Model.FullName</div>
<ul>
@foreach (var fingerPrint in Model.FingerPrints){
<li>@fingerPrint.WhateverProperty</li>
}
</ul>
...
As you can see, this loops through the FingerPrints
(or whatever the property is called on your model object) and prints them out in a <ul>
. This is to give you an idea of how to access the data from your model.
It is a good idea to create strongly-typed views like this - as this is the WHOLE idea of MVC in the first place :)
Don't forget to add an if
check around the part of the page you're access the @Model
data (otherwise you will get a NullReferenceException
):
@if (Model != null){
<div id="modelDataInHere">
... // Display the data from your model, nice and pretty-like
</div>
}
Note the casing difference between the declaration and printing the values to the HTML output. I won't go into detail, but this is correct and necessary. Don't mix the two up.
Over to that "Validation Error" (AddModelError
) we added in the Controller.
If you're using the Html.BeginForm()
helper - like in the example - then you can add a Summary
in the form, somewhere. When ModelState.AddModelError()
is called in your controller, this populates the view data and this error can be displayed on the page.
Like so:
@using Html.BeginForm()
{
@Html.AntiForgeryToken()
// This puts any "generic" error messages at the top of the form
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
// Original form fields
<label for="userId">Enter your User ID:</label>
<input type="number" name="userId" id="userId" />
<input type="submit" />
}
No need for any Ajax or any JavaScript in here. You're simply using MVC for what it was designed in the first place.
You can use Ajax, however.
I would encourage you to read into it. There is also a similar helper: Ajax.BeginForm()
, which renders the form as an Ajax one.
A bit of JS set up is required. And your controller action could return a PartialView, rather than a full-blown one.
Have a read up on using Ajax form posts here: http://eliot-jones.com/2014/09/mvc-ajax
Time is marching on, and this post is getting longer.
But for the sake of clarity, your view and controller should look something like below. (I've stuck in a made-up class, so you can see the where the properties come from):
@model YourNameSpace.Models.User
... all your other code in here...
<div>
@using Html.BeginForm()
{
@Html.AntiForgeryToken()
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<label for="userId">Enter your User ID:</label>
<input type="number" name="userId" id="userId" />
<input type="submit" />
}
</div>
@if (Model != null)
{
<!-- If there is a model present, display the data for it: -->
<div>
<div>@Model.Username</div>
<div>@Model.FullName</div>
<ul>
@foreach (var fingerPrint in Model.FingerPrints)
{
<li>@fingerPrint.WhateverProperty</li>
}
</ul>
</div>
}
public ActionResult Index()
{
// This is your standard "GET" request for "Index"
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index(int userId)
{
// MVC's DefaultModelBinder is smart enough to map your form
// post values to objects of the correct type, given the name in the form
// Get the data from your repository etc.
var model = GetUser(userId);
if (model == null)
{
ModelState.AddModelError("", "The user ID you entered cannot be found. Please try again");
}
// Then return this model to the view:
return View(model);
}
public class User
{
public string Username { get; set; }
public string FullName { get; set; }
public List<FingerPrint> FingerPrints { get; set; }
}
Sincerely hope this helps you, and I wish you the best in your project.
Any questions, please feel free to ask :)
Upvotes: 9
Reputation: 4147
Seems like you need to work with json and AJAX method:
[HttpPost]
public ActionResult BasePage(int userId)
{
// user ID is binded to userId variable, based on the input name
var model = populate(userId);
// Remember to return the model to the view
return Json(model);
}
The Javascript for calling this would be:
function listUserFingerprints() {
$.post("/FingerprintTool/BasePage", {userId: $("#formUserId").val() }, function(model) {
console.log(model); // do whatever you want with model here
}
}
Upvotes: 1