I am porting an existing application to a DotNetNuke module and discovered bizarre behavior. I use ClientID when creating javascript so that the code can identify the HTML elements. Normally that creates a value something like this:
"g_ctl00_ctl01_ctl00_ctl00_txtSearch"
We've all seen this a million times, right? Well, after porting this code to a DotNetNuke module and running it for the first time, the ClientID property is returning this:
"dnn_ctr397_GalleryServerPro.Web.Gallery, TechInfoSystems_ctl00_ctl01_ctl00_ctl00_txtSearch"
Notice the comma and the space. This is causing all kinds of javascript errors. For example, the ASP.NET Login control is now outputting invalid javascript:
var dnn_ctr397_GalleryServerPro.Web.Gallery, TechInfoSystems_ctl00_ctl01_ctl01_lv_ctl02_Login1_UserNameRequired = document.all ? document.all["dnn_ctr397_GalleryServerPro.Web.Gallery, TechInfoSystems_ctl00_ctl01_ctl01_lv_ctl02_Login1_UserNameRequired"] : document.getElementById("dnn_ctr397_GalleryServerPro.Web.Gallery, TechInfoSystems_ctl00_ctl01_ctl01_lv_ctl02_Login1_UserNameRequired");
My module has an assembly name of TechInfoSystems.GalleryServerPro.dll, the default namespace is GalleryServerPro.Web, and the user control is in a class named Gallery.cs, so that partially explains where some of that extra text is coming from, but why is it there? And what can I do to ensure that ClientID does not output commas, spaces, or other characters that can cause problems in javascript?
Thanks,
Roger
I (sort of) figured it out. I needed to add an ID to the user control that is being added to the DotNetNuke page. This is the main view user control - the one that inherits PortalModuleBase. The user control is contained entirely in a code-behind class (no .ascx), so adding this to the Init event fixed everything:
this.ID = "gsp";
My theory is that the issue happens when these are true:
The "view" user control uses only the code-behind file (no .ascx file)
The user control does not have an ID specified.
When registering a module without the .ascx, one must specify the class and assembly in the Module Definitions. In my case, it is "GalleryServerPro.Web.Gallery, TechInfoSystems.GalleryServerPro". That looks very close to the text DNN ends up inserting into the clientID string. DNN (or ASP.NET) must be taking that string and using it to build the clientID in the absence of an ID assigned in the user control.
Not sure if this is a DNN bug, but I am happy to figure it out and now can move on...
You could try referencing your control using a class instead?
There are plenty of good functions out there for doing such a thing. I would recommend jquery for a start, to make all things javascript a world easier.
I have NO idea why this is happening (but then I've never used DNN)
what you could do is take control of the ClientID yourself for the affected servercontrols, for instance, it looks like you're using a TextBox control with server ID = txtSearch, what you could do is make a class that inherits from TextBox and setting its ClientId to be equal to the Server ID (assuming there's only one control called txtSearch)
see here
It might be that DNN would override even that, but it might be worth a shot
Related
In ASP.NET much has been made about the ClientIdMode property that gives developers greater control over the a control's ID attribute as it appears in the HTML.
However little attention appears to have be paid to the way the controls render their Name attribute. It appears to be a simple concatenation of the control's ID and its hierarchy of naming containers.
Given a sufficiently complex web page these names get very long. They not only make the HTML payload big (and ugly) but are also posted back to the server on every postback. (Also, they make their way into the Control State of some third party control suites. )
Why isn't there a ClientNameMode property - or similar? Surely it is as important as the Id attribute? Is it possible to override some method that generates / rehydrates the Name attribute so that we can man handle it to maybe match the Id? (made shorter by the ClientIdMode)
An example of a name of a control on a page that I am working on is
USoWAR1_tabContainer_UDetailsTabContainer_tabContainer_UDetailsTab_UDetails1_UDueDateAndNotifications1_decDetail_DataEntryRow1_datDueDate_DateTimePicker_calendar_AD
As far as I know the only way to alter this functionality is to extend controls into your own and override UniqueID property (e.g. by returning Server-side ID).
I had this same issue, and had to use JS to set the attr after loading.
$('#idofdomobjiwanttoname').attr("name", "whatIWantToNameIt");
I want to be able to declaratively specify the webcontrols of any given page in the standard way, but have the runtime not necessarily new up instances of the types I specified, but rather for instance check against web.config whether a different webcontrol (which would inherit from the specified one) should be used instead.
for instance I could have a webcontrol in the path /templates/controls/default/PersonSelector.ascx and another one in the path /templates/controls/CUSTOMER_A/PersonSelector.ascx (inheriting from the first)
so if my config indicated that I preferred to load controls from the CUSTOMER_A folder where available, and since it would be available in this case, the actual markup which was used for the page would come from .../CUSTOMER_A/PersonSelector.ascx. All code which compile-time assumed that it was in fact an instance of ../default/PersonSelector.ascx would still work since it is actually a subclass.
This would afaik be a major benefit to our site which is a multitenant site in which 99.5% of the generated html is identical across all our customers (except for skinning, naturally) but some of the customers wants different headers/footers/selection-logic etc. So far all the differences are handled by bloating the controls and if/else-ing all over the place.
I realize that asp.net is probably not the weapon of choice for doing this kind of thing, but it's the one we've got, and the codebase is large enough that a rewrite would be a pain.
Yes, you can load user controls dynamically using the LoadControl method. Provided that you have Control1 and a Control2, where Control2 inherits from Control1, you could do this in a page:
//Obtain control name in the format "~/pathFromApplicationRoot/ControlName.ascx"
string controlName = MethodThatDecidesTheControlNameToLoad();
//Load control
var userControl = (Control1)this.LoadControl(controlName);
//Add it to page
AControlSuchAsAPlaceHolder.Controls.Add(userControl);
Edit:
In order to be able to specify your user controls declaratively in markup I guess you could make the user control replace its own content with the tenant specific control if the config told it to override.
I am creating a web user control for a simple poll. I am currently registering it on the page and then referencing it via tagprefix.
The form for the poll is in basic html (no server controls) and is in the front-end of the web control. How can I change the look of the user control depending on the settings passed into it? Is this possible without using server controls?
Update
Can I change the html layout of a user control? If so could someone post some examples. Please note I do not use asp.net form controls, so none of that please :)
You might be able to also use jQuery to replace existing css setting in your code. Create properties on for your user control, and then pass settings in the classes. Then use jQuery to replace them. This however requires jQuery to be linked to your page (or within your control) and you'd have to write the CSS classes out to the jQuery code (using server controls, but you could use the literal control so there's no excess code).
Personally I'd go with the option of using server controls instead of straight up HTML, you'd get alot more flexibility, and then passing through the settings would be pretty straightforward, put something like this in your controls backend code:
Private _TextBoxCssClass As String
Public Property TextBoxCssClass() As String
Get
Return _TextBoxCssClass
End Get
Set(ByVal value As String)
_TextBoxCssClass = value
txtBox1.CssClass = value
txtBox2.CssClass = value
End Set
End Property
You most likely want to have a property or event in the control that changes the css. It may end up best to add some server controls or javascript / jquery to make it easier.
If its only the styles you want to change, then you can expose a property to set the style attribuites of the respective control inside your User Control. If you want to control the whole HTML layout of the control then Custom Control is the viable option.
I'm currently working on an ASP.NET 3.5 project, and I wanted to know your opinion regarding the following situation, which I happen to run into sometimes:
Let's say I've defined the following control of an imaginary component framework somewhere in my code:
<Window runat="server" ID="windowTest" />
Let's assume that with the above mentioned imaginary component framework it's possible to get a reference to my Window control from the client-side using its ID (for example to change its appearance):
function MyFunc(){
var win = GetWindow("windowTest");
}
Let's also assume that both code snippets are placed in different files, e.g. the JavaScript code in MasterPage.Master and the control in AnotherPage.aspx.
As you might already have noticed, the passing of the control's ID as a hard-coded string to the GetWindow function is a bit problematic here, since changing the control's ID is going to break the JavaScript function.
This situation surely smells like it needs a good ol' Replace Magic Number with Symbolic Constant refactoring. I can achieve this by dynamically creating the Window control and using a constant for the value of the control's ID:
AnotherPage.aspx:
Window windowTest = new Window();
windowTest.ID = Consts.ID_WINDOW_TEST;
form1.Controls.Add(windowTest);
MasterPage.Master:
function MyFunc(){
var win = GetWindow("<%= My.Namespace.Consts.ID_WINDOW_TEST %>")
}
My question now is: How do you handle such situations? Do you create all your controls dynamically (like shown in the example above) when running into this situation, and are there any drawbacks using this approach, e.g. Designer doesn't display the control anymore? Or do you say "Screw it, nobody's going to change that control's ID" and leave it hard-coded in your code? Or do you have other approaches to this situation?
I personally am a fan of the of the first option (refactoring), since a) it makes sure that a change to the ID is not going to break my code and b) I almost never work with the Designer, but I thought I'd ask this question on SO to get some valuable opinions on this.
Thanks in advance for all the responses.
Greetings,
Giu
Update / Clarification:
I made a small error in the first version of this question by stating that the code snippets are placed in the same file. Since both the control and the JavaScript method are located in the same file, there is no need to create the control dynamically and defining the control's ID using a constant; by defining the control directly in the .aspx file I could use its ID in the JavaScript method as follows: GetWindow("<%= windowTest.ID %>");
But, my problem is another one; the control and the JavaScript method are each placed in different files, in which case the mentioned approach of using the control's ID doesn't work anymore. Therefore I introduced the solution mentioned in my question with the constant and the dynamic creation of the control. I now corrected both the filenames in my question so that the correct scenario is described to which my question is related.
In 4.0 you can control the client ID that's generated in master/content page situations quite well. but i believe if someone changes the ID manually in the page at one place and not in the javascript code it will still be a problem. If you are the only one who'll be working on this code then you can always be mindful and refactor properly. Otherwise you can go in for the constants option or store the IDs in a separate resource file.
In my opionion there a two suitable solutions:
1) Use the JQuery framework to get ahold of the html element you want to adress via JavaScript. JQuery is designed to be able to work with autogenerated hierarchically created control IDs
2) Use .net Framework 4.0 and don't use autogeneration of the Control ID. (I've heard that this is a new feature in 4.0. I think in your situation it might be worth trying out)
Check out Rick Strahl's blog post entitled "A generic way to find ASP.NET ClientIDs with jQuery"... it seems to have some good ideas that could be of some help to you.
He uses jQuery, as the first responder suggested, but does it in a way that you are using ASP.NET's built-in ClientID property to get the actual id ASP.NET generates and uses a client-side friendly mechanism that enables you to write script code referencing controls that won't break with ID changes.
I think I summed up the question in the title. Here is some further elaboration...
I have a web user control that is used in multiple places, sometimes more than once on a given page.
The web user control has a specific set of JavaScript functions (mostly jQuery code) that are containted within *.js files and automatically inserted into page headers.
However, when I want to use the control more than once on a page, the *.js files are included 'n' number of times and, rightly so, the browser gets confused as to which control it's meant to be executing which function on.
What do I need to do in order to resolve this problem? I've been staring at this all day and I'm at a loss.
All comments greatly appreciated.
Jason
If the issue is simply that the same file is being embedded multiple times and causing conflict, look into using RegisterClientScriptInclude. If you use the same identifier for all of your calls to RegisterClientScriptInclude only one instance of that file will be embedded:
http://msdn.microsoft.com/en-us/library/2552td66.aspx
However, if the issue is that your methods are being called but they don't know what controls on the page to operate on then you need to figure out how to provide your JS some context. Define a JavaScript object that represents your control on the client-side, and on the server-side emit the call that will instantiate it with the client IDs of the controls you'll be operating on.
We are using CustomValidator to validate User Control. The control works fine until you drop two instances of the Control on the same page, since they reference the exact same JavaScript functions, only one control works. Work around, we appended JavaScript function name with control id.
Validate_SAPDepartment<% =ControlId %>(oSrc, args) {...}
In codebehind, we assinged ClientValidationFunction
CustomValidator1.ClientValidationFunction = "Validate_SAPDepartment" + this.ControlId
This may not be the right approach but it works.
I've had this situation before. You register a separate JavaScript file with the page using the ScriptManager. You can stream this as a resource file embedded into the dll if you wish.
Then you only call into the functions from your control.
Otherwise a completely separate jquery file may also work.