4

I'm programming an ASP.Net MVC page and I'm using data from the server to create a Google chart. The x-axis is the date. The y-axis is the value. There are 2 lines of data being plotted to compare. Here is the relevant code:

@model IEnumerable<Tuple<DateTime,int,int>>


<div id="chart_div_2" style="width: 900px; height: 500px;"></div>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
    google.load("visualization", "1", { packages: ["corechart"] });
    google.setOnLoadCallback(drawChart);
    function drawChart() {

        var arr = [['Year', 'Sales', 'Expenses']];

        //Using the Razor Model to create a Javascript array.
        var arr2 = [
            @foreach(var row in Model)
            {
                @:["@row.Item1.ToString("MMM d")", @row.Item2, @row.Item3],
            }
        ];

        for (var i = 0; i < arr2.length; i++)
        {
            arr.push(arr2[i]);
        }

      var data = google.visualization.arrayToDataTable(arr);
      var chart = new google.visualization.LineChart(document.getElementById('chart_div_2'));
      chart.draw(data);

    }
</script>

First of all, this code does actually work. Creating arr2 this way does turn a Razor model into something that I can use. However, my nose says code smell. It says that throwing together two languages razor and Javascript, which have somewhat similar C-based programming flow syntax could be confusing to the next person that comes along and tries to read it.

Is there a better way to write this?

3
  • What do you mean use a JSON serializer? Google charts needs a 2-dimensional array, not JSON. Commented Aug 26, 2012 at 23:00
  • [["Year", "Sales", "Expenses"]] is JSON and a 2D array. If you serialize a collection of collections then you'll get a 2D array in JSON. Commented Aug 26, 2012 at 23:14
  • I'm not sure that I understand the code that you mean. A 2-d array looks like [[ "Aug 1" , 1 , 1 ], [ "Aug 2" , 2 , 3 ]] , however a "2D array in JSON" created using IEnumerable<Tuple<DateTime,int,int>> for the model looks like [{"Item1":"\/Date(1343797200000)\/","Item2":1,"Item3":1}, {"Item1":"\/Date(1343797200000)\/","Item2":2,"Item3":3}] . Assuming that I create a JSON object, how does that get translated into the 2D array that the Google function needs? Commented Aug 26, 2012 at 23:20

1 Answer 1

3

However, my nose says code smell.

Oh yeah it stinks, I can feel it.

Is there a better way to write this?

Of course. Never build JSON manually as you did by mixing the 2 languages and writing loops and stuff. Use a JSON serializer:

@model IEnumerable<Tuple<DateTime,int,int>>

<div id="chart_div_2" style="width: 900px; height: 500px;"></div>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
    google.load("visualization", "1", { packages: ["corechart"] });
    google.setOnLoadCallback(drawChart);
    function drawChart() {
        var arr = @Html.Raw(
            Json.Encode(
                new object[] { new[] { "Year", "Sales", "Expenses" } }
                .Concat(
                    Model.Select(x => new object[] 
                    { 
                        x.Item1.ToString("MMM d"), 
                        x.Item2, 
                        x.Item3
                    })
                )
            )
        );

        var data = google.visualization.arrayToDataTable(arr);
        var chart = new google.visualization.LineChart(document.getElementById('chart_div_2'));
        chart.draw(data);
    }
</script>

This will generate an equivalent code markup as yours but the whole model manipulation and encoding is done on the server. You could also write a custom HTML helper in order to simplify your code to this:

public static class ChartExtensions
{
    public static IHtmlString ToChartData(
        this IEnumerable<Tuple<DateTime, int, int>> model, 
        params string[] titles
    )
    {
        return new HtmlString(
            Json.Encode(
                new object[] { titles }
                .Concat(
                    model.Select(x => new object[] 
                    { 
                        x.Item1.ToString("MMM d"), 
                        x.Item2, 
                        x.Item3 
                    })
                )
            )
        );
    }
}

and then in your view:

@model IEnumerable<Tuple<DateTime,int,int>>

<div id="chart_div_2" style="width: 900px; height: 500px;"></div>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
    google.load("visualization", "1", { packages: ["corechart"] });
    google.setOnLoadCallback(drawChart);
    function drawChart() {
        var arr = @Model.ToChartData("Year", "Sales", "Expenses");
        var data = google.visualization.arrayToDataTable(arr);
        var chart = new google.visualization.LineChart(document.getElementById('chart_div_2'));
        chart.draw(data);
    }
</script>
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.