CMS

Autocomplete to the rescue when you need Sitecore paths in forms

One of Sitecore’s best features is its logical tree structure for storing content. Users are easily able to refer to any item in the tree using the “path” including it and all of its parent items. This is great when users need to be specific about a piece of content.

When developing applications for use in Sitecore it’s often tempting to use regular .NET development paradigms (instead of Sitecore’s SPEAK language). However, there is one big downside to this approach: you can’t use Sitecore’s regular content field controls that make it easy to pick a specific item from the content tree. The usual solution to this type of problem is to use a text field where users enter the item’s path.

That approach, however, is an option prone to errors: either by users copying and pasting incorrectly or by users typing the wrong thing in. Yes, you can catch these errors in validation, but wouldn’t it be better to prevent them in the first place?

I recently had this very situation come up and came up with a solution that allows you to give users accurate paths from Sitecore to choose from without using the Sitecore UI components. The trick was to use regular form autocompletion to populate the text box in question with known paths generated by the CMS. All a user has to do to use the field now is to start typing the name of the item in question and choose its path from the list presented.

I accomplished this on this form I was developing using a three part strategy:

  1. Setup autocomplete for the field.
  2. Populate the autocomplete options.
  3. Validate the user input.

If you’re doing this yourself, the particulars of your implementation will likely differ, but the approach should stay the same.

Enabling autocomplete for the Form Field

Enabling autocomplete was done using some Javascript that, quite honestly, I found online at W3Schools. The provided code was not exactly what I needed but it was a very good start. The sample code performed a true auto complete while I wanted users to be able to start typing any part of a path to find the options they were looking for.

Fixing this was a simple matter of replacing the logic that looked at the beginning of possible options and replaced it with logic looking inside the potential strings:

if (arr[i].substr(0, val.length).toUpperCase() == val.toUpperCase()) {

became

if (arr[i].toUpperCase().includes(val.toUpperCase())) {

I also added a script to highlight the found values in the autocomplete options and replaced the W3Schools highlighting logic with it. This slightly improves the usability of the form field.

b.innerHTML = highlight(arr[i], val)

instead of

/*make the matching letters bold:*/
b.innerHTML = "<strong>" + arr[i].substr(0, val.length) + "</strong>";
b.innerHTML += arr[i].substr(val.length);

I like to write my Javascript directly on the page that I’m working on and then move it into the main Javascript file when I’m happy with it, so that’s what I did.

With all that done, you then enable autocomplete on the form field in your C# view. Yes, I could’ve used jQuery for this, but I didn’t want to mess with the working W3Schools code when I didn’t have to! The code below will enable autocomplete on the “content-path” form field and use the values from the contentpaths array. We’ll populate that array next.

@{
	string ContentPaths = "";
	foreach (string ContentPath in Model.ContentPaths)
	{
		ContentPaths += String.Format("\"{0}\",", ContentPath);
	}
}
<script>
	var contentPaths = [@Html.Raw(ContentPaths)];
	autocomplete(document.getElementById("content-path"), contentPaths);
</script>

Populating the autocomplete options with Sitecore paths

This is the important part of the process: getting valid Sitecore paths into that array. This was easily done in my controller by populating a list on the Viewmodel object with valid paths for the field. In this case, the underlying content item lets admins pick a place in the tree to begin populating the field with. Another important usability thing is in place here too: removing the parts of the Sitecore path that are not relevant to users when picking the right path (you know, the parts like “/Sitecore/Content” that are going to be on all paths). You’ll want to put this back in when you’re actually using the form data.

Here’s the controller code:

string RemovedPathSubString = "/sitecore/content"
var ContentPaths = new List();
var ContentPathsField = (Sitecore.Data.Fields.LinkField)Sitecore.Context.Item.Fields[Templates.MyFormPageFields.Fields.ContentRoot];
var ContentPathsRoot = ContentPathsField.TargetItem;
if (ContentPathsRoot != null)
	{
	var ContentItems = ContentPathsRoot.Axes.GetDescendants();
	foreach (Item ContentItem in ContentItems)
		{		
ContentPaths.Add(ContentItem.Paths.Path.Replace(RemovedPathSubString, ""));
		}
}
viewModel.ContentPaths = ContentPaths;

And the code in the view cshtml file. It’s important to use the Html.Raw() helper, otherwise all your handy punctuation that makes the string look like a Javascript array will get escaped (thanks .NET!).

@{
	string ContentPaths = "";
	foreach (string ContentPath in Model.ContentPaths)
	{
		ContentPaths += String.Format("\"{0}\",", ContentPath);
	}
}
<script>
	var contentPaths = [@Html.Raw(ContentPaths)];
	autocomplete(document.getElementById("content-path"), contentPaths);
</script>

Validating Entries

We all know users cannot be trusted so form entries must be validated. By populating the contentPaths array with “good” values, it is then very easy to write client-side validation of the field values by checking the submitted value against the entries in that array and throwing an error if it doesn’t match. This should catch situations where users try some wishful thinking for paths that don’t exist (or, more likely, simply hit the wrong key with the cursor in the field).

About The Author