简体   繁体   中英

set c# mvc session variable via hard-coded html select

I have a C# MVC view that depends on the model data being passed through - tables of numbers - that requires the view to set a threshold that the view starts rendering the data, ie

@for (var value = ["threshold"]; value <= 5; value++){
    ...
}

My current thinking is that the best way to handle this is to have an html select that changes the session variable:

<select id="thresholdSelect">
    <option value="1">1</option>
    <option value="2">2</option>
    <option value="3">3</option>
    <option value="4">4</option>
    <option value="5">5</option>
</select>

Unfortunately, my knowledge of C# MVC means that I don't know how to set the session variable in this way, and my perusal of DropDownListFor boggles my mind.

It seems overly complicated, given the requirement is for hard-coded values limited only to a single page, meaning I would expect the ideal answer to be a few lines in the view that trigger on a change event for a select element. Examples for the DropDownListFor in comparison shows it bouncing around the related controller and model and gets data stored from databases, which is overkill for what I need.

How do I do this? I've been setting session variables at the controller level, but now need one at the view level.

The basic steps I need to implement are:

  1. Show the page with a default session variable if not set (easily done in the controller before returning the view)
  2. Show a select element with the threshold values (easily done as I've put the code in the question)
  3. When the select element changes, fire an event that sets the session variable to the selected value.

MVC doesn't necessarily have "events" in the web-forms sense of the word (though you can use them) but it is generally not advised. MVC corresponds to the HTTP model for sending / receiving data from a server. When you want to interact with the server, you need additional action methods on your controller classes.

Your controller can accept input from the client in several ways but the most basic of these is by specifying arguments to your action methods which MVC will attempt to bind to the request data (form, querystring, etc). Here is an example controller:

//Controller class
public class ReportController : Controller {

    public ActionResult Index() {
        ActionResult result = null;
        ReportSelectionViewModel viewModel = this.BuildViewModel();

        result = View("Index", viewModel);
        return result;
    } // end action Index

    [HttpPost()]
    public ActionResult ChangeReport(ChangeReportRequest changeRequest) {
        ActionResult result = null;
        ReportSelectionViewModel viewModel = this.BuildViewModel();

        Session["ReportOptions"] = changeRequest;
        viewModel.Messages.Add("Selection was changed.");
        viewModel.ReportRequest = changeRequest;

        result = View("Index", viewModel);

        return result;
    } // end action ChangeReport

    //Method that encapsulates the logic needed to build a view model for actions on this controller
    protected ReportSelectionViewModel BuildViewModel() {
        ReportSelectionViewModel viewModel = null;
        viewModel = new ReportSelectionViewModel();
        viewModel.AvailableThresholds.AddRange(new int[] { 1, 2, 3, 4 });
        //Set the value to whatever the user previously selected if available
        if (Session["ReportOptions"] != null) {
            viewModel.ReportRequest.ThresholdSelect = ((ChangeReportRequest)Session["ReportOptions"]).ThresholdSelect;
        } // end if
        return viewModel;
    } // end function BuildViewModel

} // end class ReportController

In the above controller, the default Index() action does not accept any arguments because it does not require any for the first visit. The ChangeReport() action accepts an argument of type ChangeReportRequest called a model. This tells MVC that this action is EXPECTING a value (or series of values) to come from the client and causes MVC to dig deeper to find out which values are supplied by the client to fulfill the controller's argument parameter and return a fully populated model object.

Here is what that the model class looks like:

//Model class that gets built by the DefaultModelBinder
public class ChangeReportRequest {

    private int _reportId;
    private int _thresholdSelect;

    public ChangeReportRequest() {
        _reportId = 1; // Meaningful default value here
        _thresholdSelect = 0;
    } // end constructor

    public int ReportId {
        get {
            return _reportId;
        } set {
            _reportId = value;
        }
    } // end property ReportId

    public int ThresholdSelect {
        get {
            return _thresholdSelect;
        } set {
            _thresholdSelect = value;
        }
    } // end property ThresholdSelect

} // end class ChangeReportRequest

It is also a good idea to have a different type of model (a ViewModel ) that is used specifically for display purposes (which differs from a normal model whose purpose is to hold transactional data). ViewModels add compile-time support in your view for which things can be displayed in the view. An example ViewModel is shown below:

//Strongly typed view model class passed to the view to add intellisense support in the view and avoid the ViewBag.
using System.Collections.Generic;
public class ReportSelectionViewModel {

    private List<int> _availableThresholds;
    private List<string> _messages;
    private ChangeReportRequest _reportRequest;

    public ReportSelectionViewModel() {
        _availableThresholds = new List<int>();
        _messages = new List<string>();
        _reportRequest = new ChangeReportRequest();
    } // end constructor


    public List<int> AvailableThresholds {
        get {
            return _availableThresholds;
        }
    } // end property AvailableThresholds

    public List<string> Messages {
        get {
            return _messages;
        }
    } // end property Messages

    public ChangeReportRequest ReportRequest {
        get {
            return _reportRequest;
        } set {
            if (value != null) {
                _reportRequest = value;
            } // end if
        }
    } // end property ReportRequest

} // end class ReportSelectionViewModel

MVC has built-in model binding that will try to map form fields to input values on controller action methods. By naming the controller method parameter the same as your select list ( you must give your select list a name in addition to it's ID ), you can simply create aa form wrapper around your select list:

Here is the view that would be used with the above code:

@model ReportSelectionViewModel

<h2>Index</h2>
<div class="message-wrapper">
    <ul class="messages">
    @foreach(string message in Model.Messages) {
        <li>@message</li>
    }
    </ul>
</div>
<form method="post" action="@Url.Action("ChangeReport")">
    <select id="ThresholdSelect" name="ThresholdSelect">
    @foreach(int item in Model.AvailableThresholds) {
        <option value="@item" @(Model.ReportRequest.ThresholdSelect == item ? "selected" : "")>@item</option>
    }
    </select>
    <!--Later this can be made into a text box or drop down or whatever, as long as the name matches the model-->
    <input type="hidden" name="ReportId" value="@Model.ReportRequest.ReportId" />
    <input id="submit" type="submit" name="submit" value="Select" />
</form>

The default MVC ModelBinder is smart. It uses reflection via the ModelMetaDataProvider to iterate over the parameters in your method and attempt to find fields from the HTTP request that match based on their name (which is why it's important to have the form fields named). It can do things more complex that an int or a string as well.

Here are some resources:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM