PortiBlog

Sharing data between SPFx web parts

24 september 2018

When creating SPFx web parts, you might want to share data between different web parts. You would like for web parts to interact and so enhance their functionality. Another reason might be simply to share data between web part and in doing so reducing the number of calls to back end systems. In this blog I will show you an easy way to share data between web parts that are on the same page. We will create two simple SPFx web parts that will share some data between the two of them. In this tutorial I will use the ReactJS framework as basis for the web parts. This tutorial does assume you have a basic understanding of SPFx and ReactJS(typescript).

Let’s start by scaffolding a new web part project. I use the Yeoman SharePoint generator to create a SenderWebPart. We will then have to change a few things from the setup we get. Locate the SenderWebPart component in your scaffolded project. The first thing to do is to define a state for this component with a string property called text. We then need a constructor, so the component can instantiate the state. Next step is to change the render method, so we get a simple form with an input field and a button. To finish it, we need to define two methods, one that regulates the input from the input element. I also added a method that will transmit the data from this web part to the other web part, we will finish this method later. For now, it just shows your input in a pop-up. My code looks like this now:

    
  
  
  
  
export default class SenderWebPart extends React.Component < ISenderWebPartProps, {
text: string;
}
> {
constructor() {
super();
this .state = {
text:
null ,
};
}

public render(): React.ReactElement<ISenderWebPartProps> {
return (
<div>
<h3>Sender web part</h3>
<p>Input data here that you want to transmit across the page</p>
<input type="text" value={this.state.text} onChange={this.changeInput.bind(this)} />
<button id="btnSend" onClick={this.sendData.bind(this)}>Send data</button>
</div>
);
}

// on change event that keeps track of the input in the input box
private changeInput(event: any) {
this.setState({
text: event.target.value
});
}

// emits the input as event to the page
private sendData(): void {
window.alert(
this.state.text);
}
}

Next we build the logic that will allow us to transmit data from this web part to the page. First we need to install a modules and typings for TypeScript:

    
  
  
  
  
npm install rx - lite -- save
npm install @types
/ rx-lite --save

Why do we need these modules? The rx-lite library will be used to create an object of the current page. We will use that to transmit an event on the page from our SenderWebPart to the ReceiverWebPart we will create later on. As said earlier on, this solution only works when the web parts are on the same page. This is the reason why; we will emit an event to the page so the other web part has to be on the same page to pick that up. The next step is to create another folder in your ‘src’ folder next to the ‘webparts’ folder called ‘RxJsEventEmitter’. In that folder create a typescript file called: RxJsEventEmitter.ts with this content:

    
  
  
  
  
import { Subject } from " rx-lite " ;
import IEventData from
" ./IEventData " ;

export class RxJsEventEmitter {
public subjects: Object;

private constructor() {
this.subjects = {};
}

public static getInstance(): RxJsEventEmitter {
if (!window["RxJsEventEmitter"]) {
window[
"RxJsEventEmitter"] = new RxJsEventEmitter();
}
return window["RxJsEventEmitter"];
}

public emit(name: string, data: IEventData): void {
if (!this.subjects[name]) {
this.subjects[name] = new Subject();
}

this.subjects[name].onNext(data);
}

public on(name: string, handler: any): void {
if (!this.subjects[name]) {
this.subjects[name] = new Subject();
}

this.subjects[name].subscribe(handler);
}
}

This is based on a project from VelinGeorgiev. This class allows us to emit and subscribe to events. So our SenderWebPart can emit events that are subscribed by our ReceiverWebPart and in that way they can share data. But you might notice we still lack one thing. I import an interface called IEventData, but that is still missing. Create another file next to this one called IEventData.ts. This will be a very simple interface that is a model for that data that we want to transmit with our event. In our case we just want to add a single property called sharedData, so it looks like this:

    
  
  
  
  
export interface IEventData {
sharedData: string;
}

export default IEventData;

We are now setup to utilize this class in our SenderWebPart so we can start emitting events. We have to do a couple of things to get that working. First import the class and interface we just created into your SenderWebPart component:

    
  
  
  
  
import { RxJsEventEmitter } from ' ../../../RxJsEventEmitter/RxJsEventEmitter ' ;
import IEventData from
' ../../../RxJsEventEmitter/IEventData ' ;

Then add a property to the component:

    
  
  
  
  
private readonly eventEmitter: RxJsEventEmitter = RxJsEventEmitter.getInstance();

Finally change the sendData method by removing the pop-up that it now generates and in stead calling our class to emit the event. You can do that like this:

    
  
  
  
  
private sendData(): void {
var eventBody = {
sharedData:
this .state.text
} as IEventData;

this.eventEmitter.emit("shareData", eventBody);
}

The emit function takes two parameters. The first is whatever name you want to give to the event you are about to emit. The second parameter is the data that will be send with it. On this end we should be all good for now. Let’s fix the receiving part.

Run the Yeoman Microsoft SharePoint generator again in the project folder to create a second webpart: ReceiverWebPart. In the generated web part, locate the component. The first thing to do is import the RxJsEventEmitter class and IEventData interface we created and add the EventEmitter as a property on this component as well. We need to add state to this component as well, you can use the setup as in the other webpart. Just a string property called text. Don’t forget the constructor to instantiate the state.
Next we will create the method that handles the incoming data:

    
  
  
  
  
private receiveData(data: IEventData) {
this .setState({
text: data.sharedData
});
}

Now all that remains is to subscribe to the event, so we can fire our method and get the input from the other webpart. In the constructor add this line:

    
  
  
  
  
this .eventEmitter.on( " shareData " , this .receiveData.bind( this ));

This will subscribe to the ‘shareData’ event and if that event is called will fire the method we just created. Change the render method so we actually see the result:

    
  
  
  
  
public render(): React.ReactElement < IReceiverWebPartProps > {
return (
< div >
< h3 > Receiver web part < / h3>
< div >< span > Received data: < / span><span>{this.state.text}< / span >< / div>
< / div>
);
}

Congratulations, you built connected web parts like a boss!

Submit a comment