Most Valuable Code (Isn't that what MVC stands for?)
Caveat: I am not a jQuery or ASP.NET MVC guru, or even an expert. What I show you in this tip is the result of implementing the answers from several different people to several different questions that I pos[t]ed on StackOverflow.
I won't go into the theory behind or the raison d'etre of MVC; I will just show you what you need to do to send some data from a couple of html elements on a page to a controller, and then some other data back again, from the controller to the page.
These are the basic steps (this assumes you already have a page with some HTML elements on it) you can follow (and tweak, as necessary):
Create a Model
A model is just a regular old C# class (right-click your Models folder and select Add > Class), and can be as simple as this:
public class UnitReportPairModel
{
public List<string> UnitReportPairEmailVals { get; set; }
}
So this model defines a single member, a generic list of string
. You should create it in your project's Models folder (I named it UnitReportPairModel.cs).
Create a Controller
The Controller
is a little more specialized, inheriting Controller
, but can be rather short/simple/sweet:
public class UnitReportPairController : Controller
{
public JsonResult GetUnitReportPairEmailAddresses(string unit, string report)
{
UnitReportPairModel model = new UnitReportPairModel();
try
{
int rptId = GetReportIDForName(report);
DataTable UnitReportPairEmailValsDT = new DataTable();
string qry = string.Format(SQL.UnitReportPairEmailQuery, unit, rptId);
UnitReportPairEmailValsDT = SQL.ExecuteSQLReturnDataTable(
qry,
CommandType.Text,
null
);
List<string> emailAddresses = UnitReportPairEmailValsDT
.AsEnumerable()
.Select(row => row.Field<string>("EmailAddr"))
.ToList();
model.UnitReportPairEmailVals = emailAddresses;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return Json(model, JsonRequestBehavior.AllowGet);
}
private int GetReportIDForName(string rpt)
{
string qry = string.Format(SQL.RptIDForRptNameQuery, rpt);
return SQL.GetIntValForQuery(qry);
}
}</string>
You should create this controller file (I named it UnitReportPairController.cs) in your project's Controllers folder. You can create one of these by right-clicking your project's Controller folder and selecting Add > Controller... > MVC 5 Controller - Empty. replace the default ActionResult Index() that returns View() with your custom code.
This controller is where the model's member[s] is/are inflated/populated. How you do that (with calls to a database or whatever) are, of course, completely up to you. In the case shown above, a call to a SQL Server database is made, then the results are saved to a generic list of string
, which are assigned to the Model's member, and returned as json. But to who? That's where the VIEW part comes in. Note first, though, that your Controller can have standalone/utility methods such as GetReportIDForName()
.
Create a View
As already mentioned, it is assumed that you already have some html on a page. In the scenario I'm showing, I created a select
element and a bunch of checkbox
es, like this (the CSS classes are, for the most part, bootstrap), which I added to the existing Index.cshtml file in the Views\Home folder:
@* row 1: Create select element with "Unit" options *@
<div class="row">
<div class="col-md-12">
<label class="sectiontext">Select a Unit</label>
<select class="form-control, dropdown"
id="unitsselect" name="unitsselect">
<option disabled selected value="-1">Please choose a Unit</option>
@foreach (var field in units)
{
<option id="selItem_@(field.unit)"
value="@field.unit">@field.unit</option>
}
</select>
</div>
</div>
@* row 2: Create Report checkboxes *@
<div class="row">
<div class="col-md-12" id="unitsCheckboxDiv">
<div class="containerforplatypus">
<label class="sectiontext">Select Report[s]</label>
@foreach (var rpt in reports)
{
@* convert id to lowercase and no spaces *@
var morphedRptName = @rpt.report.Replace(" ", string.Empty).ToLower();
<input class="ckbx leftmargin8"
id="ckbx_@(morphedRptName)" type="checkbox"
value="@rpt.report" />@rpt.report
}
</div>
</div>
</div>
Wait, There's More
This is an additional piece of HTML that figures into the "receiving-data-back-in-the-AJAX-callback" part of this tip:
@* row 3: Email recipients input/text elements *@
<div class="row">
<div class="col-md-12">
<div class="containerforplatypus">
<h4 class="h4, sectiontext">Specify Recipients</h4>
<label class="margin4horizontal">Email 1</label>
<input type="text" name="email1" id="email1" />
<label class="margin4horizontal">Email 2</label>
<input type="text" name="email2" id="email2" />
<label class="margin4horizontal">Email 3</label>
<input type="text" name="email3" id="email3" />
<button class="btn btn-primary" name="addlEmails"
id="addlEmails">Add Another Email</button>
</div>
</div>
</div>
Now the jQuery Part of It
I reckon it is considered better practice to break jquery code into separate files, storing them in a Scripts folder, but I just added a Script
section at the bottom of the page, which you can do like so:
@section Scripts
{
<script type="text/javascript">
$(document).ready(function () {
});
</script>
};
I have a moderate amount of code in the Script
section, but to avoid confusing the issue and to keep this tip as simple and focused as possible, I am just going to show the parts that pertain specifically to the interchange of data between the page (client/jquery code) and the server (controller/C# code):
$(".ckbx").change(function () {
var unitval = $('#unitsselect').val();
var rptval = $(this).val();
var model = JSON.stringify({ unit: unitval, report: rptval });
$.ajax({
type: 'GET',
url: '@Url.Action
("GetUnitReportPairEmailAddresses", "UnitReportPair")',
data: { unit: unitval, report: rptval },
contentType: 'application/json',
cache: false,
success: function (returneddata) {
populateemails(returneddata);
},
error: function () {
alert('hey, boo-boo!');
}
});
});
For the AJAX call above to reach your control, the Control
needs to be named UnitReportPairController
(the first argument is the name of the method, the second is the name of the Controller
sans the "controller
" appendage).
So this AJAX call is sending two string
values, such as "RED ROBIN
" and "PRODUCE USAGE
" to the Controller
method. What it gets back from that method (a jsonified generic list of string
, or an array of string
) is dealt with in the success/error dual callback. If the call returns successfully, and the "success
" block is reached, it calls the method populateemails
, passing the returned data along to it. This method is:
function populateemails(trampdata) {
$("#email1").val('');
$("#email2").val('');
$("#email3").val('');
if (trampdata.UnitReportPairEmailVals.length > 0) {
$("#email1").val(trampdata.UnitReportPairEmailVals[0]);
}
if (trampdata.UnitReportPairEmailVals.length > 1) {
$("#email2").val(trampdata.UnitReportPairEmailVals[1]);
}
if (trampdata.UnitReportPairEmailVals.length > 2) {
$("#email3").val(trampdata.UnitReportPairEmailVals[2]);
}
}
Note: This jquery is doubtless rude, crude, and unrefined; feel free to refactor and elegantize it.
Riding Off into the Sunset
And there you have it - selecting a unit and a report invokes the AJAX call, which gets the appropriate email addresses for that unit/report pair, and populates the input text emails with those values. If this tip saves you time, be sure to use that time doing something useful, such as coming up with an antidote for Duckbill Platypus venom (they have a poison toe, don't ya know).