I have an ASP.Net control that injects a javascript pageLoad function. I also have a pageLoad function directly on the page. I can't seem to get them both to fire. Are you limited to only a single function with the same name in javascript?
Yes... Like most languages, JavaScript requires symbols to be unique within their scope. In JavaScript, if you create multiple definitions for a function within a given scope, then the last one to be defined "wins" - it will be as though the previous definitions never existed.
What you need to do, in order to make your redundant pageLoad function work, is to use the Sys.Application.add_load() method. Using it, you can attach as many handlers as you want to the page load event. What's more, you can use anonymous function to add in the add_load method. Doing this will guarantee you that there is no danger of to handlers with duplicate names. Example:
StringBuilder sb = new StringBuilder();
sb.Append("Sys.Application.add_load(");
sb.Append("function() { alert('page load'); });");
ClientScript.RegisterStartupScript(this.GetType(), "Page_Load", sb.ToString(), true);
You can as easily use the Sys.Application.add_load on the client side, you can even add the same handler more than once. This will result in firing the same function multiple times :)
I have to disagree with Shog9...
I know it's possible to have multiple $(document).ready(f(n)) calls in a single document, with each being called in turn when the DOM finishes loading. I don't believe that multiple calls to ClientScript.RegisterStartupScript() cause any issues either.
Related
I have a custom user control (ascx) that contains a textbox and a Javascript-based counter to let the user know many characters they have left to type. In this control is the following:
function GetTextBox() {
return document.getElementById("<%=txNotes.ClientID %>");
}
This worked fine when we only had one instance of this user control on the page, but now we have to support multiple. As you know, having multiple instances of this control on a page will result in multiple GetTextBox() functions, only the last of which will be called no matter what. To support multiple instances, I use this:
if (!string.IsNullOrEmpty(TextBoxName) && !Page.ClientScript.IsClientScriptBlockRegistered(TextBoxName))
{
string Script = string.Format("function Get{0}Notes() {{ return document.getElementById(\"{1}\"); }}",
TextBoxName, txNotes.ClientID);
Page.ClientScript.RegisterClientScriptBlock(GetType(), TextBoxName, Script, true);
}
TextBoxName is a public usercontrol property, so if the developer passes Employee through, it will generate a Javascript function called GetEmployeeNotes(). This works greate because now we can have a unique GetNotes() function.
However, I don't like how it's hardcoded into the codebehind. I would like a markup-based solution for this, something that doesn't require a rebuild of the project in case I want to change the Javascript. Does anyone know of a way to do this?
Edit: I've already thought of creating a separate .js file that I could read with a text reader, but that sounds a bit hacky and I'd like to avoid that if at all possible.
Edit 2: Guard's answer below would work, but I don't want to go that route for the reason I gave beneath his answer. If no one can offer another way to do what I want to do, I will most likely mark his as the answer since it technically does exactly what I am asking.
I'm not a .NET specialist, but isn't it working as a preprocessor?
Isn't it legal to write
function Get<%=Name %>Notes() {...}
?
Why not use a generic function and just pass the id of the corresponding textbox? As in: GetNotes(thisTextBoxId) {...}. Not only would that deal with your problem but also is more DRY.
I have a WebPart custom control (Composite) in .Net, which gets created on page load to show a Chart using 'Dundas Charting Controls' (this is created by a user control inside the page). I get the properties for this control from the database.
I have another control, which is a Filter (outside webpart) and based on data of this filter control which the user selects and which I would get on postback after click of button, I have to show the filtered chart results. The problem is CreateChildControls() gets called before the postback data is available (which would be available only after the Page_Load event fires).
I'm unable to get this data in time to pass on the parameters for filtering the Chart Results.
The implementation os like this ...
Webparts
Page > User Control > Webparts > Composite Control/Chart
Filter
Page > User Control > Composite Control [I get this data on Postback]
It sounds like you are running into an event ordering issue. I always try to make my controles relatively dump - so they don't really know how they are being used.
Consider creating a method in your chart control to force an update of its data:
public void UpdateChart(-- arguments as needed --)
then create an event in your composit control (that has your filters) like
public event Eventhandler FiltersChanged;
Assign this to an event hander on parent page:
filterControl.FiltersChanged += new EventHandler(Filter_OnChange)
Then create an event handler that tells your chart control about the change
Filter_OnChange(Object sender, EventArgs e)
{
// get whatever data you need from your filter control
// tell the chart about the new data and have it reload/redraw
myChart.UpdateData( - filter options here -}
}
In doing so, you let the page direct the order of operations and do not rely on the order in which the child controls Load methods are called.
James - Thanks for your answer, but this does not seem to work in my scenario or rather I couldn't make it work, when I tried it. The controls seems to be doing too much and is getting data from every where, it has its own constructor implementation, Load() override etc so a single UpdateChart() function may not have done the trick in this case.
This is what I did, finally.
I fire an Ajax request with Filter Data and set the value in a Session Variable before page does a Postback, this way I get the data at all places/events, and pass on the same as parameter where required. I know it may seem weird way to implement this, but it saved additional Database calls (which in this case are many to create the controls again) even though it comes at the cost of an additional Server HTTP ajax request.
Let me know this implementation can have any negative impact.
I created an asp.net Server control that derives from a LinkButton, and renders a small javascript function to the page.
I want to use this control many times on the page, but just want the javascript to be rendered once on the page. On the other hand, I rather not manually add it in a js file, because i don't want to have a chance of forgetting to add the js file in the future.
What will be the best way to go about doing this ?
Preventing duplication in client scripts
Use a registration method such as Page.ClientScript.RegisterClientScriptBlock(..). There are a couple of overloads but they all work in a similar fashion. It's smart enough to render the script only once no matter from how many control instances it's issued.
Microsoft remarks in the MSDN documentation:
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.
The MSDN docs also contain sample code such as this:
// Check to see if the client script is already registered.
if (!cs.IsClientScriptBlockRegistered(cstype, csname2))
{
StringBuilder cstext2 = new StringBuilder();
cstext2.Append("<script type=text/javascript> function DoClick() {");
cstext2.Append("Form1.Message.value='Text from client script.'} </");
cstext2.Append("script>");
cs.RegisterClientScriptBlock(cstype, csname2, cstext2.ToString(), false);
}
Other similar registration methods
Different methods can be used to register your client script in different ways - see each of the ClientScriptManager.RegisterClientScript*(..) methods in MSDN for more detail about each. They're all built to prevent duplication in the rendered page.
This gives you the freedom to output scripts at the control level without worrying or having to track or count scripts.
Edit:
The ClientScriptManager instance is accessed through the Page.ClientScript property.
You can use the Page.RegisterClientScriptBlock method.
Here is an example from the MSDN:
if (!this.IsClientScriptBlockRegistered("clientScript"))
{
// Form the script that is to be registered at client side.
String scriptString = "<script language=\"JavaScript\"> function DoClick() {";
scriptString += "myForm.show.value='Welcome to Microsoft .NET'}<";
scriptString += "/";
scriptString += "script>";
this.RegisterClientScriptBlock("clientScript", scriptString);
}
HTH
Any way to reference a script on a CDN and not have it duplicated multiple times? Or can I just not worry about it since it's just a pointer anyway and the browser is (?) smart enough not to load the same library twice?
In other words, suppose I want the following code inserted from a server control:
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
If I use a RegisterClientScriptBlock I can use the IsClientScriptBlockRegistered method to not have the server control load it multiple times. But, the preceding code is likely to have been put on the page anyway by either another (different) server control or by the developer who wants to use jQuery for something outside of the server control.
How do I get the server control to not load the script if the developer already loaded it?
I have a UserControl A which contains
a dropdown
a placeholder
At runtime the placeholder will be populated with some UserControl B.
There are certain times which I need to trap the javascript onchange event in the dropdown to call a javacript function in B (to do a clientside update of B). What is a good design/practice for how to do this?
The naive way is to make the asp:dropdownlist a public member then send it into a public method of B:
// In controlling code
...
userControlB.Initialize(userControlA.TheDropDownList);
...
// In usercontrol B
public void Initialize(DropDownList dropdownFromA)
{
dropdownFromA.Attributes.Add("onchange", "myBfunction()");
}
But something smells bad with this approach. I would like to keep A and B as loosely coupled as possible. Any better ideas?
To keep them really decoupled you could introduce an observer pattern such as described here. I would have the drop down register it's own change event which would notify the observer that a change occured. Then if any other controls on the client are interested they will enlist to be notified when a specific observation is made.
Edit
Well you could name the variables based on a logical name for the observation, then you'd just check to make sure the observer exists before registering with it.
A different system I have seen used, would be more like an event dispatcher, essentially when you subscribe you provide a name for the event, then you fire you would include two arguments the name of the event, and the data for the event. In this model you would only have a single dispatcher on a page.
Within the DOM I'm fairly sure that all controls are accessible so making the dropdown public isn't really relevant, particularly as control a and control b are members of the same page and so are accessible to each other in your code-behind.
I'd think a more loosely coupled approach would be to have control B register it's method as an event listener on control a's change event. I'm not sure how much more loosely coupled you could make two controls where one depends on changes from another.
I include a JS file in a user control. The host page has multiple instances of the user control.
The JS file has a global variable that is used as a flag for a JS function. I need the scope of this variable be restricted to the user control. Unfortunately, when I have multiple instances of the control, the variable value is overwritten.
What's the recommended approach in a situation like this?
Some options are to dynamically generate the javascript based on the ClientId of the User Control. You could dynamically generate the global variable for example.
Another option and one I would recommend is to encapsulate the global variable and function within an object, then your user control can emit the JS to create an instance of that object (Which can be dynamically named thus letting you scope the object as you see fit).
Edit
I don't have a working code sample that I can share but, I have done this in a couple different ways. the easiest method is to do this in the markup of your user control.
<script language='javascript'>
var <%=this.ClientID%>myObject=new myObject();
</script>
Assuming your control has a clientId of myControl this will create a variable myControlmyObject.
Another way to do this would be to generate the script in the code behind you could register it using: Page.ClientScript.RegisterStartupScript().
I would recommend refactoring your code such that all the common JS logic is stored in one place, not in every UserControl. This will reduce the size of your page by a good margin.
You can pass in the id of the UserControl to the common JS method(s) to differentiate between the UserControls.
For the issue of limiting the scope of your 'UserControl' variable, you could store some sort of a Key/Value structure to keep your UserControl-specific value - the Key would be the UserControl clientID, and the value would be the variable that you're interested in.
For example:
var UCFlags = new Object();
//set the flag for UserControl1:
UCFlags["UC1"] = true;
//set the flag for UserControl2:
UCFlags["UC2"] = false;
To access them, you simply pass the ClientID of the UserControl in to the UCFlags array:
myFlag = UCFlags["UC1"];
On the server-side, you can replace the constant strings "UC1" or "UC2" with
<%= this.ClientID %>
like this:
myFlag = UCFlags["<%= this.ClientID %>"];
You can still use the <%= this.ClientID %> syntax here even though the bulk of the JS is in a separate file; simply set
UCFlags["<%= this.ClientID %>"] = value;
before the call to embed the JS file.
Well, if you have to keep with the current solution, you could rename your global variable to something like the following code, which should be in the .ascx file for your control:
<script type='text/javascript'>
var <%= this.ClientID %>_name_of_global_variable;
</script>
Where "this" is the asp.net control. That way, each control has a unique variable name, based off the client id. Make sure you update the rest of your javascript to use this new naming convention. The problem, it looks messy, and the variable names will become very long depending on where the control is embedded in the page.
Does that make sense? It should take minimal javascript modification to get it working.
I ran into same issue and below blog post solved it. Solution is to take Object oriented way for javaScript
Adding multiple .NET User Controls that use JavaScript to the same page