Dynamically writing Javascript code? - javascript

I am creating dynamic sites with the same code base, where I will need to display the appropriate Google ads Javascript code, based on some logic.
In my .Net 4, MVC3 environment, I have setup the following scenario:
Navigating to www.mysite.com/script_processor/ returns the following text:
function doAlert(v1, v2){alert('Called alert()' + v1 + ' : ' + v2);}
This text come out of my model as such:
page.stringResponse = "function doAlert(v1, v2){alert('Called alert()' + v1+ ' : ' + v2);}";
I can then do something like this from a separate site/page:
<script type="text/javascript" src="http://mysite.com/script_processor/">
</script>
<script type="text/javascript">
doAlert('string 1', 'string 2');
</script>
As you would expect, I get an alert box with "Called alert() string 1 : string 2", so the function on mysite.com is accessible from site 2.
Obviously when I do a view source form the page, I only see the doAlert() call, and not the content of the function that sits on mysite.com.
Now, instead of doAlert() on mysite.com, I want to have a function that dynamically writes out javascript that can will be seen on site 2 when it's called.
I created a model method:
public GetResponsePage GetPageResponse(string V1, string V2)
{
var page = new GetResponsePage();
page.stringResponse = "<script type=\"text/javascript\">document.write('testing injected Javascript.');alert('value of v1: " + V1 + "value of v2: " + V2 + "');</script>";
return page;
}
When navigating to the route, I see the popup, and the "testing injected Javascript." on the page.
When I reference this from site 2, I don't see the popup, nor do I see "testing injected Javascript" in the page source.
As I mentioned I will later replace this with the JS code for the appropriate Google Ads js code.
I don't think this is working quite right... what am I missing?
Thanks.

You can use the ever evil eval to dynamically execute JavaScript. The JavaScript you pass in can also declare functions.
eval("function doAlert(v1, v2){alert('Called alert()' + v1 + ' : ' + v2);}");
I'm not sure exactly what you're trying to accomplish, but you could put an eval call like this wherever you want:
function createDoAlertFunction(){
eval("function doAlert(v1, v2){alert('Called alert()' + v1 + ' : ' + v2);}");
}
Just note though, that this should be avoided. Stick to declaring your functions the old fashioned way, like you already are.
EDIT
Oh, you want MVC to to inject JavaScript dynamically. MVC has a JavaScriptResult, but it looks like its use is strongly, strongly discouraged.
Nonetheless, here's a link that shows its use
public ActionResult DoSomething() {
string s = "$('#some-div').html('Updated!');";
return JavaScript(s);
}
and why it's not a good idea

I know it is very late. But there is very easy way to send JavaScript from controller.
#Html.Raw("<script>" + #ViewBag.DynamicScripts + "</script>")
In controller send JavaScript as follows:
ViewBag.DynamicScripts = "alert('test');";

Related

Pass string variable in C# to javascript function for use in alert box

so what I'm trying to do is pass a simple string variable that contains my error message in C# into my javascript function when I call the function. My function call works fine, but it keeps outputting the wrong thing. This might be important too, I'm calling the Response.Write pieces within my Global.asax.cs file and my javascript file is within my Scripts folder in my MVC project. Based on the research I've found, this is what I currently have after help from the comments:
function KickOutUnwantedUsers(aMesssage) {
console.log("Inside KickOutUnwantedUsers"); //for debugging
console.log(aMesssage); //for debugging
alert(aMessage);
window.open('', '_self').close();
}
It just continues to output this
<%=errorMessage%>
I'm not sure how to fix it, as everything I've found says to pass the variable that way, but I'm wondering if I need to pass it as an actual parameter into the function, and if so, how to do that.
UPDATE: Here is the C# code:
else
{
string user = s.Remove(0, 4);
string errorMessage = "THE FOLLOWING ERRORS MIGHT HAVE OCCURRED: " + user +
" has either been disabled or the account does not exist." +
" PLEASE CONTACT YOUR ADMINISTRATOR. ";
Response.Write("<script type=\"text/javascript\" src=\"Scripts/HelpfulFunctions.js\">");
Response.Write("</script>");
Response.Write("<script type=\"text/javascript\">");
Response.Write("KickOutUnwantedUsers('" + errorMessage + "');");
Response.Write("</script>");
}
SOLVED
#Igor was very helpful in the comments, and I did things as he suggested, which for some reason would not work at first, but then the following day I deletedmy JavaScript file, remade it under the same name and retyped out the javascript code and it worked. Coding is strange sometimes. I must've had a typo or something.
This is what the (badly documented) HttpUtility.JavaScriptStringEncode is for:
// the second argument "true" causes quotation marks to be inserted as well
var message = <%= HttpUtility.JavaScriptStringEncode(errorMessage, true) %>;
First. Such line as
var message = '<%=errorMessage%>';
only makes sense in ASP.NET markup file. In js file it is meaningless, since javascript files are not processed by ASP.NET server-side.
Second. Since you are passing message string as parameter, you need function signature to reflect this:
function KickOutUnwantedUsers(aMessage) {
alert(aMessage);
window.open('', '_self').close();
}
and - note the quotes around the parameter value:
Response.Write("KickOutUnwantedUsers('" + errorMessage + "');");

Can I reference a Node JS function from JS within a <script> tag in a Jade template

I am building a blog with Node.js, Express and MongoDB. In my "create new post" template I have a title field and a slug field.
I am using the Slugs for Node.js from NPM: https://npmjs.org/package/slugs
What I am trying to do is:
dynamically take the value from the title text input using EventListener,
filter it through the slugs() function, and
add it to the value attribute of the slug text input.
So I would type "My favorite character is &" in the title field and the value in the alias field would dynamically change to "my-favorite-character-is".
I believe you have to do something like the accepted answer in this question:
JADE + EXPRESS: Iterating over object in inline JS code (client-side)?
However, that was more for referencing variables rather than executing functions. And it seems like that is preprocessed and then you can't access it anymore.
Is what I'm trying to do even possible?
Or should I go with something like this?
https://github.com/stipsan/String.Slugify.js
Here's what I tried to no avail:
!= "<script>"
!= "var post_title = document.getElementById('title');"
!= "var post_alias = document.getElementById('alias');"
!=
!= "var aliasValidator = function() {"
!= " this.value = " + slug( + "this.value" ) + ";"
!= "};"
!= "var titleValidator = function() {"
!= " post_alias.value = " + slug( + "this.value" ) + ";"
!= "};"
!=
!= "post_title.addEventListener({'keyup': titleValidator, 'keydown': titleValidator, 'change': titleValidator});"
!= "post_alias.addEventListener({'change': aliasValidator});"
!= "</script>"
And here's the view where the variable is passed:
var slugs = require('slugs');
newPost: function(req, res) {
return res.render('add-post', {
title: "Write new post",
slug: slugs,
dayDateName: tools.dayName
});
}
This should be pieced together with a few things:
Get yourself a sluggify function you can load into the browser as well as node. There are trivial ways to do this like copy/pasting the code into a public/slugs.js file, as well as more sophisticated things like browserify or RequireJS. Start simple, but to directly answer your question, no, you can't just magically call a function that exists within your node process from the browser directly but YES you can share the same function in both node and the browser using one of the aforementioned techniques.
In the browser, use events to take the title as it is entered, transform it, and populate the slug field. This is all browser-side JS and has nothing to do with template rendering or node
In your node code to handle the form submission, you'll also need to make sure the slug is valid be running it through the sluggify function again (here you can just use the npm module).
For jade, you don't need all those crazy != prefixes, just do this (this has no direct bearing on your question, just FYI).
script
var put = "your raw javascript here";
var jade = "knows what to do and you don't need prefixes or escaping";
The way you are writing your template, you seem to be implying the function will be executed on the client.
This is not possible.
Instead, you should use an ajax call back to your server to execute the slug function, or precompute all the values and send this to the client.

Grails - render Map from controller via Ajax using JSON

I know this is a recurrent/classical topic but I did not found anything that helped me so far.
I am trying to render a Map from my controller. This results from an Ajax request and is supposed to be "eaten" by a Javascript function 'onSuccess'.
Here is my Javascript and .gsp view:
<g:javascript>
function updateVideoLoad(e) {
var map = eval("("+e.responseText+")") // evaluate the JSON
$('#resultsChecker').html(map.urlAccepted + ' - ' + map.provider + ' - ' + map.videoId + ' - ' + map.videoTag)
}
</g:javascript>
<g:formRemote name="myForm" update="" url="[controller: 'project', action:'addVideo']" onSuccess="updateVideoLoad(e)">
...
</g:formRemote>
Here is my controller:
import grails.converters.JSON
class ProjectController {
def addVideo() {
...
def videoMap = [urlAccepted: videoList[0], provider: videoList[1], videoId: videoList[2], videoTag: videoList[3]]
render videoMap as JSON
}
It looks to me exactly as the example provided in the Grails documentation.
However it does not work. On the browser console, I get:
Uncaught ReferenceError: e is not defined
from my g:remoteForm.
Any suggestion is most welcome.
Thank you for your help.
I'm guessing here, but it looks like a mistake in the documentation. I would expect that this part:
onSuccess="updateVideoLoad(e)"
^^^
Should really be:
onSuccess="updateVideoLoad(data,textStatus)"
^^^^^^^^^^^^^^^^^
Looking at the generated code (under Grails 2.0), these are the variables used in the returned:
<form onsubmit="jQuery.ajax({type:'POST',data:jQuery(this).serialize(), url:'/project/addVideo',success:function(data,textStatus){updateVideoLoad(e);},error:function(XMLHttpRequest,textStatus,errorThrown){}});return false"
method="post" action="/project/addVideo" id="myForm">
Look at the success property in the generated code.
This seems to be working:
<g:formRemote name="myForm" update="" url="[controller: 'project', action:'addVideo']" onComplete="updateVideoLoad(XMLHttpRequest)">
Is there a place where to get documentation on those javascript events (onComplete, onSuccess, ...) ?
This appears to be a documentation bug. If using JQuery (the default as of Grails 2.0) The variable should be 'data' not 'e' and it will contain your JSON object that you returned from your controller, and there is no need to eval or parse it like in the docs.
I had the same problem, but I'm using Dojo.
If you use dojo, you shoud use the variable name 'response':
onComplete="updateVideoLoad(response)"
It works fine for Dojo.

using jstl in Javascript

in my jsp page I have:
<form:select path="index" id="sIndex" onchange="showDetails()">
<form:options items="${smth}" itemLabel="name" itemValue="index"/>
</form:select>
And in my javascript function:
*function showDetails() {
var sIndex=document.getElementById("sIndex");
var index=sIndex[sIndex.selectedIndex].value;
var name = '${smth[index].name}';
var address = '${smth[index].address}';
var message = "<table><tr><td>Name:</td><td>" + name + "</td></tr>";
message = message + "<tr><td>Address:</td><td>" + address + "</td></tr>"
message = message + "</table>"
document.getElementById("candDetails").innerHTML = message;
}*
And it doesn't takes the index in ${}, but if I use alert(index) it recognize it.
Java/JSP/JSTL runs at the server side, produces HTML/CSS/JS output and sends it to the client. HTML/CSS/JS runs at the client side, not at the server side as you apparently expected. Open the page in your browser and do a 'view source'. Do you see it?
Javascript only sees the HTML DOM tree in the client side and can access it. You need to get the name and address from the HTML DOM tree. You already have the name in the option element, but the address is nowhere available. You could use JSTL to generate a Javascript array variable so that the Javascript code can use it further.
To learn more about the wall between Java/JSP and Javascript you may find this article useful.
The EL expressions (the code between ${}) are evaluated at runtime of the JSP servlet, not once the page has been rendered in the browser, which is when your JavaScript is being called.
View the generated source of the page and you will probably see the problem.

Trying to use jQuery to display JSON text data

I know very little (none) JavaScript, or much about using API's. However I would like to display some hotel reviews on my webiste made available over the qype.com API. However I'm struggling with being able to manage this.
This is the code I have so far:
$(document).ready( function() {
$.getJSON( "http://api.yelp.com/business_review_search?term=hilton%20metropole&location=B26%203QJ&ywsid=APIKEY Removed",
function(data) {
$.each( data.businesses, function(i,businesses) {
content = '<p>' + businesses.reviews.text_excerpt + '</p>';
content = '<p>' + businesses.reviews.date + '</p>';
$(content).appendTo("#review");
} );
}
);
} );
I have a div in the body called review where I want to display the text.
Any advice greatly received.
JSON can be found at http://api.yelp.com/business_review_search?term=hilton%20metropole&location=B26%203QJ&ywsid=lOoGGbkYpVmTvxHlWGT2Lw
Also, I have multiple businesses on the same page, how would I make use of this multiple times on the same page, but output the data in different locations?
Update: Ah, I see your error now. businesses.reviews is an array (each business can have more than one review) so you have to loop over each business and each business' reviews.
i had to change some things to get it to run in my test bed, but you can see a sample of this code running here: http://bit.ly/4mTxPp
yelp currently support JSONP calls so you can change your code to:
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
function showData(data) {
$.each(data.businesses, function(i,business){
// extra loop
$.each(business.reviews, function(i,review){
var content = '<p>' + review.text_excerpt + '</p>';
content += '<p>' +review.date + '</p>';
$(content).appendTo('#review');
});
});
}
$(document).ready(function(){
// note the use of the "callback" parameter
writeScriptTag( "http://api.yelp.com/business_review_search?"+
"term=hilton%20metropole"+
"&location=B26%203QJ"+
"&ywsid=lOoGGbkYpVmTvxHlWGT2Lw"+
"&callback=showData"); // <- callback
});
function writeScriptTag(path) {
var fileref = document.createElement('script');
fileref.setAttribute("type","text/javascript");
fileref.setAttribute("src", path);
document.body.appendChild(fileref);
}
</script>
Do you get an error in Firebug using this code? What happens exactly?
I expect this problem is caused by the fact that you're trying to do a cross-domain request which is not allowed. Normally you'll want to do this kind of data gathering on your back-end, but you can use an alternative such as JSONP to do the same.
Take a look at jQuery's documentation on this stuff and it should be clear what's going on.
Also, as a side note: In your callback you have content = which is ok but not ideal. Assigning to content like this will create a variable in the global scope which you do not want. In this case it probably wont cause an issue but say you have 2 of these requests going at once, the second assignment could easily step on the first causing hard-to-debug weirdness. Best to just always create variables with var.
If data.businesses is an array, I would assume that data.businesses[x].reviews is also an array. This code loops the businesses as well as the reviews for each business. It also gets rid of the content variable by appending straight to the #review div.
$.each(data.businesses, function(i,business){
$.each(business.reveiws, function(r,review){
$("#review").append(
'<p>' + review.text_excerpt + '</p>'
).append(
'<p>' + review.date + '</p>'
);
});
});
I think you can specify JSONP with your $.getJSON method by adding "callback=?" to the url parameters.
As of jQuery 1.2, you can load JSON
data located on another domain if you
specify a JSONP callback, which can be
done like so: "myurl?callback=?"
$.getJSON("http://api.yelp.com/business_review_search?term=hilton%20metropole&location=B26%203QJ&ywsid=APIKEY Removed&callback=?",
function(data){
...
});
The problem is that you are making a cross domain request, which is not allowed for security purposes. Either you will have to make a proxy script on your domain (like for example in php) and call yelp from that or fetch the data completely on the server side.
I assume you must be experiencing part of your data (which you are supposed to see) disappearing. I think you must edit your code to:
content = '<p>' + businesses.reviews.text_excerpt + '</p>';
content += '<p>' + businesses.reviews.date + '</p>';
Hope this helps...

Categories