Monday, July 15, 2013

SharePoint JSGrid. Creating custom Widgets

Hi! There are not so many info all over the Internet explaining how to use complex features of SharePoint 2010 JSGrid control, such as custom prop types, display controls and widgets. In this post we’ll show how to create custom prop type and attach custom widget to it. In addition, you’ll see the way to handle data validation for columns of such types.

Creating custom Prop Type

First of all, we should create custom property type. The Init method of JSGrid controller is the place to do that. The easiest way is to derive new type from existing one. We’ll use standard String prop type:
this._MyCustomPropType = createMyCustomPropType('String');
function createMyCustomPropType(basePropType) {
            var newPropType = SP.Internal.JS.object(basePropType);
            newPropType.ID = 'MyCustomPropType';
            newPropType.BeginValidateNormalizeConvert = function (recordKey, fieldKey, newValue, bIsLocalized, fnCallback, fnError) {
                if (!newValue) {
                    fnCallback({ isValid: false, dataValue: newValue, normalizedLocValue: newValue, errorMsg: 'Value is not set.' });
                } else {
                    $.ajax({
                        url: 'some_validation_url',
                        data: {
                            value: newValue
                        },
                        success: function (result) {
                            fnCallback(result);
                        },
                        error: function (request) {
                            fnCallback({ isValid: false, dataValue: newValue, normalizedLocValue: newValue, errorMsg: request.responseText });
                        }
                    });
                }
            };
            newPropType.widgetControlNames = ['MyCustomWidget'];
            return newPropType;
        }

The ID property should be unique to identify the prop type between others.

BeginValidateNormalizeConvert method contains the validation logic. In our case we make the field required and add server-side validation (jQuery ajax call in our case). If you don’t need a delayed validation, you can directly call fnCallback function which comes as an argument of validation fuction.

It is important to set widgetControlNames property of newly created prop type to the same value as the ID of the widget you’d like to use for editing data. Please pay attention that this is not the ID itself, but rather an array containing this ID.

Registering custom Prop Type

When the prop type is created, it should be registered in JSGrid infrastructure. You'll have to specify both edit and display controls for the prop type. In our case we use OOB Text display control and EditBox edit control. The code:

SP.JsGrid.PropertyType.RegisterNewCustomPropType(this._MyCustomPropType,
            SP.JsGrid.DisplayControl.Type.Text, SP.JsGrid.EditControl.Type.EditBox, this._MyCustomPropType.widgetControlNames);

Creating custom Widget

Now you can create and register the widget control itself:

SP.JsGrid.PropertyType.Utils.RegisterWidgetControl(
            'MyCustomWidget',
            function (gridContext) {
                return new MyCustomWidget(gridContext);
            },
            []);
MyCustomWidget = function (gridContext) {
    this.SupportedWriteMode = SP.JsGrid.EditActorWriteType.DataOnly;
    this.SupportedReadMode = SP.JsGrid.EditActorReadType.DataOnly;
    var self = this;
    this.cellContext = null;

    this.Dispose = function () {
    };
    this.GetIcon = function () {
        return new SP.JsGrid.Image('../images/editheader.png').Render('');
    };
    this.OnValueChanged = function () {
    };
    this.Expand = function () {
        var options = {
            allowautoresize: false,
            allowmaximize: false,
            title: 'Pick Resource',
            width: 600,
            height: 400
        };

        SP.UI.ModalDialog.showModalDialog ({
            url: 'some_url',
            args: { },
            dialogReturnValueCallback: function (oDialogResult, oRet) {
                if (oDialogResult) {
                    self.cellContext.SetCurrentValue({ data: oRet.Guid, localized: oRet.Name });
                    self.cellContext.NotifyEditComplete();
                }
                self.Collapse();
            }
});
    };
    this.Collapse = function() {
        this.cellContext.NotifyCollapseWidget();
    };
    this.BindToCell = function (cellContext) {
        this.cellContext = cellContext;
    };
    this.Unbind = function () {
    };
};

The most important part in the widget definition is Expand method. In our case we open modal dialog and use the data returned when the dialog is closed.

The only thing left is to specify the ID of new prop type when the grid data are being built and serialized. PropertyTypeId field of GridField should be used for this purpose.

4 comments: