I get a lot of requests lately to share my example for a simple generic PageComponent JavaScript object (class) that can handle ribbon events.
The was it works is simple, I will probably blog about it in more details later on but in a nutshell there are 3 client side objects that are involved with the ribbon work on the page:
1. PageManager
One object that have a collection of all PageComponent objects for the CommandDispatcher to be able to work with them
2. CommandDispatcher
The only object that handles ribbon events. Any action that happens on the ribbon sends the CommandName of the associated event to the CommandDispatcher. In turn, the CommandDispatcher goes to all available PageComponent objects and ask each one of them if the can handle that command, and if they do – it asks them to handle the command, thus invoking their command handlers.
3. Collection of PageComponent objects
PageComponent object are client side script classes that inherit from CUI.Page.PageComponent (yes, JavaScript class that inherits from a base class!). They declare a collection of CommandName strings of commands they know how to handle and are sometimes associated with a control on the page (web part, field control etc), and when ever such a command is invoked the CommandDispatcher asks them if they can currently handle that command, and if yes – it asks them to handle it. There is no limit to the number of PageComponent objects on the page, or to the number of PageComponent that handles a specific commands. You can also handle commands from OOB ribbon controls, and does not have to be the creator of that ribbon control.
Note: If a command has no PageComponent that can handle it, the control associated with it will be disabled (button, group) except for a tab that does not have to have a command associated with it.
Here is an example of a page component that I start from, feel free to take it and use it, it is very similar to the example available on MSDN with a bit more explanations:
//See documentation in page component: http://msdn.microsoft.com/en-us/library/ff407303.aspx
//TODO: choose namespace like you would in .NET applications. Use company name, project name and module name to avoid conflicts
Type.registerNamespace('Company.Project.Ribbon.PageComponent');
//Create the object
Company.Project.Ribbon.PageComponent = function (PageComponentId) {
this._pageComponentId = PageComponentId;//keep record of associated control (web part, field control, etc..) that is active in this current instance of page component.
//Initializes the base type CUI.Page.PageComponent (Base class is associated through Type.registerClass(Type, BaseType)
Company.Project.Ribbon.PageComponent.initializeBase(this);
}
//Declate it's prototype
Company.Project.Ribbon.PageComponent.prototype =
{
//This will be initialized by caller (web part, field control etc)
_pageComponentId: "PageComponentIDHolder",
getId: function () {
return this._pageComponentId;
},
init: function () {
//Create a list of commands that your page component can handle (JSON string array).
//TODO: edit this collection, these are the command names you wish to handle from the ribbon controls you are listening to.
this._myCommandList = ['Company.Project.Ribbon.PageComponent.CMD1',
'Company.Project.Ribbon.PageComponent.CMD2',
'Company.Project.Ribbon.PageComponent.CMD3'];
//Create an array of methods used to handle commands passed to the page component.
//Use Function.createDelegate to keep current class instance (this) when the method is called.
//TODO: add handler per command in this._myCommandList. Later on - you will have to create the actual script handler method.
this._myHandledCommands = {};
this._myHandledCommands['Company.Project.Ribbon.PageComponent.CMD1'] = Function.createDelegate(this, this.CMD1_Handler);
this._myHandledCommands['Company.Project.Ribbon.PageComponent.CMD2'] = Function.createDelegate(this, this.NotImplemented);
this._myHandledCommands['Company.Project.Ribbon.PageComponent.CMD3'] = Function.createDelegate(this, this.NotImplemented);
},
getFocusedCommands: function () {
return this._myCommandList; //return supported commands collection
},
getGlobalCommands: function () {
return this._myCommandList; //return supported commands collection
},
canHandleCommand: function (commandId) {
//TODO: In our logic, if there is a handler we can handle the command.
//But you might have more logic here, like: if commandId = DeleteItem - can handle only if there is 1 item selected.
var canHandle = this._myHandledCommands[commandId];
if (canHandle)
return true;
else
return false;
},
handleCommand: function (commandId, properties, sequence) {
//Handle the command - simply getting the command handler (delegate) form the hash table and invoking it.
return this._myHandledCommands[commandId](commandId, properties, sequence);
},
isFocusable: function () {
return true;
},
//=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
//=~=~ CUSTOM PAGE COMPONENT LOGIC STARTS HERE ~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
//=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
// This is where you should add your custom script hadler methods.
// TODO: Implement each handler in this._myHandledCommands
//
CMD1_Handler: function (commandId, properties, sequence) {
alert("Handling CMD1_Handler");
},
NotImplemented: function (commandId, properties, sequence) {
alert("This command was not implemented yet");
}
//
//
//
//=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
//=~=~ CUSTOM PAGE COMPONENT LOGIC ENDS HERE ~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
//=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~=~
}
//This makes our object inherit of CUI.Page.PageComponent
Company.Project.Ribbon.PageComponent.registerClass('Company.Project.Ribbon.PageComponent', CUI.Page.PageComponent)
//Execute pending operations waiting for this script to be loaded.
NotifyScriptLoadedAndExecuteWaitingJobs("Company.Project.Ribbon.PageComponent.js");
So, now that we implemented our PageComponent, all we need is to create an instance and register is with the PageManager using this code that makes sure all JS files were fully loaded:
function init2() {
//All JS files were loaded - create instance and register it.
var instance = new Company.Project.Ribbon.PageComponent("ComponentID");
SP.Ribbon.PageManager.get_instance().addPageComponent(instance);
}
function init1() {
//Wait for ribbon JS to load
ExecuteOrDelayUntilScriptLoaded(init2, 'sp.ribbon.js');
}
//Wait for our JS to load
ExecuteOrDelayUntilScriptLoaded(init1, 'Company.Project.Ribbon.PageComponent.js');
I hope this helps you when you are implementing your custom ribbon solutions, or if you need to handle an existing ribbon command in addition to its OOB logic.
Note: to replace an OOB logic of a ribbon command there are other steps you need to take, I will try blogging about it soon as well.
Thanks, Shai.