Monday 14 March 2016

Create dynamic list forms with jQuery and SP Designer

In an earlier post, I covered ways to make certain fields in a SharePoint list's input forms invisible, using JavaScript. But recently, I had a request to make input fields appear based on the user selecting a value from a picklist (drop-down list). With quite a bit of help from my colleague David T, I managed to figure out a way to create quite dynamic input forms for a SharePoint list using jQuery. This is how we did it.

You will need SharePoint Designer access in order to do this. If you don't have SharePoint Designer access then this method will not be possible for you.

So, this is what we'll end up with. When the user wants to create a new item, the NewForm.aspx page will come up looking like this:


Select a category and the dynamic input form will change.
The user will then select the category they want and the form will display differently, depending on the category chosen. In this case, the list was designed to hold Service Alerts and Planned Outages for IT systems within our company. Service Alerts happen on an ad hoc basis and don't have Start or End dates. 


This is how the form displays if you choose "Service alert".
But Planned Outages, by definition, will have a Start and an End date. Thus choosing Outage will reveal the fields for the Beginning and End of the Planned Outage.


However, Outages require an Start Date/Time and and End Date/Time ...
So, in the browser, set up your list with all the fields you need for either (or all, if you're having more than two choices) situation, and be sure to include a Category field as a Choice, so you can switch between the options in the NewForm.aspx. I started with an Announcements list, as this was essentially a news feed, and customised it slightly.


Here's the Announcements list with the added custom fields.
Make sure you have the columns in the order you want them to appear in the NewForm.aspx. If you don't, and you change the order later, this will affect the way the jQuery script works and you'll end up with a broken form.

Next, create a document library in your site, and name it "siteJS". In that, place a blank Notepad document, named "NewForm.js". You'll use this later to hold the script that controls the NewForm.aspx. 

Now, fire up SharePoint Designer and navigate to the Site where you created the List. In the Folder List, find the Lists section and expand it. Now find your target list, and expand that.


This is where all the out-of-the-box list forms are stored.
Find the NewForm.aspx under the List and open it. If you get an alert box asking if you want to check the page out, click Yes.

Now somewhere around line 15 in the code for the page, you should see a line that says:

<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">

Under that, you'll place your calls to the jQuery files. I prefer to store the jQuery files locally (as you can see from the paths in the screengrab). But you can use calls to the remote jQuery files if you want - a quick Google search will throw up a slew of examples.


This is what the code looks like in SP Designer. Make sure you put your JavaScript in the right place or the function won't work.
Once you done that, you can also add a call to your blank Notepad document you saved earlier.


Add the call to the text file that will hold your custom JavaScript. Relative pathways are fine.
Now, we're more or less ready to start on the real meat of this task. But first, you'll need to fetch the ID's of all the fields in the NewForm.aspx page. You can do this most effectively by bringing the page up in your browser then Viewing Source. Each field will have an ID associated with it. These IDs also reflect the order of the fields in the NewForm page. If you change the order of the fields, the IDs will change. Use Find to speed though the source code of the NewForm page.

It would save you time if you load each ID into the NewForm.js file as you go along, as this is where they'll end up. I prefer to use NotePad++ over regular Notepad, but either will do.  So, open the NewForm.js file and begin entering the field IDs as you copy them from the View Source window. To save yourself a little time, enter them in this format:

var trBody = $("#ctl00_m_g_6c609f0f_5ff0_49e3_b8da_0653643109f9_ctl00_ctl04_ctl04_ctl00_ctl00_ctl04_ctl00_ctl00_TextField").closest("tr");  // Field = Body Text

You're setting each field up as a variable, which you can use later in the process to control the visibility of the field. For text fields, the above format is fine, but date fields are trickier to manage, because date fields are made up of more than one component. So they require two variables to be set to manage them correctly. I set up the date fields variables like this:

var trExpires = $("#ctl00_m_g_6c609f0f_5ff0_49e3_b8da_0653643109f9_ctl00_ctl04_ctl08_ctl00_ctl00_ctl04_ctl00_ctl00_DateTimeField_DateTimeFieldDate").parent().parent().parent().parent().parent().parent().parent(); // Row = Expires 
var fncExpires = $("#ctl00_m_g_6c609f0f_5ff0_49e3_b8da_0653643109f9_ctl00_ctl04_ctl08_ctl00_ctl00_ctl04_ctl00_ctl00_DateTimeField_DateTimeFieldDate").closest("tr");  //Field = Expires

The first variable above controls the tables row that the field appears in, and the second variable controls the date function itself.

When you have all the field IDs added in this way, you're ready to add the rest of the scripting.

Before your list of field variables add this scripting:

$(document).ready(function() {
// *** START HIDE-SHOW FIELDS BASED ON "CATEGORY" DROP-DOWN SELECTION ***

// FORM FIELD VARIABLES
var selectList1 = $('#ctl00_m_g_6c609f0f_5ff0_49e3_b8da_0653643109f9_ctl00_ctl04_ctl01_ctl00_ctl00_ctl04_ctl00_DropDownChoice'); // Field = Select category

Commenting is good practice that helps you keep track of what's going on. Notice that I put the Category drop-down variable at the top of the listing of variables and named it slightly differently from the others.

Now ... below the list of variables, add this script:

//resets the form
function clearAll() {
  $('*[id*=5ff0_49e3_b8da_0653643109f9_ctl00_ctl04]:visible').each(function() {
      $(this).closest("tr").hide();
      });
      $(selectList1).closest("tr").show(); //show dropdown
      $(trStartDate).hide();  // Field = Start Date
      $(trEndDate).hide();  // Field = End Date
      $(trExpires).hide();  // Field = Expires Date
}
clearAll(); // hides all rows upon loading the page

This hides all the fields except for the Category drop-down. The line that begins $('*[id*= finds every field with an ID that contains "5ff0_49e3_b8da_0653643109f9_ctl00_ctl04" and the next line hides them all. The next four lines that begin with "$" shows the drop down and hides the table rows that contain the date fields.

Now, beneath the above script, add this:

//Category > Alert
function alert() {
clearAll(); // call the Alert form function
$(trCategory).show();  // Field = Category
$(trTitle).show();  // Field = Title
$(trStatus).show();  // Field = Status
$(trIncident).show();  // Field = Incident number
$(trBody).show();  // Field = Body text
$(trBodyTool).show(); // show text field options
$(trUpdate).show();  // Field = Update Test / Reason
// Next two lines required to make Expired date field show
$(trExpires).show();  // Row = Expires Date
$(fncExpires).show();  // Row = Expires Date
}

This block of script makes the required fields for the Alert category visible.

Next, add this script, which makes the other category (Outage) visible:

//Category > Outage
function outage() {
clearAll(); // call the Outage form function
$(trCategory).show();  // Field = Category
$(trTitle).show();  // Field = Title
$(trStatus).show();  // Field = Status
$(trIncident).show();  // Field = Incident number
$(trBody).show();  // Field = Body text
$(trBodyTool).show();  // show text field options
$(trUpdate).show();  // Field = Update Test / Reason
// Next two lines required to make Start date field show
$(trStartDate).show();  // Row = Start Date
$(fncStartDate).show();  // Row = Start Date
// Next two lines required to make End date field show
$(trEndDate).show();  // Row = End Date
$(fncEndDate).show();  // Row = End Date
// Next two lines required to make Expired date field show
$(trExpires).show();  // Row = Expires Date
$(fncExpires).show();  // Row = Expires Date
}

Finally, add the block of script that controls the whole function:

// calls function if selectlist1 is changed
$(selectList1).change(function() {
var funcCall = "";
var primarySelect = $(selectList1).val().toLowerCase().replace(/-|\s/g, '');
var funcCall = primarySelect;
//alert("Segment: "+ primarySelect +"\nRequestType: "+secondarySelect +"\nFunction Name: "+funcCall);
eval(funcCall+'()');
});

You don't need the Alert line. I was using this for testing a version of this script that had two different category drop-downs.

Finally, make sure the whole script block is closed by adding:

});

Save the file, and you're ready to test.

That should be it. Hope this helps someone.

No comments:

Post a Comment