SPFX Sample – Drag and Drop elements across web parts
SharePoint

SPFX Sample – Drag and Drop elements across web parts

Content type Blog Post
Author Stefan Bauer
Publication Date 23 Feb, 2017
Reading Time Less than 1 minute

The SharePoint Framework (SPFx) improved a lot during the last drops. I thought to myself what fancy sample can I build using the framework. During the Hackathon at the European SharePoint Conference we saw a lot of great uses of this new framework. One team was even able to add a framework web part to SharePoint 2007.
Actually, last weekend I came up with an idea. It was more a proof of concept I would like to try. I asked myself the question. Will I be able to drag and drop data between two individual web parts.

The Base SetUp

I created a new SharePoint Framework project and added two web parts. It might would have been easy to move elements in just one web part, but I wanted to try this approach across different web parts.
The first thing I did was to created two React components. One of those components provides an area where items could be dropped. The other component defines how the entries will be rendered.
To use this component in both web parts I created a single reusable component apart from the actual web part code.

SPFX – Reusable core component

For styling, this component I attached the style sheets directly in my core component. I also disabled the auto prefixing of style sheet classes provided by the framework because I wanted to share the style sheet definitions across both web parts too.

From my perspective, it is a good approach to develop components that are intended to be used in multiple web parts beside the actual web part code.

It is an approach similar to asp.net web controls.

Add Reusable Component to Web Part

To add these components to the web parts all that needs to be done is to import the component first.

JavaScript

import * as DragNDrop from '../coreComponents/DropBoxes';

 

import * as strings from 'firstWebPartStrings';

import FirstWebPart, { IFirstWebPartProps } from './components/FirstWebPart';

import { IFirstWebPartWebPartProps } from './IFirstWebPartWebPartProps';

View on Github

At the top of ‘FirstWebPart.ts’ and ‘SecondWebPart.ts’ I imported the components as ‘DragNDrop’ from a folder that actually was one level higher than the web part code.
The next thing that needed to be done is to create instances of the control in the render method of the web part.

public render(): void {

 

  const element: React.ReactElement<IFirstWebPartProps> = React.createElement(FirstWebPart, {

      description: this.properties.description

    });

        

        // Create a new instance of the DropBox container

  const myDropBox = React.createElement(DragNDrop.DragNDropContainer, {

    items: [

      { title: "EAT" },

      { title: "SLEEP" },

      { title: "<Code>" },

      { title: "Repeat" }

    ],

    title: this.title

  });

 

  // add drop box to UI

  ReactDom.render(myDropBox, this.domElement);

 

}

View on GitHub

In the first web part code I added some predefined items while the second web part doesn’t contain any predefined items.
Now everything was ready to implement the drag and drop functionality.

Drag and Drop Event Handlers and Handler Binding

The drop container component is named ‘DragNDropContainer’. The events that need to be bound to this component are:

  • onDrop
    This event will be fired when a new element was dropped in the container.
  • onDragOver
    This event occurs during dragging the item around and contains useful information.

 

Drag and Drop Item

The name of the other component is ‘DragNDropItem’ and handles the following events

  • onDragStart
    This event occurs when you grab an item and start to move it around.
  • onDragEnd
    This event happens after the user stopped dragging the item around.

Last but not least the attribute ‘draggable’ needs to be defined on every instance of this component. Without this attribute the item is locked and cannot be moved.
So far so good, but now for the tricky parts. To be able to move items around the React data binding, and event firing needs to be implemented the following way.

Data Binding

The first problem I faced with React was how to be able to access the parent container from every child item instance. Normally only data of the current element can be accessed through the events.
The solution was that I passed the parent container directly to the items. This way I was able to access both data, data of the child element and the parent container.
The generation and rendering of the ‘DragNDropContainer’ look like this.

JavaScript

 

render() {

 

  return (

    <div className="dropbox"

      onDrop={this.handleDrop.bind(this)}

      onDragOver={this.handleDragOver.bind(this)} >

        <div className="dropbox-title">

           <h2>{this.props.title}</h2>

       </div>

       <div className="dropbox-zone">

       {

         this.state.items.map(function (item) {

         return <DragNDropItem parentList={this} key={item.title} >

            {item}

            </DragNDropItem>

       }, this)

     }

     </div>

   </div>

)

}

View on GitHub

During the generation of the ‘DropNDropItems’ I simply passed the current container as a ‘parentList’ parameter.
Through this method I was able to update the state of the parent container and remove the already moved element.

Events

Finally, let’s take a closer look on the event firing order.
When you drag items around you actually not drag the item itself, you simply drag the data or the DOM content of the element. After you moved an element, the originating element needs to be removed manually.
For the user it looks like dragging the item around.

On Drag Start

This is the first event that will be fired when you start to drag an item. Now its time to attach information about the selected element to the event. For storing those the data the ‘dataTransfer’ object needs to be used.

JavaScript

event.dataTransfer.setData("data", JSON.stringify(itemData));

event.dataTransfer.dropEffect = "move";

View on GitHub

The itemData variable stores all information of the component and only was converted to a JSON string. This ‘data’ is persistent throughout the whole event.

On Drop

The second event that needs to be handled once the item was dropped in the drop container. This is attached to the ‘DragNDropContainer’. All that needs to be done in here is to create a new instance of the moved element.
All that this event does it to request the previously stored event data. Then the data were pushed to the state of the ‘DragNDropContainer’.

JavaScript

<a href="https://github.com/StfBauer/spfx-drag-n-drop/blob/master/src/webparts/coreComponents/DropBoxes.tsx#L50-L63">// Handle Drop event</a>

handleDrop(event) {

 

    event.preventDefault();

 

    let data = null;

  

        // convert data back to a json object

    data = JSON.parse(event.dataTransfer.getData("data"));

 

        // manipulate the state of the draped container

    var newState = this.state;

    newState.items.push(data);

    this.setState(newState);

}

View on GitHub

The update of the state automatically adds a new item instance, as a child control of the user interface.
The last thing that needs to be done is to remove the origin element and identify if the drag was successful or failed.

On Drag End

Again this event is registered on the child items but not on the container. This event contains the same stored data in the dataTransfer object. In addition the dataTransfer contains a variable named dropEffect. This ‘dropEffect’ indicates the state of the dragging transaction.

event.dataTransfer.dropEffect !== "none"

The value ‘none’ means that the move was canceled or unsuccessful. In this case the item will automatically return to its original position on the screen.

Other possible values of the dropEffect are:

  • copy
    A copy of the source item is made at the new location.
  • move
    An item is moved to a new location.
  • link
    A link is established to the source at the new location.
  • none
    The item may not be dropped.

If the transaction contains ‘move’ the originating item can be removed again by updating the state of the parent container.
I hope this helped a bit to demystify the drag and drop handling in components.
Finally, let’s take a look at the final solution.

The Finished Solution and Thoughts

Like I promised. Drag and drop in web applications are no rocket Science and helps to improve the user experience for certain scenarios. Just to mention some. You might be able to create your own Kanban board and update the state of a task element.
Another scenario is that you will be able to drag and drop documents across libraries. I think there are many things this user interaction pattern can be useful.
Sadly drag and drop is currently not supported on mobile or tablet devices.
In future I expect that this will become more relevant because the number of devices that are touch enabled will increase in future.

If you like to try this solution yourself. Please feel free to clone it from my GitHub repository.
In case you experience any problem you can create a new issue on the repository or comment below.

About the Author

Stefan Bauer

Stefan Bauer is a design-minded developer & information architect and Office Development MVP based in Vienna / Austria.

Follow Stefan on twitter here. Blog post re-purposed with permission.

Reference:
Bauer, S. (2016). Drag and Drop across web parts using React and SPFX – SPFx Sample. [online] n8design. Available at: http://www.n8d.at/blog/spfx-sample-drag-and-drop-elements-between-web-parts/ [Accessed 23 Feb. 2017].