Reputation: 25
I am new to MVC and stuck with a problem. I have a view that allows users to select an item and then click on a button to view a report on that item. The report is a also an MVC View that expects it's model to be initialized. There is a controller action method to initialize the Model and call the report view.
So my view needs to call the report controller that initialized the model and calls the report view. The call to the report controller has to be made from a JavaScript function.
Finally, I want the report to come up in a new window while leaving the original view up.
I can call the report View directly using window.open, but that skips the report initializer and the report view crashes when it tries to access the model.
Is there a way to call a controller using window.open? Can I call the report controller from the report view if the model is null? can I call the report controller from my JavaScript function in the first view?
Or how do you suggest I handle this? Thanks for your help!
URW
Pseudo code follows:
Below is the code for the View I am working on. It's a search form. The user can enter some values (or not) and click search, on the top part of the form. the form re-displays to show search results below the search form. below the list of items found, are 2 buttons: One to call the ShowReport function and one to call a Wizard. The 2 buttons are on seperate forms, because both need to submit. At least that's how I see it right now. I can change that, if having 2 forms is a problem.
// This is the function that should call the controller to show the report
function ShowReport()
{
if (ReportID != null && ReportID > 0)
{
// I need to call report controller passing ReportID
}
else
{
alert("The Report ID is not valid. Please select a different itemand try again.");
return;
}
}
This function stores the reportID whenever the user selects an item in the list by clicking the radio button at the beginning of the row. It also enables or disables the ShowReport and Run Wizard buttons, depending on the selected item. The report is not available for all items in the list, so this function checks to see if a report is available for the current selection and enables the SHOW Report button if a report is available.
function activateStart(annualWaterReportId) {
// store the ReportID for this ID in a global
// in case the user wants to see the Full report
ReportID = reportId;
document.getElementById("startButton").disabled = false;
if (ReportId > 0) {
document.getElementById("viewReport").disabled = false;
} else {
document.getElementById("viewReport").disabled = true;
}
}
THis function starts the report wizard when the user selects and item and clicks the RunWizard button.
function startAction() {
var elements = document.getElementsByName("Select");
var id = null;
for (var i = 0; i < elements.length; i++) {
if (elements[i].checked) {
id = elements[i].value;
break;
}
}
if (id != null) {
document.getElementById("IDList").value = id;
document.forms["runWizard"].submit();
}
else {
alert("You must first select an item before pressing the Start Wizard button. " );
}
}
THe following form displays 3 fields that will become search parameters when the user enters/selects something and clicks Search below the search fields.
@using ((Html.BeginForm("Index", "Search", FormMethod.Post, new { onsubmit = "showLoadMask('Searching...')" })))
{
<table class="searchTable">
<tr>
<td>
<div>
Name:</div>
<input name="searchName" type="text" value="@ViewBag.searchName" />
</td>
<td>
<div>
ID Number:</div>
<input name="searchID" type="text" value="@ViewBag.searchID"/>
</td>
<td>
<div>
Year:</div>
<input name="searchYear" type="text" value="@ViewBag.searchYear"/>
</td>
</tr>
<tr>
<td colspan="4">
<input type="submit" class="smallButton" value="search" />
</td>
</tr>
</table>
}
next comes the list of items found when the user clicked Search above. Each row is preceded by a radio button, used by the user to select a row before clicking one of the two buttons below the list.
<table style="width: 100%" id="searchResults">
<thead>
<tr>
<th>
Select Person
</th>
<th>
Name
</th>
<th>
City
</th>
<th>
State
</th>
<th>
Zip Code
</th>
</tr>
</thead>
<tbody>
@foreach (var searchVM in Model)
{
<tr>
<td>
<input type="radio" name="Select" value="@searchVM.Number" onclick="activateStart(@searchVM.YearID) " />
</td>
<td>@searchVM.Number
</td>
<td>@searchVM.Name
<td>@searchVM.City
</td>
<td>@searchVM.StateID
</td>
<td>@searchVM.PostalCode
</td>
</tr>
}
</tbody>
Below the search results are 2 buttons that are critical: One to show the Wizard and one to show the report. This form exists only to allow the user to access a Wizard after selecting an item in the list above.
<form id="runWizard" method="post" action="@ViewBag.wizardAction">
<input type="button" value="Start Wizard disabled id="startButton" onclick="startAction()" />
<input type="hidden" name="pageAction" id="pageAction" value="NEXT_ACTION" />
<input type="hidden" name="currentPage" value="3" />
I added this form to use for the "View Report" button so I can better control what happens when the button is clicked.
@using (Html.BeginForm("CallFullReport", "Search", FormMethod.Post, new Dictionary<string, object>{"ReportID", }))
{
// Clicking this button should bring up the report in a new window, while leaving the current window as is.
<input type="submit" value="View Full Report" disabled id="viewReport" onclick="showReport()" />
<input type="hidden" id="ReportID" name="ReportID" value="-1"/>
}
The Controller I want to call is in a seperate dll, The source code is in a file called HomeController.cs the Controller looks like this:
public ActionResult FullReport(int reportID) {
ReportModel reportModel = null;
try {
reportModel = _db.Reports.Find(reportID);
}
catch (Exception) {
// gen up an error message
}
return View(reportModel);
}
Finally, there is the view the controller calls. It displays a report, using the data in reportModel loaded by the controller I've reduced the code to the bare minimum just so I can show you something that makes sense.
<table style="width: 100%">
foreach (Var item in ReportModel)
{
<tr>
<td class="tableLabel base">Name: </td>
<td class="tableData base">@Item.Name</td>
</tr>
<tr>
<td class="lineSpacer"> </td></tr>
<tr>
<td class="tableLabel base">Address: </td>
<td class="tableData base">@Item.Street1</td>
</tr>
@if (Item.Street2 != null && Item.Street2 != "")
{
<tr>
<td> </td>
<td>@Item.Street2</td>
</tr>
}
<tr>
td> </td>
<td>@Item.City, @Item.StateID @Item.PostalCode</td>
</tr>
<tr><td> </td></tr>
<tr><td class="tableLabel base">Phones:</td><td class="tableData base">@(new HtmlString(phones))</td></tr>
<tr><td class="tableLabel base">Email:</td><td class="tableData base"><a href="mailto:@Item.Email">@Item.Email</a></td></tr>
}
</table>
That's all the pseudo code I have. I've inherited all this code and I am new to MVC and everything that goes with it. Let me know if I can provide additional info.
Thanks bunches
Upvotes: 2
Views: 4104
Reputation: 33815
Since there is so much code to post, I'm afraid I can only offer a few generic suggestions. Hopefully they will help solve your problem.
First off, any opening of a new window MUST be done client-side. You cannot return a new window from the server. I know you didn't mention this in your use case, but it's important to note.
If you need the model in your report view populated, it may be easiest to call it with Ajax, passing along the data required to populate your view. The following is a jQuery example of this borrowed from Dr. Molle https://stackoverflow.com/a/3825419/1165998
Give your button that calls the report load a unique id: loadReport
$(document).ready(function () {
$('#loadReport').on('click', function (){
$.post(url, { ReportId: reportId }, function (data) {
var win=window.open('about:blank');
with(win.document)
{
open();
write(data);
close();
}
});
});
});
I think that you can do away with the Html.BeginForm for populating this data, since you want to push it to a new browser window in either case. I would delete the CallFullReport section and just have a single button to load the report (our button with a unique id of loadReport).
Upvotes: 2