The Question:
Is there a way that I can pass a JavaScript function into a page (from a database) to be run?
Background info
I have developed a metadata driven asp.net mvc web site (basically I change data in the db, and the pages get generated on the fly).
Unfortunately I have one page that has some routing requirements (i.e. if you choose option 1, then hide item x)
I could solve this by having a custom page, but it would be nice if I could come up with a more robust solution.
So what I am thinking is that I can attach an onchange event handler (based on a metadata value), and then somehow have some code that is executed based on that onchange event.
You can have server-side code render static javascript yes, but you can't change the javascript when on the client due to a change in requirements; you have to render JS code that's dynamic enough to handle all the interactions you may need to do. By server-side code, that means any code as well as any external resources (ie. database). Though, I'm not sure storing static JS code in the DB is buying you anything more than you could already have by storing the JS in a file, and having JS components smart enough to have these different interactions within that file...
So yes server-side storage can render out static JavaScript, but JS can't change from server-side code dynamically, unless your JS code call a web service on the server side.
Make sense?
Its not really the safest thing to do, but using JavaScript's eval() function, you can pass in whatever you want and have it run.
Another idea is to generate a javascript file on the server (whether by getting it from the database or by some other method), and on the page, add a script tag that calls a handler (ashx file) that renders the javascript file.
On the client, you would have this javascript:
var h = document.getElementsByTagName("head")[0];
var js = document.createElement("script");
js.type = "text/javascript";
js.src = "scriptfilegenerator.ashx";
h.appendChild(js);
And on the server (ashx file) you would have this code:
context.Response.AddHeader("content-disposition", "inline; filename=scriptfile.js");
context.Response.ContentType = "text/javascript";
context.Response.Write("your script here");
Related
I have a form in my content page for which I am doing some client side validations via Javascript. The Javascript behaves as expected if I place the JS code directly in the content page. But if I place the JS code in it's own file and try accessing that from the content/master page (through the script tag's src attribute), I get a run time error when the validation function in JS being called.
To be specific, I get the below error.
Microsoft JScript runtime error: Objected expected/required at this line - document.getElementById('<%=txtemailId.ClientID %>').value
txtemailId is in the content page.
Javascript code is placed in validation.js and accessed via master page.
The reason I guess is that when .net is parsing the files, it is unable to substitute txtemailId.ClientID with the client side value that would be generated later on. So, how should one go about it? Thanks!
The answer to this is quite simple.
Within your content page declare an in-line JScript variable, make sure this is above your tag.
<script>
var emailClientId = <%=txtemailId.ClientID%>;
</script>
Within your include.js file make use of the globally scoped emailClientId variable.
Obviously this is a bit clumsy because not all of the JScript code is contained within the include.js file, which makes it difficult to debug / diagnose as it is not clear specifically where the value is coming from. You should clearly comment / document this within your code to make maintenance of he codebase easier in the future.
You're right, the code <%=txtemailId.ClientID %> will only get replaced with the real client ID by ASP.Net if this code is in an ASP.Net file. So if you put this javascript in a separate .js file, ASP.Net will know nothing about it, and the code will not be parsed.
The easiest way to achieve this is to make the control IDs parameters of your functions. This way you can have the calling javascript code in your .aspx (or .ascx) file, along with the <%=txtemailId.ClientID %> code.
Standalone .js files are not run through the ASPX parser, so your expression is not being evaluated.
To solve the problem, you could set global variables (or function parameters, or something else) in the ASPX files with the IDs of the controls, then use them in the standalone .js file.
Alternatively, you could uses class names (which don't get mangled) instead of IDs. (This is very simple using jQuery)
An approach like this is best suited for something you want to achieve:
validation.js
var Validation = {
EmailId: null,
Validate: function() {
var email = document.getElementById(EmailId).value;
}
}
page.aspx
Validation.EmailId = '<%=txtemailId.ClientID %>'; //initialize
Validation.Validate(); //whenever you want to validate
I was wondering if its possible to have an ASP.NET AJAX custom usercontrol 'register' its use of CSS files like it does with JS files. ?
Eg: Implementing the IScriptControl interface allows for the GetScriptReferences() to be called by the Script Manager, is there a similar thing for CSS files?
No.
You could write your own methods to use embedded resources and generate html to write them to the page.
As a best-practice approach, though, I wouldn't recommend this. You should have a clear separation of style and markup. If you want to write a custom control that is dependent on a stylesheet, then I suggest that you provide a default css file along with your control.
EDIT (to answer questions in the comments)
You can run this code to add a reference to the CSS file to your page (at runtime). Note that because you're using the RegisterClientScriptBlock method, you can manage insert duplication. Here is an excerpt from the link for the method:
A client script is uniquely identified by its key and its type. Scripts with the same key and type are considered duplicates. Only one script with a given type and key pair can be registered with the page. Attempting to register a script that is already registered does not create a duplicate of the script.
StringBuilder sb = new StringBuilder();
sb.Append(#"<link rel=""stylesheet"" type=""text/css"" href=""");
sb.Append(this.Page.ClientScript.GetWebResourceUrl(this.GetType(), resourceName));
sb.Append(#""" />");
this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "MyCSS", sb.ToString());
I am trying to make use of the yahoo exceptional performance rule : avoiding duplicate script
To do so i would like to be able to know whether or not a script was already added to the page before injecting it in the page. It looks like i can't figure what has been added in asp.net code behind unless i have a scriptmanager added to the page. but i would like to avoid using asp.net AJAX. From the description of the rule, it looks like it is something possible in php though.
Assuming that i can't do the check in my code behind, i was considering using jQuery $.getString function but it doesn't check before fetching the script. If i was to choose the javascript file, would i have to parse the whole http response in order to figure out which script was loaded on the page?
If the page is registering the scripts with the ASP.NET Page.ClientScript Register APIs then you can use Page.ClientScript.IsClientScriptIncludeRegistered. On the other hand, if you are using those APIs you don't really need to call it, since it already ensures only one of each is registered.
http://msdn.microsoft.com/en/us/library/system.web.ui.clientscriptmanager.isclientscriptincluderegistered.aspx
If the page just has regular ole script elements statically in the markup, and you need to detect if a script is loaded on the client side, you will have to get all the script elements on the page and look at their .src values. The thing with that is that some browsers automatically resolve that url to a full path, not just the one you declared. So, you can account for that in various ways -- you can just search for the end of the string being the script you want, or you can cause the url you want to compare with to also be resolved by setting it onto a dynamically created script element (which you never add to the DOM but is still resolved for you).
This is just off the top of my head, sorry if I get something wrong:
var s = document.createElement("script");
s.src = "foo.js";
var loaded, scripts = document.getElementsByTagName("script");
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src === s.src) {
loaded = true;
break;
}
}
if (loaded) {
// this script is already loaded
// assuming you dont have multiple copies in different locations
}
You don't need to use any client-side scripting to do this... you can do this in your code behind using the ClientScriptManager without needing to make use of ASP.NET AJAX (I think you're confusing ClientScriptManager with ScriptManager*) in your control/page just use:
ClientScript.RegisterClientScriptInclude("some-script", "myScript.js");
or from your user controls:
Page.ClientScript.RegisterClientScriptInclude("some-script", "myScript.js");
This will use the key "some-script" & only register one copy of the script on the page.
*To be clear I think the confusion is arrising from the difference between these:
ClientScriptManager if a server-side helper class which is used to manage client side scripts (in other words its whole purpose is to do exactly what you are trying to do). It is accessed via the Page's ClientScript property.
ScriptManager is a Control used to aid client side Ajax scripting in ASP.NET AJAX
(hell I even confused myself & gave the wrong example code initially)
well that wouldn't actually work in a master detail scenario with multiple web user controls.
Then you wouldn't have control over who has to do the script initialization if the web user control is dynamic.
It's easier to link once, but a developer would have to weigh his options between ClientManager and using a script load.
yeah you have to parse the whole response...
why don't you create a javascript file and put all of your javascript there and then import that javascript file in your code??? in this way you can get rid of duplicate script insertion.
I have a bunch of javascript functions which depend on some server-side constants (such as strings from my resources file). While I was developing them I kept the javascript code in the view header, so I could simply used server-side tags inside my javascript code, but now I'd like to move the javascript functions to a separate file.
I can't use a regular js file since that would not be interpreted by the server, making the server tags embedded there useless. I don't want to define variables in the page either since that seems way to awkward and error-prone.
What I've done instead is to create a new aspx file, put the javascript functions there and include that aspx file instead of a regular js file in my master template. It seems a bit unorthodox, but it seems to work fine.
Is there any downside to my approach that I have not taken into account? Or any better (less obscure) method?
Edit Bonus question: Should I use a DOCTYPE inside the included scripts file? After all, the scripts file is included by a script tag already. I tried to mimic regular js files, so I did not specify any DOCTYPE.
The file extension, be it js, aspx, ashx, bla, foo, whatever isn't all that important. If you have server side generated javascript that isn't specific to a page, then creating an ASPX page to render the javascript should be okay.
We'll often use HTTP handlers to generate dynamic javvascript in our systems. We also make sure to set the response headers to text/javascript to let the client browser know that we are sending back javascript.
Using a view, with MVC's templating capabilities is a great way to accomplish this. It is easy to maintain, well understood and gets the job done fast. The only real trick is to get it to serve the correct content-type. Doing something like this:
public ActionResult ConfigurationSettings()
{
Response.ContentType = "text/javascript";
return View(Configuration);
}
Actually gets you text/html content type. The trick is to make sure you get the last word, add this to your controller:
protected override void Execute(System.Web.Routing.RequestContext requestContext)
{
base.Execute(requestContext);
requestContext.HttpContext.Response.ContentType = "text/javascript";
}
And you will get the correct content type; ViewResult seems to force it to go text/html.
We use the ScriptDataBlock control from JsonFx to emit variables into the page itself. It acts like a dictionary where the key is the variable name (e.g. "MyNamespace.myVar") and the value is a normal C# value (including whole objects). The control emits appropriate namespace generation and type conversion to native JavaScript types. So in that sense it ends up not being "awkward" or "error prone":
myDataBlock["MyApp.myVar"] = myObject;
If you are doing an external file, then a generic .ashx handler is probably your best bet. It will be lighter than a whole aspx page and gives you pretty raw control over the output. Things you will want to be sure to do are to set the "Content-Type" response header to "text/javascript" (technically incorrect but most common MIME type) and for Internet Explorer which tends to not respect the Content-Type header, also set the "Content-Disposition" header to "inline;MyScriptName.js" so it knows the extension of the content.
Also, if these really are "constants" rather than runtime calculated data, you will want to set the "Expires" header to some future date to encourage the browser to cache the result. That will further reduce your server requests.
Edit: no DocType if you are creating JavaScript. DocType is only for markup.
You are using the MVC framework (your question is tagged as such) right? If so, you can create an action that returns a JavaScriptResult that will be executed on page when it loads:
public class JSController : Controller {
public ActionResult Headers() {
// create your variables here
return JavaScript("alert('hi');");
}
}
And then you can add it to your aspx/master page:
<script src="/JS/Headers" type="text/javascript"></script>
From time to time I run into situation in which I could solve a given problem by dynamicly creating piece of JavaScript, spesific to that instance of the page, that I could then insert in the final markup. Usualy this is because I want some behaviour to happen on the client side, rather than on the server side and creating a static JavaScript isn't an option.
For example, when trying to submit a files original path without submitting the file itself, when trying to do it for multiple dynamicly created components.
How would you recommend me to create a script tag and populate it with, for example, JavaScript?
Use your page's ClientScriptManager and it's RegisterClientScriptBlock() method.
string javascript = "javascript goes here";
string scriptname = "Name of this script"; // used to prevent adding the same script twice at two places in the page life cycle
if (!Page.ClientScript.IsClientScriptBlockRegistered(scriptname))
{
Page.ClientScript.RegisterClientScriptBlock(this.Page.GetType(), scriptname, javascript, true);
}