Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Developing Custom Date Picker, People Picker and Choice (menu to choose from) using AngularJS/jQuery for SharePoint 2013

5.00/5 (7 votes)
2 Jan 2016CPOL5 min read 51.9K   863  
Creating Custom Date Picker, People Picker and Choice for SharePoint 2013 Apps or Content Editor Web Part using AngularJs/jQuery.

Introduction

When we need to create a custom form for adding new items or editing items in a list, we may be stuck at some fields like Date, People or Group and Choice. The reason behind is: users will likely have Date Picker, People Picker and Dropdown, Radio buttons or check boxes for these kind of fields. We cannot impose them a text input for these kind of fields that they will type the exact value to the text input and then save. If we do so, then it will be total horrible, I suppose. So, the aim of this article is: how can we create these controls for better UX of our users.

Initial Setup

Before we dive into this article, the following knowledge is required.

  1. AngularJs
  2. jQuery
  3. SharePoint 2013 REST API to List
  4. SharePoint 2013 and AngularJs

For demo purposes, I am assuming that we have a list called Task with the following fields:

Title Single line of text
DueDate Date and Time
AssignedTo People or Group
Status Choice

Put some values in Status field like Running, Closed and so on for testing purposes. Now, we need to download some libraries like:

  1. jQuery (download from official site or nuget)
  2. AngularJs (download from official site or nuget)
  3. jQuey UI (download from official site or nuget)
  4. ui-date directive (it will be helpful if you like to develop Date Picker using AngularJs)
  5. ngTagsInput (it will be helpful if you like to develop People Picker using AngularJs)
  6. bootstrap-tagsinput (it will be helpful if you like to develop Date Picker using jQuery)

For styling everything, bootstrap should be a proper choice. I am not showing here the steps for creating app or Content Editor Web Part, but in the attachment you will find a SharePoint Hosted App using AngularJs. In this article, I will just discuss the approaches. So before creating these controls, add the above libraries into your project.

Choice

For creating a choice input field, at first, we have to get Choice field values.

JavaScript
var choiceFieldEndPoint = "/_api/web/lists/GetByTitle('List Name')/
fields?$filter=EntityPropertyName eq 'Name of the Choice field'"

If you send a get request to the above URL, it will return all values of the Choice field. In AngularJs, you may write your HTML like the following:

HTML
<select class="form-control" 
        data-ng-model="vm.newTask.status"
        data-ng-options="status as status for status in vm.newTask.taskStatusOptions">
</select>

Now in the controller, get Choice field values from your service and bind it into vm.newTask.taskStatusOptions.

JavaScript
demoAppSvc.getTaskStatusOptions()
        .then(function (response) {
            vm.newTask.taskStatusOptions = response.d.results[0].Choices.results;
        });

If your Choice field allows multiple selections, then create check box group as shown below:

HTML
<label ng-repeat="status in vm.newTask.taskStatusOptions">
    <input type="checkbox"
           value="{{status}}"
           ng-checked="vm.selectedOptions.indexOf(status) > -1"
           ng-click="vm.toggleSelection(status)"> {{status}}
</label>

In controller, add the following code to make it work:

JavaScript
vm.selectedOptions = [];
vm.toggleSelection = function toggleSelection(status) {
                var index = vm.selectedOptions.indexOf(status);
                if (index > -1) {
                    vm.selectedOptions.splice(index, 1);
                } else {
                    vm.selectedOptions.push(status);
                }
            };

When you will save value to Choice field, write the following code if your Choice field does not allow multiple selections.

JavaScript
function saveTask(task) {
    var data = {
        __metadata: {
            'type': 'SP.Data.TaskListItem'
        },
        Status: task.status //It's a string
    };
    var url = "/_api/web/lists/GetByTitle('Task')/Items";
    return baseSvc.postRequest(data, url);
}

If Choice field allows multiple selections, then you have to pass an array of string.

JavaScript
function saveTask(task) {
    var data = {
        __metadata: {
            'type': 'SP.Data.TaskListItem'
        },
        Status: {
            results: task.selectedOptions //It's an array
        }
    };
    var url = "/_api/web/lists/GetByTitle('Task')/Items";
    return baseSvc.postRequest(data, url);
}

I am not showing here how baseSvc.postRequest has been created. I wrote other two articles about it using jQuery and AngularJs both.

How can we do that using jQuery, let's see it in action.

HTML
<select id="task-status"></select>

Now get Choice values by an Ajax request and populate dropdown as shown below:

JavaScript
function createStatusOptions() {
    $.ajax({
        url: _spPageContextInfo.siteAbsoluteUrl + choiceFieldEndPoint,
        type: "GET",
        headers: {
            "accept": "application/json;odata=verbose",
        },
        success: function(response) {
            var statusOptions = response.d.results[0].Choices.results
            var status = $('#task-status');
            $.each(statusOptions, function(index, value) {
                status.append($("<option>").attr('value', index).text(value));
            });
        },
        error: function(error) {
            alert(JSON.stringify(error));
        }
    });
}

If your choice field allows multiple selections, then create a checkbox group instead of select using jQuery what exactly I did using AngularJs above.

Date and Time Picker

In SharePoint, we can use jQuery UI date picker. It has the same version also in AngularJs. Let's start with AngularJs. Basically, I am recommending this. Before using it, please read the documentation.

HTML
<input class="form-control" data-ui-date="vm.dateOptions" data-ng-model="vm.newTask.dueDate" />

ui-date directive has been used here. You have to pass date option over it as shown below:

JavaScript
vm.dateOptions = {
    changeYear: true,
    changeMonth: true,
    yearRange: '1900:-0'
};

For saving this value into a Date and Time field, you have to convert it as ISOString.

JavaScript
function saveTask(task) {
    var data = {
        __metadata: {
            'type': 'SP.Data.TaskListItem'
        },
        Status: task.status,
        DueDate: new Date(task.dueDate).toISOString()
    };
    var url = "/_api/web/lists/GetByTitle('Task')/Items";
    return baseSvc.postRequest(data, url);
}

For jQuery, the following demonstrates the HTML and jQuery codes:

HTML
<input type="text" id="due-date">
JavaScript
$(function() {
    $("#datepicker").datepicker({
        changeYear: true,
        changeMonth: true,
        yearRange: '1900:-0'
    });
});

You can add more options that are available here.

People or Group Picker

For this control, I Googled a lot because we know that it's a very complex control. We need to show suggestions while typing in the input field, then remove user from input field and so on. Finally, I manage to find this and this. Basically, this directive allows us add tags in input field and it also shows suggestions while typing in. I have tried to make it as a People or Group Picker. Let's start with AngularJs step by step. First step should be getting suggestions while typing in. We can get suggetions from the following three endpoints.

JavaScript
var userSearchSuggestionEndpoint = "/_api/web/siteusers?$select=Id,
					Title&$filter=substringof('some character', Title)";

You can get search suggestion from the above URL which is very fine. But there is a problem: It will be a case sensitive search. I mean if you search with "Mo", it will return results that contains "Mo" in Title and will not return those that contains "mo".

JavaScript
var userSearchSuggestionEndpoint = 
"/_api/SP.UI.ApplicationPages.ClientPeoplePickerWebServiceInterface.clientPeoplePickerSearchUser";

The above endpoint is very powerful. It can search users from all directories. We can get suggestion by a POST request to it.

JavaScript
var data = {
    queryParams: {
        __metadata: {
            type: 'SP.UI.ApplicationPages.ClientPeoplePickerQueryParameters'
        },
        AllowEmailAddresses: true, 
        AllowMultipleEntities: false, 
        AllUrlZones: false, 
        MaximumEntitySuggestions: 50, 
        PrincipalSource: 15, 
        PrincipalType: 15,
        QueryString: "Your search string"
        Required: false, 
        SharePointGroupID: null, 
        UrlZone: null,
        UrlZoneSpecified: false, 
        Web: null,
        WebApplicationID: null
    }
}

See more about SP.UI.ApplicationPages.ClientPeoplePickerQueryParameters in MSDN

It also has a problem that it does not return People or Group Id in response. Id is needed while saving value to the People or Group field. But it returns LoginName as key in response and we can get Id from LoginName. See here.

Now the last endpoint I found so far seems to be helpful. It has no case sensitive or LoginName issue. It provides us everything what we need for creating a People or Group picker.

JavaScript
function getPeoplePickerSuggestion(searchKey) {
    var userSearchSuggestionEndpoint = "/_vti_bin/ListData.svc/UserInformationList?
		$select=Id,Name&$filter=substringof('" + searchKey + "',Name)";
    return baseSvc.getRequest(userSearchSuggestionEndpoint);
}

Now look at the directive I have used from here. Please read the documentation for more customization.

HTML
<tags-input ng-model="vm.newTask.assignedUsers"
                    display-property="Name"
                    placeholder="Pick User"
                    add-from-autocomplete-only="true"
                    replace-spaces-with-dashes="false">
            <auto-complete source="vm.getPeoplePickerSuggestion($query)"></auto-complete>
</tags-input>

Basically, you have to pass array as ng-model which will be responsible for all picked users and an another function that returns deferred results as suggestion. See my controller code.

JavaScript
vm.newTask = {
    assignedUsers: []
};
vm.getPeoplePickerSuggestion = function(searchString) {
    return demoAppSvc.getPeoplePickerSuggestion(searchString)
        .then(function(response) {
            var results = response.d.results;
            return results.filter(function(user) {
                return user.Name.toLowerCase().indexOf(searchString.toLowerCase()) != -1;
            });
        });
};

If your People or Group field does not allow multiple selections, then your code should look like the following:

JavaScript
function saveTask(task) {
    var data = {
        __metadata: {
            'type': 'SP.Data.TaskListItem'
        },
        Title: task.title,
        DueDate: new Date(task.dueDate).toISOString(),
        Status: task.status,
        AssignedToId: 'Id of the selected user'
    };
    var url = "/_api/web/lists/GetByTitle('Task')/Items";
    return baseSvc.postRequest(data, url);
}

Following is for multiple selections. You will need to pass an array of integers instead of a single integer.

JavaScript
function saveTask(task) {
    var data = {
        __metadata: {
            'type': 'SP.Data.TaskListItem'
        },
        Title: task.title,
        DueDate: new Date(task.dueDate).toISOString(),
        Status: task.status,
        AssignedToId: {
            results: [] // construct a array of user Id from selected users or groups
        }
    };
    var url = "/_api/web/lists/GetByTitle('Task')/Items";
    return baseSvc.postRequest(data, url);
}

For jQuery version, at first see this document and implement it in your own way.

HTML
<input type="text" id="people-picker" />
JavaScript
$('#people-picker').tagsinput({
    itemValue: 'Id',
    itemText: 'Name',
    typeaheadjs: {
        name: 'users',
        displayKey: 'Name',
        source: getPeoplePickerSuggestion() // this function returns user or group suggestions
    }
});

Conclusion

In the whole article, I gave you the high level idea. So, download my source code as well and start digging into that. Do not forget to give library references to your project. Otherwise, it will not work.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)