Pass Spring model vars from JSP template to javascript - javascript

Within a <script> tag in a jsp file I have some code that looks like this:
var chart = new myChart({'id': ${user.id}, 'type': ${user.type}, 'name': {user.name});
Where user is a Spring model attribute set on the server side. My problem is that passing each different field of user feels clunky. It would be nice if I could do something like this:
var chart = new myChart('user': "${user}");
However when done this way the value of ${user} will be an object string that looks like this:
User{id=1, type='admin', name='foo'}
I know that I could just parse this string into an object in my external js, but it feels like there should be a cleaner way to do this. Thanks for the help.
EDIT: Going off of Cameron's answer below I could add this to my server-side controller code:
model.put("user", user);
String userJSON = MyJSONSerializer.serialize(user);
model.put("userJSON", userJSON);
Then in my JSP I could do:
var chart = new myChart({'user': JSON.parse('${userJSON}')});
This does solve my problem of having a messy options object, but it brings up the issue of using two model attributes for the same exact data. It would be ideal if I could use one model attribute for the user object, and then could somehow encode it to JSON on the fly.

You can convert the Object to JSON using a library like Jackson:
public static String objectAsJSON(Object obj) {
try
{
// This could be optimized by making a static ObjectMapper
return new ObjectMapper().writeValueAsString(obj);
}
catch (IOException e)
{
// Log exception and re-throw or return null
return null;
}
}
You could either call this method before putting the objectin your model and reference the String in your JSP like this:
var chart = ${jsonChart};
Or you could write a custom taglib and call objectAsJSON() from there. Then the actual myChart object could go in your model once. Your JSP might look like:
var chart = <chartlib:toJson obj="${chart}" />;

Related

Using javascript/jquery variables inside blade

I'm trying to use javascript variables inside laravel blade.
But I'm getting this error.
What should I do for using this ?
var activeTab = $("ul#tablist li.active");
var floor = {!! \App\Floor::find(activeTab.attr('id')) !!}
Use of undefined constant activeTab - assumed 'activeTab'
Ok, so you can have another route like this:
Route::post('/getFloorById', [
'uses' => 'YourController#getFloorById',
'as' => 'getFloorById'
]);
Then, in you controller you have the method:
private function getFloorById($segments)
{
$floorId = $segments[1];
return \App\Floor::find($floorId);
}
And last, you need your javascript code:
var floor = $.post('/getFloorById', {"id": activeTab.attr('id')});
This is the ajax method. Keep in mind that you might need to use echo instead of a return. And you might need to wrap your code into json_encode(\App\Floor::find($floorId)) like this, but for the most part this should get the job done.
You might want to call the post from an onclick event that way you get the new object every time the tab changes.
Unfortunately blade is just a framework to output php. You don't need to do that, and you won't be able to anyways. That would be a big security issue.
If you really need that floor use what #Rohan suggested, javascript directly:
var floor = activeTab.attr('id');

Creating an array of JSON objects in JavaScript on a Razor page from custom viewmodel in MVC?

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; }
}
}
}
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));
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.
Try
var yourJavascriptArray=#Html.Raw(Json.Encode(YouModel));

What is the equivalent of this javascript object in c#

I have this javascript (I'm new to javascript so forgive me)
var data = [
['fff', 10.38],
['ddd', 56.33],
['ss', 24.03],
['ff', 4.77],
['dd', 0.91]
];
alert(data);
To me this looks like an array of arrays. and the alert gives:
fff,10.38,ddd,56.33,ss,24.03,ff,4.77,dd,0.91
I'm trying to create this in a c# Model - I've tried a lot but it never seems to provide exactly the same result.
alert('#Model.obs');
where the Model is:
//property
public object[] obs { get; set; }
//contructor
public EmployeeAbsenceChartViewModel()
{
object[] obbs1 = new object[2];
obbs1[0] = "rick";
obbs1[1] = 3;
object[] obbs2 = new object[2];
obbs2[0] = "rick";
obbs2[1] = 3;
obs = new object[] { obbs1, obbs2 };
}
the alert for the #Model.obs results in
System.Object[]
This should be pretty simple but the javascript that uses the data object doesn't like what I return from the model. I think I'm, missing something somewhere...
so, how would I recreate the var data object in my c# model?
When you directly write object to your View, it executes ToString() method.
In order to insert data in such format that JavaScript can work with it, you need to use JSON.
alert('#Html.Raw(JsonConvert.SerializeObject(Model.obs))');
Note that JsonConvert is a part of JSON.NET library which you can obtain using NuGet. Probably, you can use Json.Encode native method - it should work in the same way.

Passing Dictionary from controller to Javascript

I have a view model like this:
public class weightdata
{
...some properties
public string weights;
}
Then, I have in the controller:
weightdata details = new weightdata();
Dictionary<string,float> dict = new Dictionary<string,float>();
//do something to fill the dictionary with values
var ser = new System.Web.Script.Serialization.JavaScriptSerializer();
details.weights = ser.Serialize(dict);
return View(details);
Then in the view:
<script type="text/javascript">
var dict = #{Html.Raw(new JavaScriptSerializer().Deserialize<Dictionary<string,float>>(Model.Weights));}
</script>
But the rendering of the page is:
var dict = (it's blank)
How can I get this dictionary of values into where it can be used by javascript?
Your property is already serialized, meaning it's already JSON. You don't want to deserialize it on the view, just write it out directly:
var dict = #Html.Raw(Model.Weights);
The other alternative is to make your property a Dictionary<string, float> instead of a string, then you would serialize it on the view:
var dict = #Html.Raw(new JavaScriptSerializer().Serialize(Model.Weights));
Something I just recently read about which may make your view a bit cleaner - you can actually dump the JSON into its own script tag with type="application/json", and reference it in javascript. This may make your editor a little happier, since it's easier to separate the javascript from the C# code.
Something like:
<!-- assuming your Weights property is the serialized JSON string -->
<script id="some-json" type="application/json">
#Model.Weights
</script>
<script type="text/javascript">
var dict = JSON.parse(document.getElementById("some-json").innerHTML);
</script>
Just make sure you're targeting IE8+ or a real browser - if you need IE7 support, you'll need something like json2.js.

Can I Pass a JS Object or Reference to a JS Object to a Function in the HTML Markup?

Pretty noobish question, and I'm probably thinking about this wrong, but...
Is there a way to pass a javascript object (or a reference to it) to a javascript function within the HTML markup?
For example:
<script type="text/javascript">
var myObject = new Object();
$('body').append('<div onclick=testThis(' + myObject + ')></div>');
function testThis(object)
{
console.log(object);
}
</script>
The markup ends up looking something like this when I inspect it:
<div onclick="testThis([object Object])">
Additional context:
The real use case is a search page in which I am querying SOLR via AJAX and get a result back as JS objects. When the user clicks on the HTML markup representing one of these search results, I want to be able to pass the object(or a reference to it) to a separate JS function for processing.
Am I thinking about this the wrong way?
No, you can't embed a reference to an object into markup.
Instead you probably would like to setup your click event listening in Javascript/jQuery:
var object = new Object();
$('<div/>').appendTo('body').click(function() {
testThis(object);
});
function testThis(value) {
console.log(value);
}

Categories