Accessing a local js variable in razor block - javascript

I am trying to write razor code inside javascript where I am trying to use a local variable inside the razor code. Here is the sample code:
<script type="text/javascript">
for (i = 0; i < data.result.length; i++) {
$("#member-table tbody").append("<tr>");
var id = data.result[i].MemberId;
var actions = $("<td>" + #Html.ActionLink("Detay", "Edit", new { id }) + "</td>)");
}
</script>
the problem is that id is not recognized by the razor code (i.e. it does not exist in the current context). How can I achieve that ? Is there any way ?

It's not possible to access a javascript variable in a razor block.
That's because razor is executed in the server, and javascript is executed in the browser.
However, by looking at your code it seems like you are using javascript to populate a table and that's bad, there are two patterns for solving this problem, one that solves everything in the server, and another one that solves everything in the browser.
Solving everything in the server:
If you decide that you want to solve everything in the server, your javascript should request the contents from the server and load them into a placeholder without changing them, something like:
$("#myButton").click(function(){
$("#myDinamicDiv").load("/Path/ToView");
});
and then you use razor's foreach loop to generate the table's html:
#foreach (var x in ViewBag.MyData)
{
<tr>
<td>Generate contents here, including links </td>
</tr>
}
Solving everything in the client:
As pointed out in another answer, if you are using the default routing, you can just create direct strings in the javascript code and add them to your page, keep in mind however, that when using this solution, as your page gets complex, your javascript will became less and less maintainable, having a for loop that iterates over data is a sign that maybe you can benefit from javascript UI frameworks like Angular.js and Knockout.js, in fact, what you are doing is the core of Knockout.js's third lesson in its tutorial (Single page applications)

If you're just using default routing, then simply just don't bother with the Razor #Html.ActionLink. Stick with an explicit tag:
var actions = $('<td>Detay</td>');
...obviously with whatever your current controller name is substituted for [your-controller-here].
(And I'm assuming your 'id' isn't necessarily URL-encoded, hence the 'escape'.)

You are mixing server side and client side here. You cannot create #Html.ActioLink using client side variables. Html.ActionLink is rendered on the server, it does not have any clue at all about your client side variables.
If you want to use a client side variable, like "id", render a plain html link (a) tag.

This worked for me once
if ('#ViewBag.DownloadLink' != '') {
window.location.href = '#ViewBag.DownloadLink';
}

Related

Access to Java model List from Javascript/JQuery dynamically

I have a model as List<Collaborateur> coming from my Controller Action to my JSP. And I have the following function in JQuery/Javascript:
function showCollaborateurEmail(index) {
alert("${collaborateurs[1].email}"); // Works
alert("${collaborateurs[2].email}"); // Works
//....
alert("${collaborateurs[index].email}"); // Not working
}
So I want to show the email of my Collaborateur model dynamically by calling the function showCollaborateurEmail(index).
Thank you for your help!
The problem that you face is the exchange of data between code on server side and code on client side. There are multiple ways to achieve your goal. You can...
store the list-elements in a hidden form-field (escaping of delimiter might be a challenge) or
generate javascript - code on server side in JSP that creates a javascript array with the list-elements or
use ajax.
1 and 2 are rather ad hoc - solutions for a small problem. 3 is rather a bigger solution (depending on your technical environment) which additionally provides the possibility to avoid page reloads.
Two approaches to explore:
1) generate a javascript array with all the elements of the list, and access the at will on the client side (javascript)
2) access the "collaborateurs" via an ajax request
I would not recommend to mix server and client code like this through jsp.
Instead you should create a servlet and call what you need with ajax.
If you want to use a dirty/quick trick I think you can build back the array from the server side to the client side inside a <script> tag
For example:
<script>
var emailArray = [
<%for (int i = 0; i > collaborateurs.length; i++){ %>
"<%=collaborateurs[i].email %>"
<%
if(i < collaborateurs.length - 1){
%>, <%
}
}%>
];
</script>
(I wrote this by hand so I'm not sure the syntax is correct, but I hope you got the idea)
One more option you will have is converting your List object to json and then set it to a variable in jsp while compiling phase and the on client side parse json and make an object and iterate over it. This way you can access your list even after loading your page also.
You can use API like gson,Jackson etc.
Example :-
List<Object> list1=new ArrayList<>();
// Add values to list here
JSONArray obj=new JSONArray(list1);
In JSP :
var collaborateursList=${collaborateurs};
function showCollaborateurEmail(index) {
alert(collaborateursList[1].email);
alert(collaborateursList[2].email);
alert("collaborateursList[index].email);
}
Please verify the index is passing correctly to the function and try using this
alert("${collaborateurs[" + index + "].email}");

Transferring javascript from a view to a seperate JS file

I am working on a legacy application and I want to move some JS code onto a separate JS file.
I will have to refractor some of the code to do this. I can put #Url.Content statements into data attributes in the HTML.
But how would I replace this line of code?
var array = #Html.Raw(Json.Encode(ViewBag.JobList));
A separate JS file will not know what #Html.Raw means.
Server side code like that cannot run in a seperate javascript file. My solution for such problems is having a short javascript part in the head that runs on the onload event. There you can set variables that you can use in a seperate javascript file:
in the head:
array = #Html.Raw(Json.Encode(ViewBag.JobList));
in the seperate javascript file:
var array;
Then, in the seperate javascript file you can do with your array whatever is necessary.
The ViewBag.JobList data is only known at HTML page generation time. To include it in an external JavaScript file, you have to have another ASP.NET resource that recalculated ViewBag.JobList and then served as part of a dynamic JavaScript file. This is pretty inefficient.
Instead, do what you're doing with the URLs: pass the data through the DOM. If you're writing into normal DOM instead of a script block, you don't need the raw-output any more (*), normal HTML escaping is fine:
<script
id="do_stuff_script" src="do_stuff.js"
data-array="#Json.Encode(ViewBag.JobList)"
></script>
...
var array = $('#do_stuff_script').data('array');
// jQuery hack - equivalent to JSON.parse($('#do_stuff_script').attr('data-array'));
(Actually, the raw-output might have been a security bug, depending on what JSON encoder you're using and whether it chooses to escape </script to \u003C/script. Writing to HTML, with well-understood HTML-encoding requirements, is a good idea as it avoids problems like this too.)
I think you need to create action with JavaScriptResult
public ActionResult Test()
{
string script = "var textboxvalue=$('#name').val();";
return JavaScript(script);
}
But, before proceeding please go through following links
Beware of ASP.NET MVC JavaScriptResult
Working example for JavaScriptResult in asp.net mvc
I would also follow MelanciaUK's suggestion :
In your javascript file, put your code inside a function :
function MyViewRefactored( array ){
... your code ...
}
In your view, leave a minimal javascript bloc :
<script>
var array = #Html.Raw(Json.Encode(ViewBag.JobList));
MyViewRefactored( array );
</script>

Store very small amount of data with javascript

I have one of those websites that basically gives you a yes or no response to a question posed by the url. An example being http://isnatesilverawitch.com.
My site is more of an in-joke and the answer changes frequently. What I would like to be able to do is store a short one or two word string and be able to change it without editing the source on my site if that is possible using only javascript. I don't want to set up an entire database just to hold a single string.
Is there a way to write to a file without too much trouble, or possibly a web service designed to retrieve and change a single string that I could use to power such a site? I know it's a strange question, but the people in my office will definitely get a kick out of it. I am even considering building a mobile app to manipulate the answer on the fly.
ADDITIONAL:
To be clear I just want to change the value of a single string but I can't just use a random answer. Without being specific, think of it as a site that states if the doctor is IN or OUT, but I don't want it to spit out a random answer, it needs to say IN when he is IN and OUT when he is out. I will change this value manually, but I would like to make the process simple and something I can do on a mobile device. I can't really edit source (nor do I want to) from a phone.
If I understand correctly you want a simple text file that you change a simple string value in and have it appear someplace on your site.
var string = "loading;"
$.get('filename.txt',function(result){
string = result;
// use string
})
Since you don't want to have server-side code or a database, one option is to have javascript retrieve values from a Google Spreadsheet. Tabletop (http://builtbybalance.com/Tabletop/) is one library designed to let you do this. You simply make a public Google Spreadsheet and enable "Publish to web", which gives you a public URL. Here's a simplified version of the code you'd then use on your site:
function init() {
Tabletop.init( { url: your_public_spreadshseet_url,
callback: function (data) {
console.log(data);
},
simpleSheet: true } )
}
Two ideas for you:
1) Using only JavaScript, generate the value randomly (or perhaps based on a schedule, which you can hard code ahead of time once and the script will take care of the changes over time).
2) Using Javascript and a server-side script, you can change the value on the fly.
Use JavaScript to make an AJAX request to a text file that contains the value. Shanimal's answer gives you the code to achieve that.
To change the value on the fly you'll need another server-side script that writes the value to some sort of data store (your text file in this case). I'm not sure what server-side scripting (e.g. PHP, Perl, ASP, Python) runtime you have on your web server, but I could help you out with the code for PHP where you could change the value by pointing to http://yoursite.com/changeValue.php?Probably in a browser. The PHP script would simply write Probably to the text file.
Though javascript solution is possible it is discouraged. PHP is designed to do such things like changing pieces of sites randomly. Assuming you know that, I will jump to javascript solution.
Because you want to store word variation in a text file, you will need to download this file using AJAX or store it in .js file using array or string.
Then you will want to change the words. Using AJAX will make it possible to change the words while page is loaded (so they may, but do not have to, change in front of viewers eyes).
Changing page HTML
Possible way of changing (words are in array):
wordlist.js
var status = "IN"; //Edit IN to OUT whenever you want
index.html
<script src="wordlist.js"></script>
<div>Doctor is <span id="changing">IN</span></div>
<script>
function changeWord(s) { //Change to anything
document.getElementById("changing").innerHTML = s;
}
changeWord(status); //Get the status defined in wordlist.js
</script>
Reloading from server
If you want to change answer dynamically and have the change effect visible on all open pages, you will need AJAX or you will have to make browser reload the word list, as following:
Reloading script
function reloadWords() {
var script = document.createElement("script"); //Create <script>
script.type="text/javascript";
script.src = "wordlist.js"; //Set the path
script.onload = function() {changeWord(status)}; //Change answer after loading
document.getElementsByTagName("head")[0].appendChild(script); //Append to <head> so it loads as script. Can be appended anywhere, but I like to use <head>
}
Using AJAX
Here we assume use of text file. Simplest solution I guess. With AJAX it looks much like this:
http = ActiveXObject==null?(new XMLHttpRequest()):(new ActiveXObject("Microsoft.XMLHTTP"));
http.onloadend = function() {
document.getElementById("changing").innerHTML = this.responseText; //Set the new response, "IN" or "OUT"
}
http.open("GET", "words.txt")
http.send();
Performance of AJAX call may be improved using long-poling. I will not introduce this feature more here, unless someone is interested.

grails gsp javascript integration, how to escape variables?

I have a gsp page (template) where I need to include some javascript. In the example below, how would I get the remoteFunction to understand, the moneyTransId will be set by the javascript function? MoneyTransId comes out fine in the alert, but I can't get it to work in the remoteFunction, and apparently need to escape it somehow.
<script type="text/javascript">
function confirmVoid(moneyTransId) {
var r = confirm("Please confirm the void");
if (r == true) {
alert("ID is: " +moneyTransId);
${remoteFunction(action:"voidTransaction", id:moneyTransId)};
...
Use the following syntax:
${remoteFunction(action:'voidTransaction', params:'\'id=\'+moneyTransId')};
This way, you won't mix server-side code with client-side code.
Hope this helps.
Server side variables and statements can not read client side (javascript) variables.
Server side code runs first, then html and javascript get generated and sent to the client (browser). Then the browser renders HTML and runs javascript. Hope this helps your thought process.
Dmitry.

ASP.NET MVC templates for both client and server

Is this possible? For an example of what I want to achieve, take the Facebook commenting system. Existing comments are rendered on the server, but if I leave a new comment, it is created using AJAX on the client. Ideally, I'd like to store the template for the comment in only one place, and have access to it on both the server (rendered by Razor) and on the client (rendered in Javascript using JSON returned by the server).
Any ideas?
EDIT: I guess another option is to stick with purely server side rendering, and when the user posts a new comment, return the rendered HTML to the browser to be stuffed into the DOM. This isn't quite as nice, but I'd be interested to know if this is possible too.
I would oppose rendering server-side and then sending it back to your JS-script for bandwith and performance. Rather you should use a templating engine that works on both the server and the client. When the client wants to refresh the comments, it requests only the data for the comments and then replaces the old comments html with the new html rendered from the data using the same template that is being used on the server.
I've been using Mustache templating engine to achieve this using PHP and JS. There is a .NET version which I guess works for ASP.NET, and I'm guessing you're using ASP.NET.
So what I do is I make sure I have data formatted in the same way in PHP and JS and then render using a Mustache template.
http://mustache.github.com/
Mustache is simple to use. You take one object and one template and you get the HTML back.
Example object:
object->user = "Kilroy"
object->comment = "was here"
object->edited = true
Example template:
{{user}} {{comment}} {{#edited}}(This comment has been edited){{//edited}}
Result:
Kilroy was here (This commment has been edited)
The approach I've used is having a hidden HTML template with wildcards and/or class names, then on document ready loaded the contents via AJAX/JSON call and finally refreshed or added new items using the same template in javascript.
<ul id="template">
<li>
<span class="message"></span>
<span class="date"></span>
</li>
</ul>
<ul id="comments"></ul>
<script type="text/javascript">
$().ready(function() {
loadComments();
});
function loadComments() {
$.post('#Url.Action("GetComments", "Forum")', {}, function(comments) {
for (i = 0; i < comments.length; i++){
loadComment(comments[i]);
}
}, 'json');
}
function loadComment(comment) {
var template = $('#template li').clone();
template.find('.message').text(comment.message);
template.find('.date').text(comment.date);
$('#comments').append(template);
}
</script>
For new messages, you can post the message to the server and then add it to the list using the loadComment function, or refresh the whole comments list. It's not a complete sample, but hope you get the idea.
I haven't worked with razor or ASP.NET MVC much, but the way I usually approach it using Monorail and NVelocity is this:
Have a template for the page.
For the comments, have a partial template that you include in your main template.
For the AJAX request, use that partial template to render the markup for the comments part. Replace it client side with your preferred method.
This way, will let you have the markup on one place, regardless on if it's a regular request or an ajax request.

Categories

Resources