0

I'm trying to populate the array in my script (it's going to be used for charting with D3.JS later on). According to this post, I'm supposed to use the syntax below. However, it doesn't work, bacause I get the error on the pushy line saying Uncaught ReferenceError: WebSite is not defined, where WebSite is the name of the namespace of the data (I'm guessing that, as it's the name of my project).

<script>
  var data = new Array();
  @foreach (var piece in @Model.DataPieces)
  {
    @:data.push(@piece);
  }
</script>

I'm pretty sure it has to do with the data type of piece, because the following change makes it work (at least not producing a bunch of errors). I'm picking out the individual fields from piece object and push those into the array, as a new object.

<script>
  var data = new Array();
  @foreach (var piece in @Model.DataPieces)
  {
    @:data.push({'cat': '@piece.Category', 'key': '@piece.Key', 'val': '@piece.Value'});
  }
</script>

It's inconvenient, prone to mistakes and requires a refactoring of the assignment each time the model changes. How can I avoid this approach and be able to automagically create JSON objects upon assignment, as shown in the first sample?

The viewmodel for the Razor page is declared as folows.

namespace WebSite.Models
{
  public class DrillDataViewModel
  {
    public List<DataPiece> DataPieces { get; set; }

    public class DataPiece
    {
      public string Category { get; set; }
      public string Key { get; set; }
      public int Value { get; set; }
    }
  }
}
Community
  • 1
  • 1
Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438
  • Not sure why anyone would do it this way and not simply pass whole array to javascript variable as json output. Javascript compiler then sees the printed json as a javascript array that is completely ready for consumption – charlietfl Jun 26 '16 at 14:29
  • @charlietfl Might be several reasons. Ignorance being the most likely, perhaps. How would you suggest to approach it? I'm there with my array from *@Model* - how do I pass it into the JavaScript method, exactly? We're talking about the *onload* right? Please elaborate. – Konrad Viltersten Jun 26 '16 at 14:34
  • No... not onload. Rather than create your razor loop, serialize your array to json and print it `var data = <% serialized_json_output %>` . I don't know dot Net syntax for this. Now when browser gets it is ready to be used immediately – charlietfl Jun 26 '16 at 14:37
  • My suggestion matches # 6 in this post. https://blog.mariusschulz.com/2014/02/05/passing-net-server-side-data-to-javascript. For long array avoids new line in page source for each and every element in your array – charlietfl Jun 26 '16 at 14:48
  • @charlietfl Yeah, I'm trying it out too. However, serializing gives me a bunch of *"* elements, like this: *"[{"Category":"Name 7","Key":"Key 5","Value":35}* which creates a lot of space waste... – Konrad Viltersten Jun 26 '16 at 15:08
  • That's not valid either...should be literal quotes. I guess I am spoiled working in php and node.js where all this is done with a single encoding statement. – charlietfl Jun 26 '16 at 15:10
  • @charlietfl Hehe, right. I'll see if I can work it out on .NET side. But just to be clear - it's supposed to be single quotes around the names of the fields in JSON, right? Like this: *[{'Category':'Name 7','Key':'Key 5',';Value':35}]*. – Konrad Viltersten Jun 26 '16 at 15:13
  • 1
    Does this work `var data = @Html.Raw(Json.Encode(@Model.DataPieces))`;? – charlietfl Jun 26 '16 at 15:14
  • JSON itself is supposed to be double quotes and serializer will escape any quotes within the string values. Javascript doesn't care as long as they match. What you output as JSON just to print will we read by js compiler as literal array – charlietfl Jun 26 '16 at 15:17

3 Answers3

1

The line @:data.push(@piece); will be written to the output HTML as

data.push(<the result of calling ToString() on @piece>);

You need to replace @piece with something that will be evaluated to the JSON you want. This should work:

@:data.push(@Html.Raw(Json.Encode(piece)));

Or you can just output the whole array at once:

var data = @Html.Raw(Json.Encode(Model.DataPieces));
Steve Ruble
  • 3,875
  • 21
  • 27
  • I can't get it to work. When I use your suggestion (second one), I get *Unterminated template literal* and checking the page, I see that *data* got the value of *System.Collections.Generic.List`1[WebSite.Models.DrillDataViewModel+DataPiece]* and not the actual values. It feels like if we're close now. Suggestions? – Konrad Viltersten Jun 26 '16 at 15:05
  • @KonradViltersten, you got that error because I left the `Json.Encode` part out. Sorry, I've edited my answer and it should work now. – Steve Ruble Jun 27 '16 at 09:41
0

Try to pass this from Razor page to JavaScript.

@Html.Raw(@JsonConvert.SerializeObject(Model.DataPieces)
  .Replace("{\"", "{")
  .Replace(",\"", ",")
  .Replace("\":", ":"))

The replaces serve to get rid of the invalid characters produced by default in the converter without you needing to play with streams or applying other libraries. Ugly but working.

Sometimes, I also add one more replace: .Replace("\"","'") to get a more JS like look. The outer method is needed so you don't get problems with & in the &quote;.

This is a hand-on solution so if anybody knows a better way, I'd love to get some feedback.

0

Try var yourJavascriptArray=@Html.Raw(Json.Encode(YouModel));

georgeouma
  • 123
  • 2
  • 10