7 Things to remember when working in Sharepoint and/or AJAX.NET

Alot of heads down code work with our Sharepoint Content Management application, so from that comes alot of lessons learned. Why seven? Because it is lucky, and that is all I got (which is enough!!!).


Besides a few quirky issues, I am pleased with the ASP.NET Ajax ToolKit, and I definitely find value in how it keeps your AJAX code (most of it done for you) within the VB/C# code behind. As opposed to heavy javascript approach that normally accompanies any AJAX enabled application, this approach allows for traditional back-end coders to make the rich browser front-ends.

So here it goes, in no particular order, except for the first one which details a coding pattern that I use throughout my apps.

1. Make good use of the CLIENTID property of server controls
When ASP.NET renders its HTML from the server controls, it can rename the objects to reflect the container relationship of that object. For example, an ASP:TextBox (which is basically an INPUT tag) with a server side ID of “txtThisTextBox”, that is within an ASP:Panel might have a client side ID of “panel1_txtThisTextBox”. This gets even worse within a Sharepoint webpart because there are ALOT of containers around every web part. This can make it very difficult to write javascript that references these objects since any change in the container structure of a control can have downstream effects.

The way that I combat this is to create an ASP:Literal object on the page (a literal prints its text as-is with no ID/style/etc..) and on the server side I create a StringBuilder object where I create a bunch of javascript variables and set the text of these variables to the CLIENTID property of the server control. The CLIENTID property knows what the server control will be named on the client, so we will let the client know these names by creating variables for them.

This is what it looks like in VB.NET


Dim sb As StringBuilder = New StringBuilder
sb.Append("var txtThisTextBox='" + txtThisTextBox.ClientID + "';")
sb.Append("var txtThisOtherTextBox='" + txtThisOtherTextBox.ClientID + "';")

' litJsVars is my ASP.NET Literal object
litJsVars.Text = sb.ToString

Then I can write my javascripts using the same object names as the server objects:


document.getElementById(txtThisTextBox).value = 'foo';

When I first started doing this, I was only tracking a few variables, but now that it has grown to over 10, I am about to change this approach to using a Javascript class so I can refer to my server objects with a syntax like “serverObjs.txtThisTextBox”.

2. Watch out for those ASP.NET Panel objects
Previously mentioned in another post, this quirky behavior of the ASP:Panel class (and it latest derivative the ASP:TabPanel) continues. The issue is that an ASP:Panel object does not always inherit (or respect) the visibility attribute of its parent container. This came to light when I had an ASP:Panel object within a YAHOO modal dialog box which sets the visibility of itself to “hidden” when not used. Unfortunately, the ASP:Panel did not hide itself, and although it had no text, its mere presence prevented buttons and links beneath it from being clicked.

Now with the ASP:TabPanel, if you place that in a YAHOO modal dialog, you can still see the tab labels, even when the YAHOO modal is hidden. To get around this, we had the ASP:TabContainer object default to a visibility style of none, and explicitly set it to block before showing the YAHOO modal.

In this example, the “tabNotesId” JS variable is the ClientID of the TabContainer in question.


function openHistory(){
    YAHOO.util.Dom.setStyle(tabNotesId,'display','block');
    YAHOO.myComp.enrollment.notes.show();
}

This means that you also have to hide the TabContainer when you hide the YAHOO panel, which can be done by subscribing to the beforeHideEvent of the YAHOO modal panel.


YAHOO.myComp.enrollment.notes.beforeHideEvent.subscribe(hideNotesTabs);

function hideNotesTabs(){
    YAHOO.util.Dom.setStyle(tabNotesId,'display','none');
}

Some may ask why I did not use the new AjaxToolKit modal dialog. One simple reason; our popups are used to help users enter data from a PDF image, so the popups need to be able to hover over the PDF while allowing the user to still scroll the PDF. Only the YAHOO modal would allow us to do that.

3. Dynamically adding tabs to an ASP:TabContainer
The ASP:TabContainer object does make it easy to add tabs programmatically, but they throw a curve ball at you if you are using any AJAX updatepanels. If you create ASP:TabPanels dynamically, you MUST create them in the Page_Init event and NOT in the Page_Load. If you do not, the adding of panels will break all of your AJAX update panels. This seems to be a more persistent issue within Sharepoint as opposed to a standalone ASP.NET application. No idea why, it just is.

4. ASP:Validators work too well
The validation objects that .NET provides are very flexible and simple, but as they are designed today, they work too well. Validators keep a page submit from happening until the validation requirements are complete for the form. Yet, in the AJAX world, you do not need to have forms to send data to a server, so validators that are meant for one small piece of your entire page, will get fired for every attempt to submit data to the server.

In my case, I had a validator in an update panel that was hidden in a modal dialog box. When I went to trigger an update panel, nothing worked. It was pure luck that I opened the hidden dialog box and realized that the validator had fired and had an error message displayed. So for now, we will need to do validation for AJAX.NET applications on the server and show errors with ASP:Labels, or Literals.

5. As usual, too many managers will make a mess of things.
In this case, it is the ASP:ScriptManager object that you need to keep an eye on. This object adds the appropriate javascript files to the page depending upon which objects are used in the server code. Every AJAX.NET page should have one and only one ScriptManager. In the Sharepoint world, where any given page can have multiple webparts that could each need a ScriptManager, this can cause problems.

The solution is simple, since it involves using someone else’s code. When you create your WebPart DLL, you can extend the class with the AJAXBasePart class that you can find here.

6. Forcing an ASP:UpdatePanel update from Javascript
The trick with AJAX.NET update panels is that they require triggers to make the update. These triggers are typically button or link clicks, but can also be the changing of a field value, even a hidden field. In my example, I have a grid where a user can select one of the rows, and a JS function will set a hidden field, which will force an UpdatePanel update, that will show the additional detail of the selected row.

The ASP code will look like this:


<asp:UpdatePanel ID="memberExtensionInfo" runat="server" ChildrenAsTriggers="false" UpdateMode="Conditional">
     <Triggers>
          <asp:AsyncPostBackTrigger ControlID="hiddenMemberId" />
     </Triggers>
     <ContentTemplate>
          <asp:HiddenField ID="hiddenMemberId" runat="server" OnValueChanged="updateExtensionPanel" />
          <asp:Label ID="applicantLastName" runat="server" Text="loading applicant info..."></asp:Label>
     </ContentTemplate>
</asp:UpdatePanel>

The VB code would look like this:


Public Sub updateExtensionPanel(ByVal sender As Object, ByVal e As System.EventArgs)
     applicantLastName.Text = hiddenMemberId.Text
     memberExtensionInfo.Update()
End Sub

The JS code would look like this:


function updateMember(memId)
     var hiddenField = $get(hiddenFieldId);
     if (hiddenField) {
          hiddenField.value = memId;
          __doPostBack(hiddenFieldId,'');
     }
}

7. TabContainer in Quirks mode
Previously ranted about here, it is easily fixed with a little CSS override:


.ajax__tab_xp .ajax__tab_tab {height:21px;padding:4px;margin:0;}

..

7 Responses to “7 Things to remember when working in Sharepoint and/or AJAX.NET”

  1. Ishai Sagi Says:

    not too sharepoint specific, is it?

  2. Administrator Says:

    Hence the “and/or” sequence in the title.

  3. Mahavir Says:

    I have used tabContainer in my custom web part i aqm using on one of my sharepoint site page. my button control in TabPanel of tabcontrol(ajax toolkit) is working for just first time it clicked. It doesnt work on subsequent clicks.
    The tabs disply properly on first page load on overloading the css but render improperly after this first click.

    Can you please help me in this case?

  4. (bs.) Says:

    sharepoint-specific or not, i appreciate your article (in other words, ishai sagi, if you have nothing nice/constructive to say, shut up). just because the article doesn’t address a specific coding problem someone is experiencing doesn’t mean this article is worthless.

    this is some GREAT tips, thanks so much!
    (bs.)

  5. Ed Says:

    i stumbled on this blog about 5 minutes before i actually encountered the problem, but this line was a life-saver…
    “If you create ASP:TabPanels dynamically, you MUST create them in the Page_Init event and NOT in the Page_Load.”

  6. Administrator Says:

    Glad to know I saved someone the headache

  7. Fireflye Says:

    I think your approach to asp validators is somewhat unfair - as I’m sure you could easily find the solution by simply using the ValidationGroup property’s capabilites. I find it mildly disturbing that you speak with such great authority on a subject you are clearly not that familiar with.

Leave a Reply