SPFx Connected Web Parts

Today I want to show you how you can create connected Web Parts in SharePoint Framework. One is Sender Web Part which send custom text from TextField and another is Receiver Web Part which write this text to panel.

For that example I used ReactiveX (RxJS) for communication between Web Parts with publish-subscribe pattern. I found RxJS event emitter written by @VelinGeorgiev.

Let’s start new SharePoint Framework project named rxjs-connected-webparts with Yeoman Generator.

yo @microsoft/sharepoint

Choose WebPart named SenderWp as the client-side component type and React as framework.

In additional install rx-lite, @types/rx-lite and sp-pnp-js with commands below:

npm install rx-lite --save
npm install @types/rx-lite --save
npm install sp-pnp-js --save

Open project with Visual Studio Code.

code .

Then we have to clone RxJsEventEmitter from GitHub written by @VelinGeorgiev to src/libraries subfolder in our project (folder needs to be created).

Go to src/libraries/EventData.ts file and modify it so it will send string variable named text between web parts.

export class EventData {
  public text: string;
}

Go to React component of SenderWp and create an interface for representing state of WebPart named ISenderWpState.ts:

export interface ISenderWpState {
    text: string;
}

Then go to SenderWp.tsx file. Import Button and TextField from Office UI Fabric React and the interface ISenderWpState created before.

import { Button } from 'office-ui-fabric-react/lib/Button';
import { TextField } from 'office-ui-fabric-react/lib/TextField';
import { ISenderWpState } from './ISenderWpState';

In addition import RxJsEventEmitter and EventData class.

import { RxJsEventEmitter } from '../../../libraries/rxJsEventEmitter/RxJsEventEmitter';
import { EventData } from '../../../libraries/rxJsEventEmitter/EventData';

Change state to ISenderWpState for your React.Component class named SenderWp.

export default class SenderWp extends React.Component<ISenderWpProps, ISenderWpState> {
    ...
}

Then create an instance of RxJsEventEmitter inside this class, define a constructor with default state text, render method with Web Part content and button event handler function where you emit event named myCustomEvent:start (your choice) with custom text data.

export default class SenderWp extends React.Component<ISenderWpProps, ISenderWpState> {

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

  constructor(props: ISenderWpProps) {
    super(props);

    this.state = { text: "Custom text" };
  }

  public render(): React.ReactElement<ISenderWpProps> {
    return (
      <div className={styles.senderWp}>
        <div className={styles.container}>
          <div className={`ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}`}>
            <div className="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
              <h2>Sender Web Part</h2>
              <TextField label="Message:" className={styles.textfield} value={this.state.text} onChanged={(e) => this.state.text = e} />
              <Button onClick={this.senderData.bind(this)} id="btnSend">
                Send data
              </Button>
            </div>
          </div>
        </div>
      </div>
    );
  }

  protected senderData(): void {
        this._eventEmitter.emit("myCustomEvent:start", { text: this.state.text } as EventData);
      }
}

Create another web part named ReceiverWp in same project with Yeoman generator (be sure that you are in the folder of your project created before).

yo @microsoft/sharepoint

Go to React component of ReceiverWp and create an interface for receiver web part state named IReceiverWpState.ts. There we have array of received strings with belonging index.

export interface IReceiverWpState {
    eventsList: Array<{ index: number, data: string }>;
}

Next go to ReceiverWp.tsx file. Import that interface IReceiverWpState created before and RxJsEventEmitter with EventData class.

import { IReceiverWpState } from './IReceiverWpState';

import { RxJsEventEmitter } from '../../../libraries/rxJsEventEmitter/RxJsEventEmitter'; 
import { EventData } from '../../../libraries/rxJsEventEmitter/EventData';

Change state to IReceiverWpState for your React.Component class named ReceiverWp.

export default class ReceiverWp extends React.Component<ISenderWpProps, IReceiverWpState> {
    ...
}

Then create an instance of RxJsEventEmitter inside this class, define a constructor with subscription for events named myCustomEvent:start (your choice) with event handler function and render method with Web Part content.

export default class ReceiverWp extends React.Component<IReceiverWpProps, IReceiverWpState> {

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

  constructor(props: IReceiverWpProps) {
    super(props);

    this.state = { eventsList: [] };

    // subscribe for event by event name.
    this._eventEmitter.on("myCustomEvent:start", this.receivedEvent.bind(this));
  }

  public render(): React.ReactElement<IReceiverWpProps> {
    return (
      <div className={styles.receiverWp}>
        <div className={styles.container}>
          <div className={`ms-Grid-row ms-bgColor-themeDark ms-fontColor-white ${styles.row}`}>
            <div className="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
              <h2>Reactiver Web Part</h2>
              <h2>Received data:</h2>
              {
                this.state.eventsList.map((item: { index: number, data: string }) => {
                  return <div key={item.index}>{item.data}</div>;
                })
              }
            </div>
          </div>
        </div>
      </div>
    );
  }

  protected receivedEvent(data: EventData): void {
      // update the events list with the newly received data from the event subscriber.
      this.state.eventsList.push(
        {
          index: this.state.eventsList.length,
          data: data.text
        }
      );
 
      // set new state.
      this.setState((previousState: IReceiverWpState, props: IReceiverWpProps): IReceiverWpState => {
        previousState.eventsList = this.state.eventsList;
        return previousState;
      });
    }
}

This is all folks. We can test our project with next command.

gulp serve

2017-10-10_1405

[ Complete code on GitHub ]

Cheers!
Gašper Rupnik

{End.}

Advertisements

4 thoughts on “SPFx Connected Web Parts

Add yours

  1. I am getting error at line

    this.state.text = e} />

    saying Cannot assign to ‘text’ because it is a constant or a read-only property.

    any help

    1. Yes, this comes with new version of React Framework -> you have to use this.setState() method.

      Please take a look to my other new posts where I used setState() method.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Powered by WordPress.com.

Up ↑

%d bloggers like this: