I'm pretty confused with how to mix razor and js. This is the current function I am stuck with:
<script type="text/javascript">
var data = [];
#foreach (var r in Model.rows)
{
data.push([ #r.UnixTime * 1000, #r.Value ]);
}
If I could declare c# code with <c#></c#> and everything else was JS code -- this would be what I am after:
<script type="text/javascript">
var data = [];
<c#>#foreach (var r in Model.rows) {</c#>
data.push([ <c#>#r.UnixTime</c#> * 1000, <c#>#r.Value</c#> ]);
<c#>}</c#>
What is the best method to achieve this?
Use <text>:
<script type="text/javascript">
var data = [];
#foreach (var r in Model.rows)
{
<text>
data.push([ #r.UnixTime * 1000, #r.Value ]);
</text>
}
</script>
Inside a code block (eg, #foreach), you need to mark the markup (or, in this case, Javascript) with #: or the <text> tag.
Inside the markup contexts, you need to surround code with code blocks (#{ ... } or #if, ...)
you also can simply use
<script type="text/javascript">
var data = [];
#foreach (var r in Model.rows)
{
#:data.push([ #r.UnixTime * 1000, #r.Value ]);
}
</script>
note #:
Never ever mix more languages.
<script type="text/javascript">
var data = #Json.Encode(Model); // !!!! export data !!!!
for(var prop in data){
console.log( prop + " "+ data[prop]);
}
In case of problem you can also try
#Html.Raw(Json.Encode(Model));
A non conventional method to separate javascript from the view, but still use razor in it is to make a Scripts.cshtml file and place your mixed javascript/razor there.
Index.cshtml
<div id="Result">
</div>
<button id="btnLoad">Click me</button>
#section scripts
{
#Html.Partial("Scripts")
}
Scripts.cshtml
<script type="text/javascript">
var url = "#Url.Action("Index", "Home")";
$(document).ready(function() {
$("#btnLoad").click(function() {
$.ajax({
type: "POST",
url: url ,
data: {someParameter: "some value"},
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(msg) {
$("#Result").text(msg.d);
}
});
});
});
</script>
This is my way to hack Razor and use Javascript without freaking out Intellisense.
Obviously you should use <text> but with an "expedient": double braces before.
This is what happens if a single brace is used:
This is what happen if a couple of braces are used:
Now constants and variable have been recognized.
It's better, but not good!
There is still the "less than" symbol that confuses intellisense making it think that following is the name of a tag. The "less than" sign is signaled as error and from here there are parsing errors as "console.log" shows.
You need to add something that quiets intellisense and doesn't get returned in the final JS code at compile time.
Solution: use this comment after "less than": /*>*/
This is the result
It's quite strange to see, but it works as it should.
you can use the <text> tag for both cshtml code with javascript
Wrap your Razor code in #{ } when inside JS script and be aware of using just #
Sometimes it doesn't work:
function hideSurveyReminder() {
#Session["_isSurveyPassed"] = true;
}
This will produce
function hideSurveyReminder() {
False = true;
}
in browser =(
Related
Using jQuery, I am dynamically adding a group of <ul> elements to a <div> using .append. I loop through a table containing a value for each <ul> I want added. That part works fine. Where I am having an issue is trying to add Razor functionality to the <li> elements.
Although the end result HTML produced looks to be syntax correct HTML, the Razor code does not function.
End Result HTML:
<div id="div_1">
<label>Clients</label>
<ul>
#if (Model.details != null)
{
foreach (var itm in Model.details.OrderBy(i => i.value))
{
<li>#itm.value</li>}
}
</ul>
</div>
The HTML above is what is produced by my .append code. It can be hard coded into my cshtml page and works perfectly. Adding it via .append at run time does not which I assume revolves around creation time or sequence?
Scaled down version of my code below:
Controller:
[HttpGet]
public JsonResult ClientMap()
{
var db = new EDM_Client();
List<client_map> map = new List<client_map>();
map = db.client_map.ToList();
return Json(map, JsonRequestBehavior.AllowGet);
}
csHTML:
<div id="divDetails" class="row">
</div>
jQuery:
//getClientMap temporarily called from a button click
getClientMap(function (map) {
$.each(map, function (idx, obj) {
$('#divDetails').append('<div id="div_' + obj.detail_id + '">' +
'<label>' + obj.detail_hdr + '</label>' +
'<ul>' +
'#if (Model.details != null)' +
'{' +
'foreach (var itm in Model.details.OrderBy(i => i.value))' +
'{' +
'<li>' +
'#itm.value' +
'</li>' +
'}' +
'}' +
'</ul>' +
'</div>');
});
});
function getClientMap(output) {
$.ajax({
url: './Client/ClientMap',
type: "GET",
dataType: "json",
async: false,
success: function (data) {
output(data);
}
});
I am trying to do this dynamically so <ul> can be easily added/deleted via table entries rather than coding changes.
Any advice on how to make this work (or an alternative way) is appreciated.
jQuery runs on the client's machine, so when you use it to generate Razor code, the client machine cannot run that code because it has to run on the server.
You can put the Razor code directly where it should be, and then use jQuery to show/hide the options as required.
Alternatively, change your Ajax method ClientMap to return a partial view instead of JSON string, then use the jQuery's html() function to add the HTML returned from Ajax:
$('#divDetails').html(data);
Is it possible to set a javascript variable from a c# controller? We have a situation where we override our master page with a dumb downed version that doesn't require login for users. However, our javascript timeout timer still runs. I would like to in the controller method that overrides the master, to override the timeout to something huge.
public dumbDownController()
{
ViewData["MasterPageOverride"] = "~/Views/Shared/_NoLogin.cshtml";
//Somehow reset that timer below from 20 to like 9999. To simulate no timeout.
return View("Cities", model);
}
Then our javascript file has.
tb.sessionTimer = (function () {
var SESSION_TIMEOUT = 20;
var currentTimeoutCounter = SESSION_TIMEOUT * 60;
...
lots more irrelevant to question
...
}
Large app, so looking to barely change the javascript. Would like to handle it from the controller.
Short Answer:
<script>
var xyz = #ViewData["MasterPageOverride"];
</script>
Longer Answer:
You can't directly assign JavaScript variables in the Controller, but in the View you can output JavaScript which can do the same.
You need to pass the variable to the View somehow. For this example I'll use the ViewData object dictionary. You can set an element in the Controller:
public ActionResult SomeAction()
{
ViewData["aNumber"] = 24;
}
Then in the View it is possible to use it as:
<script>
var xyz = #ViewData["aNumber"];
</script>
Which will be sent to the client browser as:
<script>
var xyz = 24;
</script>
Strings need a bit more attention, since you need to enclose them between '' or "":
<script>
var xyz = "#ViewData["aString"]";
</script>
And as #Graham mentioned in the comments, if the string happens to be valid JSON (or any object literal, but it is very easy to create JSON), and you want to use it as a JavaScript object, you can:
<script>
var xyz = #ViewData["aJsonString"];
</script>
Just make sure the output is valid JavaScript.
On a footnote, be careful with special HTML characters, like < as they are automatically HTML-encoded to prevent XSS attacks, and since you are outputting JavaScript not HTML, this can mess things up. To prevent this, you can use Html.Raw() like:
<script>
var xyz = #Html.Raw(ViewData["aJsonString"]);
</script>
If tb is within the scope of your controller, consider just overriding the sessionTimer function:
public dumbDownController(){
...
tb.sessionTimer = function(){return false;}
}
You have two options (as I understand):
Create the variable from viewbag, data, tempdata, like so:
var SESSION_TIMEOUT = #ViewData["MasterPageOverride"];
var SESSION_TIMEOUT = #ViewBag["MasterPageOverride"];
var SESSION_TIMEOUT = #TempData["MasterPageOverride"];
Or, do it via jQuery AJAX:
$.ajax({
url: '/YourController/YourAction',
type: 'post',
data: { id: id },
dataType: 'json',
success: function (result) {
// setVar
}
});
I have searched SO and not found a solution to this problem. I have code like this:
<script>
$("AddToFavorites").Click(function() {
var apiLink = "/url/AddToFavorites?id=" + #Model.RecipeId;
$.ajax({
url: apiLink,
type: "GET"
});
});
</script>
the . in Model.RecipeId gets underlined and I get a compiler error of:
Conditional compilation is turned off
Is it not possible to use c# code in a javscript block? If this is the case, how do I get around it to make the url dynamic in a case like this?
Thanks.
Try changing the code to
<script type="text/javascript">
$("AddToFavorites").click(function() {
var apiLink = "/url/AddToFavorites?id=" + "#(Model.MerchantID)";
$.ajax({
url: apiLink,
type: "GET"
});
});
</script>
also you may want to add
/*#cc_on #*/
if the error continues to happen.
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
}`
I have implemented an autocomplete in my app for zip codes. I am debugging in Firebug and I see in my console that the action is performing and I get a list of zip codes in the list of results, but the actual list is not displaying when I debug.
Here's the action in my Customers controller:
//the autocomplete request sends a parameter 'term' that contains the filter
public ActionResult FindZipCode(string term)
{
string[] zipCodes = customerRepository.FindFilteredZipCodes(term);
//return raw text, one result on each line
return Content(string.Join("\n", zipCodes));
}
Here's the markup (abbreviated)
<% using (Html.BeginForm("Create", "Customers")) {%>
<input type="text" value="" name="ZipCodeID" id="ZipCodeID" />
<% } %>
and here's the order I load my scripts:
<script type="text/javascript" src="/Scripts/jquery-1.4.2.js"></script>
<script type="text/javascript" src="/Scripts/jquery.ui.core.js"></script>
<script type="text/javascript" src="/Scripts/jquery.ui.widget.js"></script>
<script type="text/javascript" src="/Scripts/jquery.ui.position.js"></script>
<script type="text/javascript" src="/Scripts/jquery.ui.autocomplete.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#ZipCodeID").autocomplete({ source: '<%= Url.Action("FindZipCode", "Customers") %>'});
});
</script>
Anything obvious that I'm missing? Like I say the script is grabbing the list of zip codes, they just won't display on my page when I test.
EDIT: I added an image that shows what I see in firebug - it appears that I get my zip codes back, but just won't display the dropdown.
I also updated my text box so that it's inside of the ui-widget div like so:
<div class="ui-widget">
<input type="text" name="ZipCodeID" id="ZipCodeID" />
</div>
and this is the script that I'm using:
<script type="text/javascript">
$(document).ready(function() {
$("#ZipCodeID").autocomplete('<%= Url.Action("FindZipCode", "Customers") %>');
});
</script>
I was able to get the autocomplete suggestions working using the following code:
Controller:
public JsonResult FindZipCode(string term)
{
VetClinicDataContext db = new VetClinicDataContext();
var zipCodes = from c in db.ZipCodes
where c.ZipCodeNum.ToString().StartsWith(term)
select new { value = c.ZipCodeID, label = c.ZipCodeNum};
return this.Json(zipCodes, JsonRequestBehavior.AllowGet);
}
Markup:
<script type="text/javascript">
$(document).ready(function() {
$("#ZipCodeID").autocomplete({
source: '<%= Url.Action("FindZipCode", "Customers") %>',
});
});
</script>
<div class="ui-widget"><input type="text" name="ZipCodeID" id="ZipCodeID" /></div>
I had huge problems with autocomplete few months ago when first setting it up. For instance, the simple default wireup like you do it never worked for me. I had to specify everything and also attach the result function to it.
This works 100% but it might not be suitable for you. But I hope it helps. Put both in document.ready() function.
$("#products").autocomplete('<%:Url.Action("GetProducts", "Product") %>', {
dataType: 'json',
parse: function (data) {
var rows = new Array(data.length), j;
for (j = 0; j < data.length; j++) {
rows[j] = { data: data[j], value: data[j].Title, result: data[j].Title };
}
return rows;
},
formatItem: function (row, y, n) {
return row.PrettyId + ' - ' + row.Title + ' (' + row.Price + ' €)';
},
width: 820,
minChars: 0,
max: 0,
delay: 50,
cacheLength: 10,
selectFirst: true,
selectOnly: true,
mustMatch: true,
resultsClass: "autocompleteResults"
});
$("#products").result(function (event, data, formatted) {
if (data) {
var item = $("#item_" + data.PrettyId),
edititem = $("#edititem_" + data.PrettyId),
currentQuantity;
// etc...
}
});
Try returning JSON from your controller action:
public ActionResult FindZipCode(string term)
{
string[] zipCodes = customerRepository.FindFilteredZipCodes(term);
return Json(new { suggestions = zipCodes }, JsonRequestBehavior.AllowGet);
}
Also don't forget to include the default CSS or you might not see the suggestions div appear.