简体   繁体   中英

List<T> passed to controller sends empty items

A PartialView contains a model Foo with a List<Bar> . Each Bar item contains two properties, BarA ( string ) and BarB (decimal).
I am trying to render a Chart in that partial view, but for that to work I need to call an action on the same Controller and pass the result to an <img /> element. To render the chart, I need the collections of BarA and BarB to format the data.
So I'm trying with something like this:

Controller

public void GenerateChart(List<Bar> model)
{
    var chart = new System.Web.Helpers.Chart(400, 200)
    .AddTitle("Title")
    .AddSeries(
        name : "name",
        xValue : model.Select(m => m.BarA).ToArray(),
        yValues : model.Select(m => m.BarB).ToArray())
    .Write();
}

Partial View

RouteValueDictionary rvd = new RouteValueDictionary();
for (int i = 0; i < Model.Bars.Count; ++i)
{ rvd.Add("model[" + i + "]", Model.Bars[i]); }

<img src="@Url.Action("GenerateChart", rvd)" />

The problem with this is that even though the model object contains the three items it should contain, these are null.
I also tried to use the ViewBag, like this:

ViewBag.BarA = Model.Bars.Select(m => m.BarA).ToArray();
ViewBag.BarB = Model.Bars.Select(m => m.BarB).ToArray();

With this on the controller side

public void GenerateChart()
{
    var chart = new System.Web.Helpers.Chart(400, 200)
    .AddTitle("Title")
    .AddSeries(
        name : "name",
        xValue : ViewBag.BarA,
        yValues : ViewBag.BarB)
    .Write();
}

But both arrays are null. I've also tried a few different ideas but I'm not able to get the information I need.

To triple-check (the data is shown fine in the view) that the data is correct, I changed to this:

@{
    string[] barAs = Model.Select(m => m.BarA).ToArray();
    decimal[] barBs = Model.Select(m => m.BarB).ToArray();
    ViewBag.BarAs = barAs; // this has 3 items with the expected data
    ViewBag.BarBs = barBs; // this also works
}
<img src="@Url.Action("GenerateChart")" />

string[] BarAs = ViewBag.BarAs; // this assigns null
decimal[] BarBs = ViewBag.BarBs; // this also assigns null

It seems you don't really understand how MVC works. I encourage you to spend some time going through all the tutorials at http://asp.net/mvc to familiarize yourself with the framework. Namely, it seems you're trying to approach a lot of this as if you were in the world of Web Forms. MVC is an entirely different beast.

First, Html.Action cannot return a full image, because all it's going to do is just dump the return value to the HTML being generated, and you can't embed a binary object directly in HTML.

Second, even if you could , you can't use that as the src for an img tag. The src must be a string, namely a URL, point to a location of an image.

So, in order to achieve this. You will need a full action that returns a proper response as an image. Then, you can simply link your image src to the route that hits this action.

public ActionResult GenerateChart(List<Bar> model)
{
    var chart = new System.Web.Helpers.Chart(400, 200)
        .AddTitle("Title")
        .AddSeries(
            name : "name",
            xValue : model.Select(m => m.BarA).ToArray(),
            yValues : model.Select(m => m.BarB).ToArray())
    .GetBytes("jpeg");

    return File(chart, "image/jpeg");
}

Then,

<img src="@Url.Action("GenerateChart", new { model = rvd })" alt="" />

Now, you're just link to a URL. That URL maps to a route that hits your GenerateChart action, which then returns an actual image - same as if you directly linked to a physical image. Now, the browser can properly render the img tag to the page.

Passing a complex type to actions via GET request is technically bloody thing so I do not know if this solution fits your needs you can follow up the method below;

Your action will recieve model as serialized string and you have to Deserialize it to your model

public ActionResult GenerateChart(string modelAsString)
{
    List<Bar> model = new List<Bar>();
    model = JsonConvert.DeserializeObject<List<Bar>>(modelAsString);

    var chart = new System.Web.Helpers.Chart(400, 200)
    .AddTitle("Title")
    .AddSeries(
        name: "name",
        xValue: model.Select(m => m.BarA).ToArray(),
        yValues: model.Select(m => m.BarB).ToArray())
    .GetBytes("jpeg");

    return File(chart, "image/jpeg");
}

Then you need to call your action via querystring like ?modelAsString={jsonData} The example URL I use to process data : http://localhost:18681/Home/GenerateChart/?modelAsString=[{%22BarA%22:%22barAData%22,%22BarB%22:1.1},{%22BarA%22:%22barAData2%22,%22BarB%22:441.14},{%22BarA%22:%22barAData43%22,%22BarB%22:44.1}]

You should create your <img> URLs via serializing your model which ready on the page's action which you use <img> s.

I have tested a dummy data you can see output below; 虚拟示例

PS: for creating dummy data I used the method below;

public ActionResult DummyData()
{
    List<Bar> model = new List<Bar>();
    model.Add(new Bar() { BarA = "barAData", BarB = 1.1m });
    model.Add(new Bar() { BarA = "barAData2", BarB = 441.14m });
    model.Add(new Bar() { BarA = "barAData43", BarB = 44.1m });
    return Json(model, JsonRequestBehavior.AllowGet);
}

And I wonder too if any more efficient way to do this via get request.

Hope this helps!

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