I'm trying to write an object as JSON to my Asp.Net MVC View using Razor, like so:
<script type="text/javascript">
var potentialAttendees = #Json.Encode(Model.PotentialAttendees);
</script>
The problem is that in the output the JSON is encoded, and my browser doesn't like it. For example:
<script type="text/javascript">
var potentialAttendees = [{"Name":"Samuel Jack"},];
</script>
How do I get Razor to emit unencoded JSON?
You do:
#Html.Raw(Json.Encode(Model.PotentialAttendees))
In releases earlier than Beta 2 you did it like:
#(new HtmlString(Json.Encode(Model.PotentialAttendees)))
Newtonsoft's JsonConvert.SerializeObject does not behave the same as Json.Encode and doing what #david-k-egghead suggests opens you up to XSS attacks.
Drop this code into a Razor view to see that using Json.Encode is safe, and that Newtonsoft can be made safe in the JavaScript context but is not without some extra work.
<script>
var jsonEncodePotentialAttendees = #Html.Raw(Json.Encode(
new[] { new { Name = "Samuel Jack</script><script>alert('jsonEncodePotentialAttendees failed XSS test')</script>" } }
));
alert('jsonEncodePotentialAttendees passed XSS test: ' + jsonEncodePotentialAttendees[0].Name);
</script>
<script>
var safeNewtonsoftPotentialAttendees = JSON.parse(#Html.Raw(HttpUtility.JavaScriptStringEncode(JsonConvert.SerializeObject(
new[] { new { Name = "Samuel Jack</script><script>alert('safeNewtonsoftPotentialAttendees failed XSS test')</script>" } }), addDoubleQuotes: true)));
alert('safeNewtonsoftPotentialAttendees passed XSS test: ' + safeNewtonsoftPotentialAttendees[0].Name);
</script>
<script>
var unsafeNewtonsoftPotentialAttendees = #Html.Raw(JsonConvert.SerializeObject(
new[] { new { Name = "Samuel Jack</script><script>alert('unsafeNewtonsoftPotentialAttendees failed XSS test')</script>" } }));
alert('unsafeNewtonsoftPotentialAttendees passed XSS test: ' + unsafeNewtonsoftPotentialAttendees[0].Name);
</script>
See also:
Does the output of JsonConvert.SerializeObject need to be encoded in Razor view?
XSS Prevention Rules
Using Newtonsoft
<script type="text/jscript">
var potentialAttendees = #(Html.Raw(Newtonsoft.Json.JsonConvert.SerializeObject(Model.PotentialAttendees)))
</script>
Related
Trying to run AnglarJS on my website just executing simple $scope variable and cannot get it to work and throwing errors (image attached). Seems simple but I tried everything, correct me if I am missing any statement. Using VS Code, Entity Framework Core. Page renders perfect.
Index.cshtml
<head>
#section scripts{
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular-resource.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular-route.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.9/angular-animate.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="~/SPA/pController.js"></script>
<script src="~/SPA/app.js"></script>
}
<div ng-app="app" ng-controller="pController">
<p{{ word }} /p>
app.js
var app = angular.module('app', ['ngRoute']);
app.controller('pController', pController);
pController.js
var pController = function($scope){
$scope.data = null;
$scope.word = "hello world";
};
pController.$inject = ['$scope'];
RESULT:
UDPATE
Figured something, system cannot find the app.js file. Pasting all the code in html all works.
SOLVED
After a day spent on this problem, I solved it simply putting all my js Angular logic into /wwwroot/js/... I think somehow my project only read js files from that path. Thank you everyone for help!
Reorder the files in your html as:
<script src="~/SPA/app.js"></script>
<script src="~/SPA/pController.js"></script>
Then do any of those:
app.js:
var app = angular.module('app', ['ngRoute']);
pController:
app.controller('pController', function($scope){
$scope.data = null;
$scope.word = "hello world";
});
Or
In case you are using minification:
Modify your app.js file as:
var app = angular.module('app', ['ngRoute']);
app.controller('pController', ['$scope', pController]);
Then your controller file:
var pController = function($scope){
$scope.data = null;
$scope.word = "hello world";
};
If this still didn't work :), put the whole code in one single js file 'app' and then try and let me know the issue.
I am using ng-grid in an ASP.NET web application to display data from a WCF Service. In this Plunker example the data is stored in a JSON file and then converted by the Angular module.
var app = angular.module('myApp', ['ngGrid']);
app.controller('MyCtrl', function($scope, $http) {
$scope.setPagingData = function(data, page, pageSize){
var pagedData = data.slice((page - 1) * pageSize, page * pageSize);
$scope.myData = pagedData;
$scope.totalServerItems = data.length;
if (!$scope.$$phase) {
$scope.$apply();
}
};
$scope.getPagedDataAsync = function (pageSize, page, searchText) {
setTimeout(function () {
var data;
if (searchText) {
var ft = searchText.toLowerCase();
$http.get('largeLoad.json').success(function (largeLoad) {
data = largeLoad.filter(function(item) {
return JSON.stringify(item).toLowerCase().indexOf(ft) != -1;
});
$scope.setPagingData(data,page,pageSize);
});
} else {
$http.get('largeLoad.json').success(function (largeLoad) {
$scope.setPagingData(largeLoad,page,pageSize);
});
}
}, 100);
};
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
$scope.gridOptions = {
data: 'myData',
enablePaging: true,
showFooter: true,
enableCellSelection: true,
enableCellEdit: true,
enableRowSelection: false,
totalServerItems:'totalServerItems',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions
};
});
Plunker
This piece of code was interesting because it read dicrectly from a JSON file and dynamically bound it to the ng-grid. I was hoping that I would be able to use the ASP.NET (C#) codebehind to pass a JSON file to the AngularJS code and let it do all of the work to parse it.
It seems like you cannot do this, so what is the best way to pass data to AngularJS from ASP.NET?
Thanks in advance.
The question you're really asking is, how do I pass some server-side information to Javasript? Angular is irrelevant here.
Solution 1: just write it to a client-side variable. Something like:
If you're using MVC:
var myJson = '#ViewBag.MyJson';
var json = JSON.parse(myJson);
If you're using WebForms:
var myJson = '<% Response.Write(MyJson); %>';
var json = JSON.parse(myJson);
Solution 2: define a server-side method that returns your json string, and call it from the client side using Ajax. Then parse it as previously. There's a decent example here.
Solution 3: can't you write to your Json file from the server side, before loading it into Angular?
You could create an Angular directive that handles parsing, and then pass it a JSON file name in your ASP.NET page, like this:
// ASP.NET page
<%
JSONFile.Text = "path/to/json/file.json";
%>
<!DOCTYPE html>
<html>
<head runat="server">
// including our parse-json directive as "metadata" in the head
<parse-json data-json-file="<%JSONFile%>"></parse-json>
</head>
<body>
...
...
</body>
</html>
And then your angular directive could look something like this:
app.directive('parseJson', ['$http', function ($http) {
return {
restrict: 'E',
scope: {
jsonFile: '#'
}
link: function (scope) {
$http.get('my/url/' + scope.jsonFile)
.success(function (data) {
// your parsing logic would go here.
});
}
};
}]);
The data prefix on the data-json-file attribute is the HTML5 standard for custom data attributes.
So, to summarize the code above. In our ASP.NET page we are including a custom tag (our angular directive) named <parse-json> in the <head> of our html page (it doesn't have to be there, but it is sorta metadata so I figured it fit). We are setting the attribute data-json-file to equal the path of the JSON file on our server. When angular loads on the client, it will request this JSON file, and then from there you can do whatever you want with it (in your case, parse it).
Hope this helps!
I am sending a string from JSON ASP.NET MVC, in my view I receive the JSON string and I assign a Script using ViewBag.string.
My problem is in the view, the string type values are replacing the quotes with me & quot; which is causing me errors.
Controller:
var jss = new JavaScriptSerializer();
string retorno = jss.Serialize(chart.ToList());
ViewBag.datos = retorno;
ViewL
<script type="text/javascript">
new Morris.Bar({
element: 'BarChart',
data:#ViewBag.datos,
xkey: 'Planta',
ykeys: 'Cantidad',
labels: 'Mes'
})
</script>
This is the code that generates me to run the view
<script type="text/javascript">
new Morris.Bar({
element: 'BarChart',
data:`[{"Planta":"CO","Mes":3,"Cantidad":2},{"Planta":"EP","Mes":1,"Cantidad":1},{"Planta":"R1","Mes":1,"Cantidad":2},{"Planta":"RM","Mes":3,"Cantidad":3},{"Planta":"RQ","Mes":3,"Cantidad":1},{"Planta":"TY","Mes":1,"Cantidad":1},{"Planta":"TY","Mes":3,"Cantidad":3},{"Planta":"TY","Mes":4,"Cantidad":2},{"Planta":"ZB","Mes":3,"Cantidad":1},{"Planta":"ZB","Mes":4,"Cantidad":1}],`
xkey: 'Planta',
ykeys: 'Cantidad',
labels: 'Mes'
})
</script>
Razor is assuming the content is HTML and is escaping your JSON. Use Html.Raw to stop this behaviour.
...
data:#Html.Raw(ViewBag.datos)
...
I have a js code in which an array works well when its like
var availableTags = [
"ActionScript",
"AppleScript",
"Asp",
"BASIC",
"C"
];
I then made an array string in my code behind or .cs like on class level
public static string[] test={"animal","lovely"};
I then changed js array to this
var availableTags = "<%=test%>"; // also tried without quotes
Now I m not having the results as was having with previous js array
Editing with complete code,the jquery I taken from http://jqueryui.com/demos/autocomplete/#multiple
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections;
using System.Web.Script.Serialization;
public partial class onecol : System.Web.UI.Page
{
JavaScriptSerializer serializer;
public static string test = "['animal','lovely']";
public static string check;
protected void Page_Load(object sender, EventArgs e)
{
serializer = new JavaScriptSerializer();
//serializer
this.detail.ToolsFile = "BasicTools.xml";
test = returnTitle();
}
}
and the script with html is
<asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server">
<script type="text/javascript" src="Jscript.js"></script>
<script type="text/javascript" src="jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="jquery-ui-1.8.17.custom.css"></script>
<link href="~/jquery-ui-1.8.17.custom.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript">
$(function () {
var availableTags = <%=test%>;
function split(val) {
return val.split(/,\s*/);
}
function extractLast(term) {
return split(term).pop();
}
$("#tags")
// don't navigate away from the field on tab when selecting an item
.bind("keydown", function (event) {
if (event.keyCode === $.ui.keyCode.TAB &&
$(this).data("autocomplete").menu.active) {
event.preventDefault();
}
})
.autocomplete({
minLength: 0,
source: function (request, response) {
// delegate back to autocomplete, but extract the last term
response($.ui.autocomplete.filter(
availableTags, extractLast(request.term)));
},
focus: function () {
// prevent value inserted on focus
return false;
},
select: function (event, ui) {
var terms = split(this.value);
// remove the current input
terms.pop();
// add the selected item
terms.push(ui.item.value);
// add placeholder to get the comma-and-space at the end
terms.push("");
this.value = terms.join(", ");
return false;
}
});
});
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server">
<div class="demo" >
<div class="ui-widget">
<label for="tags">Tag programming languages: </label>
<input id="Text1" class="#tags" size="50" />
</div>
</div>
actually its a auto complete functionality to give tags ,the auto complete suggestions for tagging I want to get from C# code ,I took Jquery source from jqueryui.com/demos/autocomplete/#multiple and then I tried to give it C# string from .cs file , I explained it with code on edited version , with C# code behind it works exactly as its in the link
You need to serialize the C# string array into a javascript array.
http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx
Usually I create a simple static class as a wrapper.
public static class JavaScript
{
public static string Serialize(object o)
{
JavaScriptSerializer js = new JavaScriptSerializer();
return js.Serialize(o);
}
}
Then you can use that class to serialize the item you need to
//C# Array(Member of asp page)
protected string[] Values = { "Sweet", "Awesome", "Cool" };
<script type="text/javascript">
var testArray = <%=JavaScript.Serialize(this.Values) %>
</script>
var availableTags = ['<%=String.join("','",test)%>'];
It would be something like this...
var availableTags = ["<%= string.Join("\", \"", test) %>"];
or
var availableTags = ['<%= string.Join("', '", test) %>'];
The first one would render as
var availableTags = ["Sweet", "Awesome", "Cool"];
and the second one would render as
var availableTags = ['Sweet', 'Awesome', 'Cool'];
both of which are fine for autocomplete.
test property should be a string property and it should render the string which will be parsed by Js engine as an array. Try this.
Code behind property
public static string test= "['animal','usman lovely']";
JS
var availableTags = <%=test%>;//No quotes
only use indexes to get your test string array
i have tried by giving indexes like
var availableTags = "<%=test[0]%>"; // animal
var availableTags = "<%=test[1]%>"; // lovely
If you're doing an online coding challenge, the browser-based IDE might not let you use string.join, like with my job, which has us use iMocha.
Instead of that, I just created a StringBuilder object (from System.Text) and iterated over the array, appending each integer and a blank space, then converting the result to a string. Something like this:
var array = new int[] {1, 3, 5, 7, 9};
var str = new StringBuilder();
foreach (int i in array)
{
str.Append(i);
str.Append(" ");
}
var result = str.ToString();
Is it possible to access a Model property in an external Javascript file?
e.g. In "somescript.js" file
var currency = '#Model.Currency';
alert(currency);
On my View
<script src="../../Scripts/somescript.js" type="text/javascript">
This doesn't appear to work, however if I put the javascript directly into the view inside script tags then it does work? This means having to put the code in the page all the time instead of loading the external script file like this:
#model MyModel;
<script lang=, type=>
var currency = '#Model.Currency';
alert(currency);
</script>
Is there any way around this?
I tackled this problem using data attributes, along with jQuery. It makes for very readable code, and without the need of partial views or running static javascript through a ViewEngine. The JavaScript file is entirely static and will be cached normally.
Index.cshtml:
#model Namespace.ViewModels.HomeIndexViewModel
<h2>
Index
</h2>
#section scripts
{
<script id="Index.js" src="~/Path/To/Index.js"
data-action-url="#Url.Action("GridData")"
data-relative-url="#Url.Content("~/Content/Images/background.png")"
data-sort-by="#Model.SortBy
data-sort-order="#Model.SortOrder
data-page="#ViewData["Page"]"
data-rows="#ViewData["Rows"]"></script>
}
Index.js:
jQuery(document).ready(function ($) {
// import all the variables from the model
var $vars = $('#Index\\.js').data();
alert($vars.page);
alert($vars.actionUrl); // Note: hyphenated names become camelCased
});
_Layout.cshtml (optional, but good habit):
<body>
<!-- html content here. scripts go to bottom of body -->
#Scripts.Render("~/bundles/js")
#RenderSection("scripts", required: false)
</body>
There is no way to implement MVC / Razor code in JS files.
You should set variable data in your HTML (in the .cshtml files), and this is conceptually OK and does not violate separation of concerns (Server-generated HTML vs. client script code) because if you think about it, these variable values are a server concern.
Take a look at this (partial but nice) workaround: Using Inline C# inside Javascript File in MVC Framework
What you could do is passing the razor tags in as a variable.
In razor File>
var currency = '#Model.Currency';
doAlert(currency);
in JS file >
function doAlert(curr){
alert(curr);
}
Try JavaScriptModel ( http://jsm.codeplex.com ):
Just add the following code to your controller action:
this.AddJavaScriptVariable("Currency", Currency);
Now you can access the variable "Currency" in JavaScript.
If this variable should be available on the hole site, put it in a filter. An example how to use JavaScriptModel from a filter can be found in the documentation.
What i did was create a js object using the Method Invocation pattern, then you can call it from the external js file. As js uses global variables, i encapsulate it to ensure no conflicts from other js libraries.
Example:
In the view
#section scripts{
<script>
var thisPage = {
variableOne: '#Model.One',
someAjaxUrl: function () { return '#Url.Action("ActionName", "ControllerName")'; }
};
</script>
#Scripts.Render("~/Scripts/PathToExternalScriptFile.js")
}
Now inside of the external page you can then get the data with a protected scope to ensure that it does not conflict with other global variables in js.
console.log('VariableOne = ' + thisPage.variableOne);
console.log('Some URL = ' + thisPage.someAjaxUrl());
Also you can wrap it inside of a Module in the external file to even make it more clash proof.
Example:
$(function () {
MyHelperModule.init(thisPage || {});
});
var MyHelperModule = (function () {
var _helperName = 'MyHelperModule';
// default values
var _settings = { debug: false, timeout:10000, intervalRate:60000};
//initialize the module
var _init = function (settings) {
// combine/replace with (thisPage/settings) passed in
_settings = $.extend(_settings, settings);
// will only display if thisPage has a debug var set to true
_write('*** DEBUGGER ENABLED ***');
// do some setup stuff
// Example to set up interval
setInterval(
function () { _someCheck(); }
, _settings.intervalRate
);
return this; // allow for chaining of calls to helper
};
// sends info to console for module
var _write = function (text, always) {
if (always !== undefined && always === true || _settings.debug === true) {
console.log(moment(new Date()).format() + ' ~ ' + _helperName + ': ' + text);
}
};
// makes the request
var _someCheck = function () {
// if needed values are in settings
if (typeof _settings.someAjaxUrl === 'function'
&& _settings.variableOne !== undefined) {
$.ajax({
dataType: 'json'
, url: _settings.someAjaxUrl()
, data: {
varOne: _settings.variableOne
}
, timeout: _settings.timeout
}).done(function (data) {
// do stuff
_write('Done');
}).fail(function (jqxhr, textStatus, error) {
_write('Fail: [' + jqxhr.status + ']', true);
}).always(function () {
_write('Always');
});
} else {// if any of the page settings don't exist
_write('The module settings do not hold all required variables....', true);
}
};
// Public calls
return {
init: _init
};
})();
You could always try RazorJs. It's pretty much solves not being able to use a model in your js files RazorJs
I had the same problem and I did this:
View.
`var model = #Html.Raw(Json.Encode(Model.myModel));
myFunction(model);`
External js.
`function myFunction(model){
//do stuff
}`