pldiguanaman
pldiguanaman

Reputation: 125

Return a ViewBag ViewModel back to Controller in BeginForm Post method in the View

I have a View which displays a ViewModel on the page. I want to allow the user to press a button to create a CSV file which is then emailed to them. I have the POSt working but the ViewModel being sent back is always empty even though the page clearly show many rows.

This is part of the View in question:

<table style="width:99%" cellpadding="3" class="ContentTable" border="1" align="center">

    @using (Html.BeginForm("SubmitExcel", "AllRecognition", new { AllRecognitions = ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel }, FormMethod.Post, new { id = "submitExcel" }))
    {
        <tr>
            <td style="padding:3px;">
                <input type="submit" name="BtnSubmitExcel" id="BtnSubmitExcel" value="Export to Excel" />
            </td>
        </tr>
    }

    <tr style="background-color:#5D7B9D;color:white;">
        <th style="width:4%;padding:3px;font-size:12px;">Date</th>
        <th style="width:8%;padding:3px;font-size:12px;">Employee</th>
        <th style="width:8%;padding:3px;font-size:12px;">Recognized By</th>
        <th style="width:6%;padding:3px;font-size:12px;">5-Star Standard</th>
        <th style="width:70%;padding:3px;font-size:12px;">Description</th>
        <th style="width:4%;padding:3px;font-size:12px;">Points</th>
    </tr>

    @{

        if (ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel != null)
        {
            foreach (Recognition.ViewModels.AllRecognitionViewModel item in ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel)
            {
                @:<tr>
                    @:<td style="width:4%;padding:3px;font-size:12px;">@item.Date</td>
                    @:<td style="width:8%;padding:3px;font-size:12px;">@item.Employee</td>
                    @:<td style="width:8%;padding:3px;font-size:12px;">@item.RecognizedBy</td>
                    @:<td style="width:6%;padding:3px;font-size:12px;">@item.FiveStarStandard</td>
                    @:<td style="width:70%;padding:3px;font-size:12px;">@item.Description</td>
                    @:<td style="width:4%;padding:3px;font-size:12px;">@item.Points</td>
                @:</tr>
                }



        }
    }

</table>

This is the controller side receiving the POST method:

public ActionResult SubmitExcel(List<ViewModels.AllRecognitionViewModel> AllRecognitions)
    {
        ViewBag.NoSearch = "block";
        ViewBag.SupervisorSearch = "none";
        ViewBag.DepartmentSearch = "none";
        ViewBag.EmployeeSearch = "none";

        DataTable dtAllRecognitions = Base.SQLHelper.ConvertListToDataTable(AllRecognitions.ToList());
        DataSet dsAllRecognitions = new DataSet();
        dsAllRecognitions.Tables.Add(dtAllRecognitions);
        FHSBase.FHS.DataHelper.SendMeExcelFile(dsAllRecognitions, "Recognitions", CurrentUser);

        ViewModels.AllRecognitionBigViewModel AllRecognitionBigViewModel = new ViewModels.AllRecognitionBigViewModel();
        AllRecognitionBigViewModel.AllRecognitionViewModel = null;
        Models.DateRange DateRange = new Models.DateRange();
        DateRange.fromDate = DateTime.Today.Date;
        DateRange.toDate = DateTime.Today.Date;
        AllRecognitionBigViewModel.DateRange = DateRange;
        ViewBag.AllRecognitionBigViewModel = AllRecognitionBigViewModel; 

        List<SelectListItem> empList = new List<SelectListItem>();
        string VPath = "Index";
        return View(VPath, empList);

    }

The "AllRecognitions" view model is empty in the ActionResult but isn't empty in the view itself. How can I get the current view model back to the ActionResult (SubmitExcel) with the current values seen in the View?

Upvotes: 1

Views: 983

Answers (2)

Papa Burgundy
Papa Burgundy

Reputation: 6467

Will this work for you? It's a different approach. Since you are not manipulating anything in your view and just want to export to Excel, why not just put your results in TempData instead of ViewBag and then retrieve it upon the POST? TempData is good in memory for that one trip back.

So, in your initial controller render, do:

TempData["AllRecognition"] = ThisIsMyAllRecognitionViewModelData;

Then when they submit to excel, that data is still in Temp.

public ActionResult SubmitExcel()
    {
var MyDataMadeIt = TempData["AllRecognition"];
// do some stuff
}

Upvotes: 0

David
David

Reputation: 218798

Your form is empty. So no data is being posted to the server.

When you submit a form, it doesn't send the entire page to the server. (How would the server even know what to do with the HTML to get the values you want from it?) It sends key/value pairs from form elements. And you have only one form element:

<input type="submit" name="BtnSubmitExcel" id="BtnSubmitExcel" value="Export to Excel" />

So only that one key/value is being sent to the server.

The first thing you need to do is wrap your form around the data that you want to post to the server. So basically around your entire table.

Next, you'll need to emit actual form elements. The text on the page isn't usable data, it's just page content. Since the model is a List<ViewModels.AllRecognitionViewModel> then you should be able to do it with a simple modification to your loop. So instead of this:

foreach (Recognition.ViewModels.AllRecognitionViewModel item in ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel)

You would want this:

for (var i = 0; i < ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel.Count(); i++)

The form elements can be hidden, so you don't change the current UX in your page. Maybe something like this:

@Html.HiddenFor(x => x[i].Date)

or perhaps:

@Html.HiddenFor(x => x.AllRecognitionViewModel[i].Date)

I'm actually guessing a bit here, since I'm much more accustomed to using the model instead of the ViewBag for this. (Which you may want to try using instead, it'll probably make things simpler.) You may need to do some debugging to determine exactly what's going on in the server-side code here.

Ultimately, what you're looking to see in your client-side code are elements similar to this:

<input type="hidden" name="AllRecognitionViewModel.Date[0]" value="..." />

So, if the above @Html.EditorFor suggestions don't work, you can always do it manually. In the loop it might look something like this:

<input type="hidden" name="AllRecognitionViewModel.Date[@i]" value="@ViewBag.AllRecognitionBigViewModel.AllRecognitionViewModel[i].Date" />

The key thing to notice is the name attribute. By sending these "arrays" of key/value pairs to the server, the model binder should be able to construct the List<ViewModels.AllRecognitionViewModel>.

Upvotes: 0

Related Questions