Pages

Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Sunday, November 30, 2014

SharePoint 2013: Client Side People Picker

Introduction

SharePoint 2013 introduced client side people picker which can also be used from SharePoint apps. As like other SharePoint controls, the new people picker control is not well documented. I’ve reversed engineered the client side people picker control and tried to put some light in this dark area.

 

Functions

In the code snippet below, you can use user login id, email address as the key resolve the user in people picker.

Add Unresolved User

If you have user login Id or email address you can use them as key to add the user in people picker as shown below. By passing the ‘true’ as the last parameter in AddUnresolvedUser means, people picker will use the key to query the user details from server.

var peoplePicker = this.SPClientPeoplePicker.SPClientPeoplePickerDict.peoplePickerDiv_TopSpan;
var usrObj = { 'Key': loginIdOrEmail };
peoplePicker.AddUnresolvedUser(usrObj,true); 

 

Add users or show auto suggest

If you would like to show auto suggest box using javascript, you can use the following code snippet. if you pass true in the second parameter for AddUserKeys function, people picker will show the auto-suggest box, otherwise it’ll try to resolve the user. In case of passing true, you can pass any search-text and people picker will show the autosuggest based on the input search text.

var peoplePicker = this.SPClientPeoplePicker.SPClientPeoplePickerDict.peoplePickerDiv_TopSpan;
peoplePicker.AddUserKeys(loginIdOrEmailOrSearchText, false); //true shows the auto-suggest box, false resolve the user

 

Show Error Message

You can show an error message using code snippet as shown below:

SPClientPeoplePicker.SPClientPeoplePickerDict.peoplePickerDiv_TopSpan.ShowErrorMessage('Error message...')

As as result you will the following error message:

image

 
IsEmpty

You can check if People picker is empty using the following code snippet. For your information, if there’s any text in the people picker textbox IsEmpty will return false. Basically it’ll check if there’s any resolved or unresolved text in the people picker editor textbox.

SPClientPeoplePicker.SPClientPeoplePickerDict.peoplePickerDiv_TopSpan.IsEmpty()

 

HasResovledUsers

You can check if there’s any resolved user by using the following code snippet. If there’s at least one resolved user in the editor, it’ll return true (even if you have resolved or unresolved user, it’ll return true). The different between IsEmpty and HasResolvedUsers is that IsEmtpy will check if there’s any text in the people picker text box. Whereas HasResolvedUsers check if there’s any resolved users in the editor.

SPClientPeoplePicker.SPClientPeoplePickerDict.peoplePickerDiv_TopSpan.HasResolvedUsers()

 

Get Current Editor Value

If you can get the editor’s current unresolved value by using the following code snippet:

SPClientPeoplePicker.SPClientPeoplePickerDict.peoplePickerDiv_TopSpan.GetCurrentEditorValue()
The code snippet will not return any resolved value in the editor rather unresolved value

 

Enable/disable

You can use the following code snippet to enable or disable the people picker editor. However setting in disable state, prevent adding any new users in the editor but still user can remove existing resolved users by clicking cross (x) sign next to resolved user.

SPClientPeoplePicker.SPClientPeoplePickerDict.peoplePickerDiv_TopSpan.SetEnabledState(false)

I think the easiest way to disable completely is to hide the delete (cross X) icon. You can do so using the following script:

$('.sp-peoplepicker-delImage').hide()

 

Remove Resolved Users

There’s no direct support for removing resolved users using the people picker API. I’ve put the following snippet below which I think should work and removes a user by using selected user’s key.

var peoplePickerId = 'peoplePickerDiv';
var peoplePicker = this.SPClientPeoplePicker.SPClientPeoplePickerDict[peoplePickerId + '_TopSpan'];

//get selected users and select the second user (index 1) to remove
var selectedUsers = peoplePicker.GetAllUserInfo();
var userToRemoveKey = selectedUsers[1].Key; 

var resovledListElmId = peoplePicker.ResolvedListElementId;
var elementToRemove = '';
$('#' + resovledListElmId).children().each(function(index, element) {
    if (element.id.startsWith(peoplePickerId + '_TopSpan_' + userToRemoveKey + '_ProcessedUser')) {
        elementToRemove = element;
        return false;
    }
});

peoplePicker.DeleteProcessedUser(elementToRemove);

Simply, for all resolved users people picker creates span elements with id in the form ‘{peoplepickerid}_TopSpan_{userKey}_ProcessedUser_{index}’. So if we know the user key we want to remove, we can find the corresponding resolved user’s span element. Then finally people picker’s ‘DeleteProcessedUser’ method is used to remove the selected user.

 

Events

There’s few events available in the people picker control as described below. All of these events will get two parameters – first one is the people picker id and second parameter is an array of selected users.

On Control Value Changed

This event will be fired if anything changes, like if user type text, user select a user from auto-fill list or user removes selected user. You can easily hook into the event as shown below:

    this.SPClientPeoplePicker.SPClientPeoplePickerDict.peoplePickerDiv_TopSpan.OnValueChangedClientScript=function (peoplePickerId, selectedUsersInfo) {
        console.log('inside OnValueChangedClientScript');
    };
 
On Control Resolved Users Changed

This event will be fired as soon as an user is resolved ( i.e., a resolved user is selected). You can hook into the event as shown below:

    this.SPClientPeoplePicker.SPClientPeoplePickerDict.peoplePickerDiv_TopSpan.OnUserResolvedClientScript = function (peoplePickerId, selectedUsersInfo) {
        console.log('inside OnUserResolvedClientScript');
    };

 

On Control Validate

The people picker control validate it’s values – sometimes when values are changed or you can fire the event by yourself by calling the method ‘Validate’. You can hook into the event as shown below:

    this.SPClientPeoplePicker.SPClientPeoplePickerDict.peoplePickerDiv_TopSpan.OnControlValidateClientScript = function (peoplePickerId, selectedUsersInfo) {
        console.log('inside OnControlValidateClientScript');
    };

 

Conclusion

I’ve tried to explain the API but you all are welcome to provide any feedback or suggestions or enhancements in this unofficial documentation.

Thursday, February 17, 2011

IE9 and User Agent String

With new IE in market the web developer community has been in the danger of supporting another extra browser. Recently we have found our application is in problem with IE9. We have a client application that put some data in User Agent Post platform value in registry. From IE6 to IE8 the post platform value was passing from server to client in UserAgent. So basically we had put some value in UserAgent Post Platform in registry from a client application and from our web application we had found the value (as the post platform values are passed in User Agent from IE6 to IE8). But with IE the post platform values are not passed with User Agent automatically.

 

User Agent and Pre/Post Platform value

If you are interested on how the Pre and Post Platform values works with User Agent you can visit the MSDN link: http://msdn.microsoft.com/en-us/library/ms537503%28VS.85%29.aspx

 

IE9 introduces Default User Agent and Extended User Agent

In IE9 the user agent has been divided into two parts:

Default User Agent (UA): IE9 by default will not send the pre/post platform values from client to server. So if you have some values in client’s pre and post platform then your web site will not get those values (from pre and post platform) as IE9 will not pass those values to server. However IE6 to IE8 will work as usual (i.e., pass those pre and post platform values) .

Extended User Agent (UA): So the question is if IE9 doesn’t send the pre and post platform values by its own then how can we access the platform values? The answer to this question is simple but implementation is tricky. You can get the pre and post platform values on client side only with JavaScript by accessing Navigator.UserAgent. So IE9 will not take the responsibility of passing Extended UA from client to server. You need to do it by your own. You need to read the extended UA by javascript on the client side and need to pass to the server by using Hidden control or any other way.

 

How to get the Pre/Post platform vales IE9?

By following javascript code you can get the pre/post platform values on client side.

<script type="text/javascript">
    alert(navigator.userAgent);
</script>

You can get more details on this on MSDN IE blog: http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx

Monday, May 31, 2010

SharePoint 2010: The current page has been customized from its template. Revert to template.

The annoying warning ‘The current page has been customized from its template. Revert to template’ comes up in page when a page is customized . For example you have created a page mypage.aspx with master page ‘default.master’. Now if you change the master page to custom.master then you will find the message ‘The current page has been customized from its template. Revert to template’. If you make any change to the page then the page will be customized and you will find a message. I think by myself that the message is pretty much annoying and  concerning to any SharePoint developer/administrator. Till now there’s no known settings to stop the message.

 

First Solution (extreme solution):

The first solution I have found in sharepoint forum. The solution is to comment out the section in master page which shows the message. As per the solution, find the following section in master page and comment it out.
<div id="s4-statusbarcontainer">
    <div id="pageStatusBar" class="s4-status-s1">
    </div>
</div>

So the solution in this case to hide div permanently from the master page. But the problem here might be that if the same div is used to show another useful message then user will not find the message.

 

Second Solution (soft solution):

Another solution might be not to remove the div from master page. Rather to hide/show the message on the client side on page load. Remember the default master page is V4.master, so in case of default master page, use V4.master instead of Default.master. The logic is:
  1. set the div’s visibility to ‘none’ as shown below. So the div is not visible by default
    <div id="s4-statusbarcontainer" style="display:none">
        <div id="pageStatusBar" class="s4-status-s1">
        </div>
    </div>
  2. Run a script on page load which will check the message inside the div. And based on the message the div will be visible or not. I have put the following script in the master page’s head section. The script check if the display message is not ‘The current page has been customized..’ and if not so then display the message:
    <head>
    ...........................
    
    <script type="text/javascript">
    ExecuteOrDelayUntilScriptLoaded(hideWarning, "sp.js");
    function hideWarning() {
        var statusbarContainer = document.getElementById('s4-statusbarcontainer');
        if (statusbarContainer != null) {    
            var messageSpan = document.getElementById('status_1_body');
            if (messageSpan != null) {
                if (messageSpan.innerHTML.indexOf('The current page has been 
                                  customized from its template.') == -1)
                    statusbarContainer.style.display = 'inline';
            }
        }
    }
    </script>
    </head>
    In the above code snippet, the method ‘ExecuteOrDelayUntilScriptLoaded’ ensures that the method hideWarning is not invoked until the base SharePoint JavaScript file sp.js is loaded.

My final judgment is that SharePoint should provide a settings from site collection/web level to enable/disable the warning. sometimes admin wants to accept the warning but don’t want to show it to end users. Hope some SharePoint team will heed into this. Till that day we, sharepoint developer, need to find a way out. Hope someone out there will get a better idea.

Tuesday, June 23, 2009

JavaScript Event

How to handle the event object?

JavaScript event handling is a bit incompatible between IE and other browsers. So if you have not clear picture of what the difference then it'll be difficult to write proper event handling code in javascript that will work swimmingly in different browsers. By default when an event will be fired and you have bound some event handler code to handle that event, the event object should be passed automatically to the function. Though all browser works this way, Surprisingly the IE browser works different way. IE doesn't pass the object directly rather you need to access it through window object. So in an event handler you need to write code in any of the two ways as shown below to get the event object:

        function eventHandler(e) {

            e = e || window.event;

        }

        function eventHandler(e) {

            if (!e) {

                e = window.event;

            }

        }

In the first event handler, the OR operator (||) is used to take e if e is not null. If e is null then window.event is used (for IE). In the second handler above the shorthand OR is written in more elaborately.

 

How to prevent the default behavior of an event?

Now let think you have an anchor and when user will click on the anchor you need to do some validation on client side and if the validation is passed then you want the anchor url to be navigated. If validation is not passed then you want to prevent the user to move the url. So you need to write javascript code to prevent the anchor's default behavior (will navigate to the href defined). You can do so by calling event object's preventDefault method. But IE doesn't support preventDefault. Rather you need to use returnValue.

 

        function eventHandler(e) {

            e = e || window.event;

            // Prevent the default action of an event

            if (e.preventDefault) {

                //For Non-IE

                e.preventDefault();

            } else {

                //For IE

                e.returnValue = false;

            }

        }     

 

 

Event bubbling and how to prevent it?

When an event is fired in an element in javascript DOM and the event is not handled in the element then the event is passed to the parent of the element. If the event is not handled in the parent element the event is propagate to the upper. So this is called event bubbling. There's another model called event capture. You can read more details about event model from here. For all browsers except IE we can call the event object's stopPropagation method to prevent event bubbling. But for IE, we need to set cancelBubble to true. The code snippet below shows how we can write a cross-browser compatible javascript code.

            // Stop event from bubbling up: 

            if (e.stopPropagation) {

                // W3C compliant browsers: 

                e.stopPropagation();

            } else {

                // IE: 

                e.cancelBubble = true;

            }

 

Filter event by checking the source of the event

Sometimes we need to check the source of the event. We can do so applying event delegation technique. For example we have a table of data and whenever a row is clicked, we need to handle the event. Rather than writing event handler for each of the tr/td we can write an common handler and check the source element in that event handler and take necessary actions. The technique is called event delegation. With Event delegation we can apply event handler to an element and using this as a basis for manipulating its children element. In all browsers except IE you can access the source element of the event by accessing target property of the event. For IE you need to use srcElement.

            var targetNode = e.target || e.srcElement;

            // Test if it was a TR that was clicked: 

            if (targetNode.nodeName.toLowerCase() === 'tr') {

               

            }

 

Event delegation relies on event bubbling. The above code wouldn't work if the bubbling was halted before reaching the 'table' node.

Saturday, March 21, 2009

prevent Concurrent Asynchronous postback

By default, when a page makes multiple asynchronous postbacks at the same time, the postback made most recently takes precedence. For example, user clicks on a button which generates asynchronous postback. Now before the response of the asynchronous postback comes to client, user click another button which also generates asynchronous postback. In this case the response of the first button's event will be discarded by the client. So user will not find the update information as the  the first button's event.

So in this case there are two approaches to handle the situation:

1. When a asynchronous postback is in progress, user will not be able to initiate another postback.

2. if user initiates multiple asynchronous postbacks then we can queue the requests and send them one by one. But in this case timeout is a problem.

 

Approach 1: Prevent users to initiate multiple asynchronous postbacks:

    <script type="text/javascript">

        var Page;

        function pageLoad() {

            Page = Sys.WebForms.PageRequestManager.getInstance();

            Page.add_initializeRequest(OnInitializeRequest);

        }

        function OnInitializeRequest(sender, args) {

            var postBackElement = args.get_postBackElement();

            if (Page.get_isInAsyncPostBack()) {

                alert('One request is already in progress.');

                args.set_cancel(true);

            }

        }    

    </script>

In the above code block, we are hooking OnInitializeRequest event on every request's initialize event. In the initialize event handler (OnInitializeRequest) we are checking if an asynchronous request is in progress. if so then canceling the current request.

 

Approach 2: Queue asynchronous requests

Andrew Fedrick describes in his blog how to queue asynchronous requests here.

For simplicity, my recommendation is to prevent users to initiate multiple asynchronous requests.

 

Some Useful Links

http://msdn.microsoft.com/en-us/library/bb386456.aspx

http://www.dotnetcurry.com/ShowArticle.aspx?ID=176&AspxAutoDetectCookieSupport=1

http://weblogs.asp.net/andrewfrederick/archive/2008/03/27/handling-multiple-asynchronous-postbacks.aspx

http://www.codedigest.com/CodeDigest/41-Cancel-Multiple-Asynchronous-Postback-from-Same-Button-in-ASP-Net-AJAX.aspx

Tuesday, February 3, 2009

Page Method (Web Service methods in aspx page)

Page method is web service method added to aspx page rather than in asmx page. Sometimes we just want a web service functionality for a single page and we don't want to use any separate web service for the shake of complexity. We need such web service like functionality when use ajax. For example when we use dynamic populate control from ajax control toolkit we need to populate data dynamically. In that case the populate control needs data from web service but if we don't want to introduce web service for keeping our system simple we can implement the code for web service in the aspx page. To declare a page method web service use the following script in the aspx page.

<head runat="server">

<script runat="server">

    [System.Web.Services.WebMethod()]

    [System.Web.Script.Services.ScriptMethod()]

    public static string Test()

    {

        return "sohel rana";

    }

</script>

    <title>Page Title</title>

</head>

You can also declare the web service method in code behind file. In that case the method will be public and static and should be marked with WebMethod attribute as shown below

    protected void Page_Load(object sender, EventArgs e)

    {

 

    }

 

    [System.Web.Services.WebMethod()]

    [System.Web.Script.Services.ScriptMethod()]

    public static string Test()

    {

        return "your data";

    }

Now we need to know how we'll call this page methods using javascript. There are two ways to call the page mehtod Test. One is call via PageMethods and another is to use ajax control toolkit's built-in feature. To use PageMethods you need to set the ScriptManager's EnablePageMethods to true as shown below.

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" />

This will generate a PageMethods javascript object with which we can call the Test method as shown below:

    <script type="text/javascript">

        function CallWebServiceMethod() {

            PageMethods.Test(complete);

        }

        function complete(val) {

            alert(val);

        }

    </script>

<input type="button" value="Click" onclick="CallWebServiceMethod()" />

 

The second option will be to use Ajax Control Toolkit's built-in feature. Obviously this option will be available if you are using Ajax Control Toolkit. For example in the following code snippet the Dynamic Populate extender control will call the service method Test and on completion of invocation of Test method the control will update the panel with id p1.

 

            <cc1:DynamicPopulateExtender ID="dpe" runat="server" ServiceMethod="Test" TargetControlID="p1">

            </cc1:DynamicPopulateExtender>

        <asp:Panel ID="p1" runat="server"></asp:Panel>

But my personal opinion is that may be the Page method should not use as I'm skeptical about the performance. May be I need to dig more on performance.

Monday, January 5, 2009

Add your own script in asp.net validation script when page is submitted

When we create a web page and use asp.net validator asp.net creates some javascript functions which is used to validate controls. Asp.net uses Page_ClientValidate() function to validate client side validators. Now if you want to do something before submitting this page (so if the page is valid to submit and all validator controls are vlaid) then you can do so by calling the Page_ClientValidate() method by yourself. Let assume the script below:

    <script type="text/javascript">

        function doSomethingBeforeSubmitting() {

            var isPageSubmitting = Page_ClientValidate();

            if (isPageSubmitting) {

            //write your code to do before page submitting.

            }

        }

    </script>

 

Now you can call this method on the client click event of an asp server side button as shown below:

<asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClientClick="return doSomethingBeforeSubmitting();" />

 

May be till now to you have got nothing special. Let's make the things a bit interesting. Now lets say the page we are going to submit will take too long to submit. In that case to prohibit user to click on the submit button twice we need to disable the submit button when the page is going to be submit. Remember we don't need to disable the button if the page is not submitting because of asp.net validator control's validation failed. we can write this code as shown below:

 

    <script type="text/javascript">

        function doSomethingBeforeSubmitting() {

            var isPageSubmitting = Page_ClientValidate();

            if (isPageSubmitting) {

                var submitButton = document.getElementById('<%=btnSubmit.ClientID %>');

                submitButton.disabled = true;

            }

        }

    </script>

 

Now lets say after submitting the page the user is redirected to another page. If the user click on browser's back button to come back to this page then user may get the submit button disabled. To get rid of this problem you may think that we may write script on document's onload event to enable the button again. But when you come to page clicking browser's back button you may not get this event fired. In IE this event will be fired but in Firefox and others this event is not fired. So what you are going to do?

To get rid of this problem I had found a solution. I had written script on document's onunload event. I had enabled the button on this unload event. So before submitting the page the button was enabled again. So if user go back to the page using browser's back button he'll get the button enabled as I had already enabled the button before leaving the page. Here's the code block:

 

    <script type="text/javascript">

        window.onunload = "EnableSubmitButton";

        function EnableSubmitButton() {

            var submitButton = document.getElementById('<%=btnSubmit.ClientID %>');

            submitButton.disabled = false;       

        }

        function doSomethingBeforeSubmitting() {

            var isPageSubmitting = Page_ClientValidate();

            if (isPageSubmitting) {

                var submitButton = document.getElementById('<%=btnSubmit.ClientID %>');

                submitButton.disabled = true;

            }

        }

    </script>

 

Here in the above script I have registered EnabledSubmitButton method on window's onload event by the following:

window.onunload = "EnableSubmitButton";

 

So I did it in reverse way, rather than enabling the button on window's onload event, I enabled the button on button onunload event.