[This article should be considered deprecated. The code represented has been improved, extended and made available as the DP_SharePoint Function Library. All future effort will be applied there.]
I’ve been digging further and further into the bowels of SharePoint. It’s not a pleasant place to dig. At my company end-users and team-site owners are prohibited from doing any back-end development. Additionally, due to various political issues, there are currently no available processes to contract for such work (although you can be added to an ever-growing waiting list). The only development path open to site owners is client-side JavaScript.
On the team-site I manage one of the most used features is a custom list cataloging enterprise Incidents managed by the team. Each incident is logged with information about the applications affected, the team engagement times and many other pieces of information. The list feeds business customer reports and generates performance metrics for upper management. With over 20 people on the team the quality of information can be rather shaky. It would benefit greatly from some simple validation and a few points of automation.
One of the first issues to consider is that the HTML generated by SharePoint is… poor. However it is predictable and as awful as it can be there is a method to the madness. To attach code to the fields in an Add or an Edit form for list you first need to obtain a reference to the field so this is where I started.
Getting a Reference to a Field
I, of course, looked online first and nearly everything I found was related to this 2007 post from the MS SharePoint team. They describe a function, getTagFromIdentifierAndTitle(), that did almost what I wanted. However I found the function complex to use as you needed to know the internal “identifier” as well as the tag name that SharePoint used to render the field. The extra information seemed to be added for performance and not for quality of the selection. I decided to take the easier way.
In my function, getFieldRef(), you need only pass the Title of the field (the name that appears on the Add or Edit page, not the internal name of the field). The function traverses all of the form fields on the page looking for the correct one and returns a JavaScript reference to it. This method does entail multiple calls to getElementsByTagName() and a loop for each type of form field but performance, even over large lists with multiple complex fields, seems more than reasonable.
So to get a reference to an input field called “ID Number” you would simply called this:
var IDNumRef = getFieldRef("ID Number");
Special Form Fields
Then the second problem was trickier. Many single columns of data in a list are represented as multiple form fields on the Add and Edit pages. A single datetime, for example, is represented as three distinct fields: an input field for the date, a select control for the hours and a second select control for the minutes. Using my function with only the field name will return the “first” field in these compound fields (which is often enough) but not the rest.
So I added a second, optional, parameter, SpecialType, that can be used to return a collection of references. In this first version the only values allowed for SpecialType are “date” and “multiselect”.
If SpecialType is “Date” getFieldRef() returns an object with the following properties:
- Date: A reference to the date input field (day, month, year).</li>
- Hours: A reference to the hours select control.</li>
- Minutes: A reference to the minutes select control.</li>
If SpecialType is “multiselect” getFieldRef() returns an object with the following properties:
- Candidates: A reference to the multiselect control with the available options (the left-side control).
- Selected: A reference to the multiselect control with the selected options (the right-side control).
- AddButton: A reference to the “Add” button for the field.</li>
- RemoveButton: A reference to the “Remove” button for the field.</li>
So, for example, to get the currently selected “Minutes” of a Date Time field called “Start” you might use the following:
var StartFld = getFieldRef("Start", "date"); var CurMinutes = StartFld.Minutes.options[StartFld.Minutes.selectedIndex].text;
I will be adding more SpecialTypes, such as the people picker, as I move forward.
To actually use the references returned by the function for real work you’ll need to know what kind of field you have (input, select, textarea, etc) of course, but that’s easy enough to determine just by looking at the form.
The Source Code
I’ll eventually be releasing all of this code, plus significant additional functionality in a single library. Although we are currently in an extended upgrade project to SharePoint 2010 we are currently still on SharePoint 2007. The code should, I believe, work on 2010 but I won’t have any way to validate that for some time.
To make the function available to the page I added it (and my custom validation code) to a hidden content editor web part on both the Add and Edit pages (in my experience, so far, there’s no need to treat them differently).
// getFieldRef Method // getFieldRef = function( Title, SpecialType ) { // Manage Parameters if ( SpecialType ) { SpecialType = SpecialType.toLowerCase(); } else { SpecialType = null; }; // Get collection of input and select tags var InputTags = document.getElementsByTagName("input") var SelectTags = document.getElementsByTagName("select"); var TextareaTags = document.getElementsByTagName("textarea"); // Loop over the tag collections to find the right one var BaseTag; for (var cnt=0; cnt < InputTags.length; cnt++) { if ( (InputTags[cnt].title == Title) ) { BaseTag = InputTags[cnt]; }; }; for (var cnt=0; cnt < SelectTags.length; cnt++) { if ( (SelectTags[cnt].title == Title) || (SelectTags[cnt].title == Title + " possible values") ) { BaseTag = SelectTags[cnt]; }; }; for (var cnt=0; cnt < TextareaTags.length; cnt++) { if ( (TextareaTags[cnt].title == Title) ) { BaseTag= TextareaTags[cnt]; }; }; // No field found, return null if ( !BaseTag ) { return null; }; // Deal with special types, if requested if ( SpecialType ) { // Create the return object var TagSet = new Object; // Special type "date" if ( SpecialType == "date" ) { TagSet.Date = BaseTag; TagSet.Hours = document.getElementById(BaseTag.id + "Hours"); TagSet.Minutes = document.getElementById(BaseTag.id + "Minutes"); }; // Special type "multiselect" if ( SpecialType == "multiselect" ) { TagSet.Candidates = BaseTag; TagSet.Selected = document.getElementById(BaseTag.id.replace("SelectCandidate", "SelectResult")); TagSet.AddButton = document.getElementById(BaseTag.id.replace("SelectCandidate", "AddButton")); TagSet.RemoveButton = document.getElementById(BaseTag.id.replace("SelectCandidate", "RemoveButton")); }; // Return the TagSet return TagSet; } else { // if no special type, return the BaseTag return BaseTag; }; };
This isn’t perfect, but it seems to work so far and is quite simple to use.
Easily getting a reference to a field is essential but not in-and-of itself very useful. In the next installment I’ll cover using these references to add events and custom validation to the fields.
1 Comment
Add a Comment