Wednesday, July 30, 2014

How to conditionally hide, disable, and make mandatory fields on SharePoint forms dynamically

Here I'd like to demonstrate the most popular cases you may face during implementation of SharePoint forms with complex logic. Most likely you will need to combine the cases described below to cover your requirements but it's quite easy if you have enough samples. To accomplish the tasks below I used SharePoint Forms Designer 2.8.10. All samples work properly for all editions of SharePoint 2010/2013 including Foundations and Office 365.

First of all, I'd like to make an important notice which is common for all the cases. fd.field() method of JavaScript framework expects an internal name of a field you want to retrieve. As you might know, it's not easy enough to obtain an internal name of a field in SharePoint, so we included this property into field properties in Forms Designer 2.8.10:

Internal name of SharePoint field

Now you can just copy and paste it into your code in a much simpler way even without closing Forms Designer. OK, let me start.

Prepopulate field and disable/enable it based on condition

Let's say we have a task form and we need to set the Percent Complete to 100% and disable it when the user turns the Status field into Completed:

SharePoint conditional fields

We should place the following code into JS-editor of Forms Designer:

function setPercentComplete() {
    if (fd.field('Status').value() == 'Completed') {
        // Setting the Percent Complete to 100
        fd.field('PercentComplete').value('100');

        // Getting JQuery-object of the field and disable it
        fd.field('PercentComplete').readonly(true);
    } else {
        // Getting JQuery-object of the field and enable it
        fd.field('PercentComplete').readonly(false);
    }
}

// Calling setPercentComplete when the user changes the status
fd.field('Status').change(setPercentComplete);

// Calling setPercentComplete on form loading
setPercentComplete();

// Enabling fields before the submission
fd.onsubmit(function () {
    fd.field('PercentComplete').readonly(false);
    return true;
});

Please, pay attention to the last part of the code:

// Enabling fields before the submission
fd.onsubmit(function () {
    fd.field('PercentComplete').readonly(false);
    return true;
});

Most browsers don’t send values of disabled fields to the server during the submission, so they will be lost. Therefore, we need to enable all fields that we need to save right before the submission in onsubmit handler.

Hide/show field or set of fields conditionally

Now I will modify the script from the previous section so that it will hide the Percent Complete field. First, we should assign a CSS-class to the field to use it in JQuery-selector:

Assign CSS-class to SharePoint field

OK, now we can use it to retrieve the field container by the following way: $('.percent-complete'). Here is the modified code:

function setPercentComplete() {
    if (fd.field('Status').value() == 'Completed') {
        // Setting the Percent Complete to 100
        fd.field('PercentComplete').value('100');

        // Getting JQuery-object of the field container and hide it
        $('.percent-complete').hide();
    } else {
        // Getting JQuery-object of the field container and show it
        $('.percent-complete').show();
    }
}

// Calling setPercentComplete when the user changes the status.
fd.field('Status').change(setPercentComplete);

// Calling setPercentComplete on form loading
setPercentComplete();

Please, notice that I've removed the last part from the code because values of hidden fields are passed to the server properly.

What if we need to hide multiple fields? There are several approaches. You can either put them into a single table and assign CSS-class to the table:

Assign CSS-class to multiple SharePoint fields

Next, use that class in JQuery-selector to retrieve the table and hide or show it:

// Hide the table
$('.fields-to-hide').hide();

// Show the table
$('.fields-to-hide').show();

Or if your fields are scattered about the form and you cannot put them into a single table, you can assign the same CSS-class to all fields that you need to hide e.g. 'field-to-hide' and use it in the selector to make all of them disappear:

$('.field-to-hide').hide();

Require field based on condition

At this section I'd like to demonstrate how to make some fields mandatory based on other field values. Let's go back to the original task form and say that we need to make the Due Date field required if the task is assigned to someone (the Assigned To field is not empty).

Here is the sample:

function setRequiredFields() {
    if (fd.field('AssignedTo').value().dictionaryEntries.length != 0) {
        // Add asterisks
        fd.field('DueDate').titleRequired(true);
    } else {
        // Remove asterisks
        fd.field('DueDate').titleRequired(false);
    }
}

// Calling setRequiredFields when the user changes the assignment
fd.field('AssignedTo').change(setRequiredFields);

// Calling setRequiredFields on form loading
setRequiredFields();

// Custom validation
fd.onsubmit(function () {
    if (fd.field('AssignedTo').value().dictionaryEntries.length != 0) {
        if (!fd.field('DueDate').value()) {
            alert('Please, fill in the Due Date field.');
            return false;
        }
    }

    return true;
});

As you can see, I've added the custom validation into onsubmit handler, whereas fd.field().titleRequired() just adds or removes the asterisk near the title.

Get values on display forms

Finally, I'd like to focus your attention on differences between display and edit or new forms. Display forms don't contain controls, so you can retrieve only the text representation of field values like you see them on a form. The samples above work on new and edit forms only. You should use the following syntax to obtain a text representation of values on a display form:

fd.field('Status').control()._el().text()

Get information on how to get or set values of different types of fields from the following article: http://formsdesigner.blogspot.com/2013/04/getting-and-setting-sharepoint-form.html

Please, feel free to ask your questions in comments.

23 comments:

  1. Thank you for posting this article. I appreciate the foundational nature of it and the additional examples. Adding the internal name field to the designer is a great addition and will come in handy. I would love to see more foundational based blog posts that help give more detail about the Forms Designer API.
    thanks
    -schuess

    ReplyDelete
  2. I have a Choice field with allowed 'Fill-in' choices. What would be the code to make it required and validate? I tried code in a sample but it doesn't work for 'Fill-in' values.

    ReplyDelete
    Replies
    1. You can make a field mandatory in the field settings. But you can retrieve its value via JavaScript as well. Please, specify what kind of choice you use: drop-down, checkboxes or radio buttons.

      Delete
  3. It becomes mandatory only under certain conditions so I can't use field settings. I'm using drop-down.

    ReplyDelete
    Replies
    1. Ok, thanks. Please, try the following code:

      fd.onsubmit(function() {
      if (
      fd.field('Choice').control()._el().find('input[type="radio"]:eq(0)').prop("checked") && !fd.field('Choice').value() ||
      fd.field('Choice').control()._el().find('input[type="radio"]:eq(1)').prop("checked") && !fd.field('Choice').control()._el().find('input[type="text"]').val()
      ) {
      alert('Please, fill-in the Choice field.');
      return false;
      }

      return true;
      });

      Delete
  4. This comment has been removed by the author.

    ReplyDelete
  5. Hi Dmitry,

    We have a field that we want editable (Progress on a task) only if the current user is equal to the "Assigned To" field (Render is set to Client). What is the javascript code that we can use? Many thanks in advance.

    ReplyDelete
    Replies
    1. I'd recommend to use our groups functionality to create separate forms for assignees based on the condition:
      ContainsCurrentUser([Assigned To])
      http://spform.com/documentation/groups

      But if you still want to use JavaScript, you need to get the current user's login as demonstrated in the following post:
      http://formsdesigner.blogspot.com/2013/03/how-to-create-dynamic-forms-with-forms.html

      And compare it with the 'Assigned To' field value:
      http://formsdesigner.blogspot.com/2013/04/getting-and-setting-sharepoint-form.html

      Delete
    2. Hi Dmitry,

      Must have been a typo on my end but the ContainsCurrentUser works now for us, so agreed, that it is the easier approach to go with groups.

      Delete
  6. Hey Dmitry,

    Is there any way to conditionally make a lookup field mandatory? In my case, I have a lookup field that by default is "(None)" (no default value). I want the user to choose an option from the lookup drop-down, validating that "(None)" is not selected.

    I cannot simply make the field mandatory as I have some JavaScript in place for cascading drop-downs on these lookup fields, making them mandatory breaks the cascading code.

    Any help would be appreciated.

    ReplyDelete
    Replies
    1. Hi Mitch,
      Sure, you can check a field value in the onsubmit handler and require a user fill in it:

      fd.onsubmit(function() {
      if (fd.field('LookupField').value() == 0) {
      alert('Please, fill-in the lookup field');
      return false;
      }

      return true;
      });

      Delete
    2. where in the cascading script does this new script go? Thanks

      Delete
    3. HI - I have the same scenario as Mitch Gates where I need to add another condition to make a look up mandatory if an option is selected, the above existed, I am using a similar script as below:

      In the below example, how can I add another condition under on submit condition:

      function setRequiredFields() {
      if (fd.field('AssignedTo').value().dictionaryEntries.length != 0) {
      // Add asterisks
      fd.field('DueDate').titleRequired(true);
      } else {
      // Remove asterisks
      fd.field('DueDate').titleRequired(false);
      }
      }

      // Calling setRequiredFields when the user changes the assignment
      fd.field('AssignedTo').change(setRequiredFields);

      // Calling setRequiredFields on form loading
      setRequiredFields();

      // Custom validation
      fd.onsubmit(function () {
      if (fd.field('AssignedTo').value().dictionaryEntries.length != 0) {
      if (!fd.field('DueDate').value()) {
      alert('Please, fill in the Due Date field.');
      return false;
      }
      }

      return true;
      });

      Delete
  7. Hello Dimity,
    Do we use the same syntax fd.field('DueDate').show()/hide() for display forms or do we use fd.field? thank you

    ReplyDelete
  8. Hey Dmitry,

    How about looking up to see if a date field is populated (any value) on a display form? I'd like to hide the field on display form if no date is populated.

    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete
  10. Hi,
    I checked this with this Look Up field.
    My look up field name is Cases.
    When alert the valueof look field , it displays the ID not the value in the look up.

    alert(fd.field('Cases1').value());

    it prints Value as 1. But my exact value is Accident. 1 is its ID. How can I get the text?

    alert(fd.field('Cases1').text()); I tried this also. But it returns an error

    ReplyDelete
  11. Its working ...
    Thank You Dmitry :)

    ReplyDelete
  12. Hi !
    One of my required fields is managed metadata. Can you please help me with syntax? I was trying to use

    fd.field('FieldName').control()._el().find('.ms-taxonomy').value()

    fd.field('FieldName').control()._el().find('.ms-taxonomy').value() == ""

    fd.field('FieldName').control()._el().find('.ms-taxonomy').length !=0

    but the check is not done for this field.

    ReplyDelete
  13. I entered the code, and it is working properly. However, Both the starting field and the conditional fields are displayed in a tab. When I add the Jaascript, the tabs on the form are no longer appearing. Do I need to add something to have the tabs re-appear on the form again?

    ReplyDelete
  14. Hi, is it possible to hide a related items field while a lookup is empty, and then display the related items when the lookup if filled? The related items don't have a place for an internal name.

    Thanks.

    ReplyDelete