Paul's profileAutoSpongePhotosBlogLists Tools Help

Blog


    May 29

    Stop Messing With My Metadata! -- Locking Down Form Fields With Javascript

    This blog assumes you are familiar with techniques to add javascript to a SharePoint page (specifically script that pre-populates fields in a form based on query string parameters), such as in this previous blog.

    In this example, I have a Course (based on Custom List Item) and a Course Event (based on Event--it's a hidden type but you can work with it) which is an instance of a Course.  I also have a list of Course Registrations where people "sign up" for courses.  To make reporting easy, I wanted to have both the Course and the Course Event in the Course Registration item's metadata.


    The instructions on the page were clear (in my mind):  "If this is the course you want, click OK."  But some people took that to mean they should change the drop down menus and click OK.  All that does is raise doubt about which course they actually wanted and cause my reports (standard views with Group By) to be inconsistent.

    So, how can we lock these fields down?  First I tried to use the element attribute disabled.  That disabled the form fields, but when the form submitted, the data fields were empty (or using the first option).  So I needed a way to keep the values from my query string even if someone changed the field.

    The answer I came up with is a mash-up of the tested fillDefaultValues function and some new javascript to add events (like onchange) to the form elements.  Areas in green are the old fillDefaultValues function.

    <script type="text/javascript">
    _spBodyOnLoadFunctionNames.push("fillDefaultValues");
    function fillDefaultValues() {
      var qs = location.search.substring(1, location.search.length);
      var args = qs.split("&");
      var vals = new Object();
      for (var i=0; i < args.length; i++) {
        var nameVal = args[i].split("=");
        var temp = unescape(nameVal[1]).split('+');
        nameVal[1] = temp.join(' ');
        vals[nameVal[0]] = nameVal[1];
      }
      setLookupFromFieldName("Course", vals["CourseID"]);
      setLookupFromFieldName("Course Event", vals["EventID"]);
      switchback("Course");
      switchback("Course Event");
    }

    function setLookupFromFieldName(fieldName, value) {
      if (value == undefined) return;
      var theSelect = getTagFromIdentifierAndTitle("select","Lookup",fieldName);
      if (theSelect == null) {
        var theInput = getTagFromIdentifierAndTitle("input","",fieldName);
        ShowDropdown(theInput.id); //this function is provided by SharePoint
        var opt=document.getElementById(theInput.opt);
        setSelectedOption(opt, value);
        OptLoseFocus(opt); //this function is provided by SharePoint
      } else {
        setSelectedOption(theSelect, value);
      }
    }
    function setSelectedOption(select, value) {
      var opts = select.options;
      var l = opts.length;
      if (select == null) return;
      for (var i=0; i < l; i++) {
        if (opts[i].value == value) {
          select.selectedIndex = i;
          return true;
        }
      }
      return false;
    }
    function getTagFromIdentifierAndTitle(tagName, identifier, title) {
      var len = identifier.length;
      var tags = document.getElementsByTagName(tagName);
      for (var i=0; i < tags.length; i++) {
        var tempString = tags[i].id;
        if (tags[i].title == title && (identifier == "" || tempString.indexOf(identifier) == tempString.length - len)) {
          return tags[i];
        }
      }
      return null;
    }
    //This function adds an onclick event to the drop down arrow image that fires fillDefaultValues
    function getDropDownImg () {
      var downArrow=document.getElementsByTagName('img')
      for(var i=0; i < downArrow.length; i++)  {
      if(downArrow[i].alt ==('Display lookup values'))  {
      downArrow[i].onclick = fillDefaultValues;
        }
      }
    }

    //This function adds an event that fires fillDefaultValues
    function switchback(fieldName){
      var switchSelect = getTagFromIdentifierAndTitle("select","Lookup",fieldName);
      if (switchSelect == null) {
        var switchInput = getTagFromIdentifierAndTitle("input","",fieldName);
        switchInput.onmouseup = fillDefaultValues;
        getDropDownImg();
      } else {
        switchSelect.onchange = fillDefaultValues;
      }
    }

    //-->
    </script>

    You'll notice, just like the fillDefaultValues function, we add parameters for our fields.  Since fillDefaultValues is being "pushed," these parameters also push our new javascript that adds the new event.

    In my testing, only IE seemed to change long select lists into inputs.  Additionally, the onchange event that MOSS has coded already [onchange="HandleChange()"] doesn't seem to like being overridden so I had to use onmouseup (other events already active in the input element are onfocusout, onkeypress, and onkeydown).  Also, because the image element is not really part of the input element, we had to override its event [onclick="ShowDropdown] with our own in the getDropDownImg function.

    This was tested in IE, FF, and even Safari with great results.  You can't "accidentally" change the metadata the page loads with based on the query string.

    Lastly, we don't want our friendly users mucking with the metadata on the EditForm.aspx page either (but we still want them to be able to delete the item).  But my research into the disabled attribute was not in vain.  I created another javascript function that is added to disable the select and input elements.  Make sure you specify the elements.  If you try to disable all input and select elements, you can't access ANY controls, including those in the page's configuration panel.

    <script type="text/javascript">
    _spBodyOnLoadFunctionNames.push("disableForm");
    function disableForm () {
      var disableInput=document.getElementsByTagName('input')
      var disableSelect=document.getElementsByTagName('select')
      for(var i=0; i < disableInput.length; i++)  {
      if(disableInput[i].title == "Course" ||
      disableInput[i].title == "Course Event")  {
      disableInput[i].disabled = true;
        }
      }
      for(var i=0; i < disableSelect.length; i++)  {
      if(disableSelect[i].title == "Course" ||
      disableSelect[i].title == "Course Event")  {
      disableSelect[i].disabled = true;
        }
      }
    }

    //-->
    </script>

    As you can see, I was a little sloppier with this and there are two places where each field name needs to be added (since at any time either list could exceed 20 items and switch from a select element to an input).  The end result looks like this:



    Another approach I just thought of includes redirecting the user from the EditForm to the DispForm or change the list definition in SPD.

    Comments (8)

    Please wait...
    Sorry, the comment you entered is too long. Please shorten it.
    You didn't enter anything. Please try again.
    Sorry, we can't add your comment right now. Please try again later.
    To add a comment, you need permission from your parent. Ask for permission
    Your parent has turned off comments.
    Sorry, we can't delete your comment right now. Please try again later.
    You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
    Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
    Complete the security check below to finish leaving your comment.
    The characters you type in the security check must match the characters in the picture or audio.

    To add a comment, sign in with your Windows Live ID (if you use Hotmail, Messenger, or Xbox LIVE, you have a Windows Live ID). Sign in


    Don't have a Windows Live ID? Sign up

    Marcus Whitewrote:
    Paul
    I too am trying to work with two lookup fields that have more that 20 items but for some reason I can not get my onmouseup event to raise. Is there anything special that needs to be in place for this to work? I've tried this on a custom list and a normal SharePoint list. IE Developer Tools says that I'm doing everything corrent but testing is proving otherwise. Thanks

    Marcus
    4 days ago
    Nov. 3
    Oct. 30
    No namewrote:

    Hi,Do you have second hand lcds, used laptop lcds and used LCD displays? Please go here:www.sstar-hk.com(Southern Stars).We are constantly buying re-usable LCD panels.We recycled LCDs.The re-usable panels go through strictly designed process of categorizing, checking, testing, repairing and refurbishing before they are re-used to make remanufactured LCD displays and TV sets.Due to our recent breakthrough in testing and repairing technology of LCD, we can improve the value for your LCD panels. website:www.sstar-hk.com[icgehefijbdih]

    Sept. 28
    Paulwrote:
    Andy,

    You can't use the disable technique for NewForm (for the reasons you mentioned).  That's why I use the "switchback" function.  Everytime someone changes the field from the query string value, the switchback function fires again to prepopulate the form fields.  Only on edit forms you can disable fields.
    Sept. 13
    Hi,
     
    I've used you code above to disable a lookup field in a newform and the form itself works ok.  It loads, automatically selects the correct value from the lookup and disables the field so I can't change it.  But when I click ok and the form is submitted, the value in the disabled field is not saved.
     
    Can you help?  Any suggestion of what to check for/what might be causing it not to save?
     
    Thanks
     
    Andy
    Sept. 9
    Paulwrote:
    I think even small workgroups can gain efficiencies with SharePoint.  Without strong backup, administration, and training, I would avoid SPD with two exceptions.  One, SPD can make "single serving" workflows (but I would still stress the importance of technology assisted human workflow) and SPD can help you develop dataview wep parts (that you later export and use in web part pages).

    I think the greatest ROI from MOSS can be found with non-profits and education because these organizations often struggle to hire and keep strong IT talent and get terrific cost breaks from MS licensing.  Anyone can make use of WSS with a small investment--the number of codeplex projects to extend WSS functionality grows weekly, but this may require additional IT resources to test and deploy.
    Aug. 9
    No namewrote:
    do you have something to say about Sharepoint for small businesses with maybe up to 30 users?
    My point here is that it makes no sense to talk to them in terms of (sharepoint-like) features and solutions, as they are not likely to invest in staging areas and want to have a direct ROI.
    This seems the scenario where Sharepoint Designer comes in. What do you think of this?

    Greetings

    Pointer
    Aug. 8

    Trackbacks

    The trackback URL for this entry is:
    http://autosponge.spaces.live.com/blog/cns!D7F85948C20F0293!397.trak
    Weblogs that reference this entry
    • None