Category: The Others

Refactoring the ChartJS SPFx Web Part  Using IoC Container

Refactoring the ChartJS SPFx Web Part Using IoC Container

In the previous blog post, I introduced how to create Chart.js enabled SPFx web parts. In the example I have used in that blog post, I created a IDataSource interface and implemented the interface with a MockDataSource class that was instantiated in the React component.

01

However, there is an issue with this design as the ChartJs React component class depends on the concrete data source class implementation instead of the abstraction of the data sources. In this way, we have to change the ChartJs React component code if we want to substitute the MockDataSource with other data sources that implements the IDataSource interfaces.

In future, we may create other data sources for the chart web part such as SharePoint data source, Office Excel data source, or Rest API data source. Instead of changing the code in ChartJs React component to substitute the data source type, we expect a better approach that allow us to configure the data source type outside of the ChartJs React component so that we can have the ChartJs React component closed to change when we need add a new data source type.

Here a IoC container will help. In this blog post, I improved the ChartJs SPFx web part solution with InversifyJS IoC container to manage the dependencies. Please find the source code used for this blog post here in github.

Setup InversifyJS in ChartJs web part project

It is pretty easy to setup InversifyJS library in our ChartJs web part project. Firstly, we run the npm command: npm install inversify reflect-metadata –save to install the library, and then we need to modify the compiler options of Typescript in the tsconfig.json file to add additional settings for “lib”, “types”, “moduleResolution”, “experimentalDecorators”, and “emitDecoratorMetadata” as the snapshot below shown.

03

Annotate data source classes with @injectable decorator

We need to add @injectable decorator to the data source classes that are implemented the IDataSource interface.

04

Apart from the MockDataSource class we already have, we are going to create another data source class for testing purpose, namely MockDataSourceForTestIOC. All the values of the mock data feed from this class are set as “10” as a comparison to the orginal MockDataSoruce class.

06

Add Inversify configuration file

We need to create a Inversify configuration file in the src folder that declares the Inversify IoC container and register the concrete data source class implementation to IDataSource interface.

08

In this file, we need import the IDataSource interface and the concrete data source classes, and then we create an instance of Inversify Container and bind a concrete class to the IDataSource interface.

07

Use abstraction in Chart.js component

In the Chart.js component class that depends on the data source implementation, we firstly import the container declared in the Inversify configuration file.

09

We then use the get method of the container  (kernel.get(“IDataSource”)) to resolve the dependency.

10

Test 

We have setup the Inversify IoC container in our project. We can now test the setup through binding the IDataSource interface to different data sources. Firstly, we bind the interface to MockDataSource class. After run gulp serve to preview the web part in local workbench, we have the results showing as:

11

Then we bind the IDataSource interface to MockDataSourceForTestIOC class that provides the data set with all the groups having same value as “10”.

11

After run the preview, we can see that the chart is rendered using the data provided by the MockDataSourceForTestIOC implementation.

12

Advertisements
Building SPFx Client-Side Web Parts using Chart.js

Building SPFx Client-Side Web Parts using Chart.js

Chart.js is a very handy Javascript charting library, lightweight, flexible, sleek UI and free of charge. In this blog post, I will walk through the steps to build SharePoint Framework client-side web parts using Chart.js library.

I have created a demo client-side chart web part with the source code on github. This web part allows users to select chart type on the web part properties panel and then renders the chart in the selected chart type using Chart.js library. An interface, IDataSource, was created in the web part and we can implement the interface with all kinds of data sources such as SharePoint, Office Excel, and Rest APIs. For this demo web part, I just implemented the interface with a mock data source class MockDataSource, however we can add any data source class in future as long as the class implements the getData method defined in the IDataSource interface.

2

Setup development environment and scaffold the project structures

Please follow this guide from Microsoft to setup the SharePoint Framework development environment.

After the development environment is ready, run yo @microsoft/sharepoint in node.js command prompt to scaffold the project structures and select “React” as the Javascript framework.

Create data access interface/classes

In this demo, three data access interface/classes need to be created, including ChartJSData DTO class, IDataSoruce interface, and MockDataSoruce class.

3

Firstly, we need to create a DTO class, ChartJSData, that holds the data read from the data sources and will be rendered using Chart.js charts.

4e

We then create the IDataSource interface with the getData() method signature and implement the interface with a mock data source, MockDataSource.

5

6

Import Chart.js library into client-side web part

To import the Chart.js library into the client-side web part, we need add an external entry in the config.json file and point it to the CDN hosted Chart.js file.

7

Then import the library in the React component file (ChartJs.tsx) that will render the Chart.js charts.

8

Create and Render the Chart.js charts in client-side web part

In the React component file (ChartJs.tsx), we firstly import the MockDataSource class and ChartJSData class defined earlier.

9

In the render() method of the ChartJs React component, we add the canvas tag where the Chart.js charts will be drew on.

We then create a private method renderChart() that creates and renders the Chart.js chart object with the data read from the MockDataSource. This method will be called in the both componentDidMount() method and componentDidUpdate() method. The reason to re-render the chart in the componentDidUpdate() method is to allow users changing chart type using the web part properties panel.

10

We now have the Chart.js library ready to render charts in the demo client-side web part. We can run gulp serve to preview the web part on the local workbench.

11

Adding GridView to SharePoint 2010 List Forms

Adding GridView to SharePoint 2010 List Forms

Quite a while ago, I got a piece of very interesting work that required me to create user-friendly list forms for adding and editing coded class timetable data on SharePoint 2010.

The timetable data of a class is coded (including the day, time, and room info.) and stored in a field of a SharePoint list.

clip_image001

It is obviously that the default Sharepoint list forms are not user-friendly at all for end users, such as teachers or courses administrators, to input and edit the class timetables.

clip_image002

To provide end-users an easy way to manage the class timetables, I created custom Sharepoint list forms (New/Edit Forms) with GridView control integrated.

clip_image004

The blog post goes through the main steps for adding GridView control to Sharepoint 2010 list forms.

  1. In your Sharepoint project in Visual Studio 2010, add a “List Definition” and name it as “Classes”, and create a list instance namely ‘Classes_Temp’.

clip_image005

  1. Add two Application Pages, “CustomNewForm.aspx” and “CustomEditForm.aspx”, to your project (these two files are initially placed in the Layout folder. You need them into the “Classes” list definition.

clip_image006

  1. Deploy the list definition on the target Sharepoint site. Open SPD and navigate to the deployed “Classes_Temp” list instance. Create custom New/Edit list forms and copy the source code to the corresponding “CustomNewForm.aspx” and “CustomEditForm.aspx” files created in Visual Studio.

  2. Create a user control which wraps a GridView control. The purpose to create this user control is to encapsulate all business logics and data access modules into a single user control which can be reused in anywhere.

clip_image007

clip_image008

  1. Create the business objects and utility classes (e.g. timetable parser and ULS Logging) .

clip_image009

  1. Insert the created GridView user control into the “CustomNewForm.aspx” and “CustomEditForm.aspx” (at the place where you want to show the Gridview).

clip_image010

  1. Now, the “CustomNewForm.aspx” and “CustomEditForm.aspx” application pages are created. We need set them as the default list forms . To do that, we open the “schema.xml” file of the “Classes” list definition, and point the “SetupPath” of the list forms to these two application pages :

clip_image012

After we deploy the list definition, we can see the GridView control appears on both New form and Edit form.

clip_image004[1]

Dynamics CRM 2011 Computer-Telephony Integration Solution Using Flexor CTI Add-on

I have been searching for CTI solutions for Dynamics CRM 2011 and found Flexor CTI is a good choice which provides most of common CTI features, such as click-to-dial, screen popping, on-screen call control, journal and log calls direct to customer records in Dynamics CRM, and also management reporting. This add-on is very easy to install and configure, and it supports a wide range of telephony devices. You could find the list of supported devices here.

This blog post goes through the steps to integrate Flexor CTI Add-on with an on-premier Dynamics CRM 2011 through Asterisk PBX.

  1. Download and install Flexor CTI for Dynamics CRM add-on.

  2. After the add-on is installed, a configuration dialogue is poped up. you need firstly login the Flexor CTI manager with your account. If you have not gotten a account, you can register as a trial user with 14 days trial period.

clip_image002

  1. Looking for telephony devices in your organisation.

clip_image004

As the telephony devices used in this example are based on Asterisk PBX. We need set the asterisk server details and AMI (Asterisk Manager Interface) user credentials. Flexor CTI communicates with Asterisk server through AMI, so we need edit the manager.conf in Asterisk server to enable AMI and also create a user credential for connecting to the AMI. You could find more details for configuring Asterisk Server here.

clip_image005

After the Flexor CTI manager connects to Asterisk server, you could scan the server and get all the available telephony devices in your organisation. Then you could select and add the telephony device you want to connect to the current PC.

clip_image006

Now, the Flexor CTI manager is installed and configured.

clip_image008

  1. Deploy Flexor CTI customisation solution on Dynamics CRM. Firstly, locate the “CamrivoxFlexorDynamicsCTI_managed.zip” file in the Flexor CTI program folder (in Program Files folder). Then log on Dynamics CRM and navigate to Solutions page, import the “CamrivoxFlexorDynamicsCTI_managed.zip” solution to the Dynamics CRM instance.

clip_image009

clip_image011

Don’t forget to publish the solution after it is imported.

clip_image012

  1. Repeat step 1 – 3 to install Flexor CTI add-on on all client PCs in your organisation.

Now, the Flexor CTI is integrated with Dynamincs CRM system. As the snapshot below shows, you can directly make a phone call to your contacts through the Dynamics CRM UI.

clip_image014

Once there is a phone call coming in, an inbound call panel pops up. You could hang off, hold, forward the phone call through the panel.

clip_image015

In addition, you could add the caller into Dynamics CRM as a Lead, Contact, or Account. Also, you could create a customer service Case record from the phone call.

clip_image016

More importantly, you can edit subject and journal entry on the call panel. Once the phone call is completed, the phone call activity will be logged in Dynamics CRM.

clip_image017

Create a Custom Delete Button on SharePoint List Form using ECMAScript and Client Object Model

Create a Custom Delete Button on SharePoint List Form using ECMAScript and Client Object Model

On a SharePoint 2010 List Form (e.g Edit Form or Display Form), there is a ‘Delete Item’ button on the top Ribbon bar which can be used to delete the current list item.

image

When the list item has been deleted, the users would be redirected to the default view page of the list. However, sometimes, users want to be redirected to another custom page instead of the default view page when they delete the item using the ‘Delete Item’ Ribbon button. Also, they may want some business logics to be executed after the item has been deleted.

We can develop a custom Event Handler to redirect the page and add the required business logic. However, Event Handlers may not be suitable or practical for same cases. For example, post-delete event (e.g. ItemDeleted) is an asynchronous event in sharepoint while page redirection need happen in a synchronous process(i.e. item deletion and page redirection need happen in a same thread).

An alternative approach to solve this problem is to replace the ‘Delete Item’ button on the Ribbon bar with a copied/fake ‘Delete Item’ button and then use ECMAScript and Sharepoint Client Object Model to remove the item and implement business logics/page redirection on the post-delete event.

Below is the list of steps to create the custom delete button.

Step 1: Launch SPD and locate the target list. Create a custom List Form (e.g. Edit Form) for this list.

image

Step 2: Open the created list form, change it to ‘Code’ view and switch it to ‘Advanced Mode’. You will add the javascript code mentioned in the rest of post to the list form. You can find the full source code at bottom of this post.

image

Step 3: Make a copy of the ’Delete Item’ button and then hide the original button. We can do that using a JQuery-based approach which was original suggested by Charlie Chen.

//Get the original 'Delete Item' button object
var button = document.getElementById("Ribbon.ListForm.Edit.Actions.DeleteItem-Large");
if (button != null) {
    var $button = $(button);
    var html = $button.html();

    //Make a cope of the orginal 'Delete Item' button and then hide the orginal button
    $button.after("<a class="ms-cui-ctl-large">" + html + "</a>").hide().next().click(function (e) {

        //TODO Remove the List Item using Client Object Model

    });
}

Step 4: Delete the list item using ECMAScript and Client Object Model.

//Create current client context
var context = new SP.ClientContext.get_current();
//Get current web object
var web = context.get_web();
//Get the list where you want to delete item from
var list = web.get_lists().getByTitle('List Title');
//Get the item to delete
var itemToDelete = list.getItemById(itemID);
//execute delete command asynchronously
itemToDelete.deleteObject();
context.executeQueryAsync(Function.createDelegate(this, this.Success), Function.createDelegate(this, this.Failed));

Step 5: Implement the Success and Failed callback functions. The Success function would be triggered if the  context.executeQueryAsync (see the code segment above) commend has been executed successfully, the Failed function would be triggered if the command has failed.

function Success() {

    //TODO: Add Post-Delete Logic Here
}

function Failed(sender, args) {
    alert('Failed to delete the item.');
}

Step 6: Add the following line of code to load the script on the list form page onLoad event.

//Load the script
_spBodyOnLoadFunctionNames.push("addCustomDeleteButton");

Below is the completed source code used in the post.

//Load the script
_spBodyOnLoadFunctionNames.push("addCustomDeleteButton");</pre>
function addCustomDeleteButton() {

    //Get the original 'Delete Item' button object
    var button = document.getElementById("Ribbon.ListForm.Edit.Actions.DeleteItem-Large");

    if (button != null) {
        var $button = $(button);
        var html = $button.html();
        dialog = this;

        //Make a cope of the orginal 'Delete Item' button and then hide the orginal button
        $button.after("<a class="ms-cui-ctl-large">" + html + "</a>").hide().next().click(function (e) {

            //get the id of current item
            itemID = getQuerystring('ID')

            //remove the item using Sharepoint Client Object Model
            deleteItem(itemID)

            //unbind the click event handler
            var $this = $(this);
            $this.unbind("click");
            dialog = null;
        });
    }

}

function deleteItem(itemID) {

    //Create current client context
    var context = new SP.ClientContext.get_current();
    //Get current web object
    var web = context.get_web();
    //Get the list where you want to delete item from
    var list = web.get_lists().getByTitle('List Title');
    //Get the item to delete
    var itemToDelete = list.getItemById(itemID);
    //execute delete command asynchronously
    itemToDelete.deleteObject();
    context.executeQueryAsync(Function.createDelegate(this, this.Success), Function.createDelegate(this, this.Failed));
}

function Success() {

    //TODO: Add Post-Delete Logic Here
}

function Failed(sender, args) {
    alert('Failed to delete the item.');
}

function getQuerystring(key, default_)
{
   if (default_==null) default_="";
   key = key.replace(/[\[]/,"<a>\\\[").replace(/[\]]/,"\\\</a>]");
   var regex = new RegExp("[\\?&]"+key+"=([^&#]*)");
   var qs = regex.exec(window.location.href);
   if(qs == null)
     return default_;
   else
     return qs[1];
}