In this blog, we will create a single selection listbox as a React component in Sharepoint framework (SPFx).
Child Component
I have created a React component and named it as "Dialog.tsx" as shown below.
Import the below node modules to your solution. Here, i have used UrlQueryParameterCollection to retrieve the ListItemId which is passed as a querystring parameter.
- import * as React from 'react';
- import { ISpFxRichTextEditorProps } from '../ISpFxRichTextEditorProps';
- import { PrimaryButton, DefaultButton, DialogType, Dialog, DialogFooter, TextField, Label } from 'office-ui-fabric-react';
- import * as $ from "jquery";
- import { UrlQueryParameterCollection } from '@microsoft/sp-core-library';
Declare the state variables as shown below.
- export interface IDialogState {
- hideDialog: boolean;
- spanID:string;
- dialogID: string;
- standard:string;
- isDraggable: boolean;
- areaTemparr:any[];
- multiSelectAreaarr:string[];
- }
Declare constant values that will be populated in List Box.
- const areaConstantValues=["Bangalore","Mysore","Kerala","Chennai","Delhi","Mumbai"];
Initiate the state variables.
- public state: IDialogState = {
- hideDialog: this.props.dialogOpen,
- isDraggable: false,
- spanID: this.props.spanID,
- dialogID: this.props.id,
- standard:this.props.standard,
- areaTemparr : [], multiSelectAreaarr:$('#' + this.props.spanID).html() != "" ? this.RemovedPtagString($('#' + this.props.spanID).html()).split(",") : []
- };
Get the values of Dialog Label and sub text label from parent component.
- private labelId: string = getId('dialogLabel');
- private subTextId: string = getId('subTextLabel');
Insert the select box as shown below.
- public render() {
- const { hideDialog } = this.state;
-
- let inputchkbox;
- let inputcontrols;
- let selectBox;
-
- if(this.state.spanID == "spnAreaVal"){
- inputchkbox = this.state.areaTemparr.map((element: string) => {
- let result = this.state.multiSelectAreaarr.indexOf(element);
- return (
- (result > -1) ? <option value={element} selected>{element}</option>
- : <option value={element}>{element}</option>
- );
- });
- }
-
- if(this.state.spanID == "spnAreaVal"){
- selectBox = <select id={"AreaSelect" + this.props.id} style={{ width: 250, height: 200 }} size={6} >
- {inputchkbox}
- </select>
- }
-
- return (
- <div>
- <Dialog
- hidden={hideDialog}
- onDismiss={this.closeDialog}
- dialogContentProps={{
- type: DialogType.normal,
- title: this.props.value,
- subText: "",
- styles: { title: { backgroundColor: "blue", height: 10, marginBottom: 10, paddingBottom: 22 } }
- }}
- modalProps={{
- titleAriaId: this.labelId,
- subtitleAriaId: this.subTextId,
- isBlocking: false,
- styles: { main: { height: 350, width: 500 } },
- }}>
- {selectBox}
-
- <DialogFooter>
- <PrimaryButton onClick={this.saveDialog} text="Save" />
- <DefaultButton onClick={this.closeDialog} text="Cancel" />
- </DialogFooter>
- </Dialog>
- </div>
-
- );
- }
Reload the container on componemtDidMount.
- public componentDidMount(): void {
- if(this.state.spanID == "spnAreaVal"){
- this.ReLoadAreaContainer();
- }
- }
-
- public ReLoadAreaContainer() {
- try {
- this.setState({areaTemparr:areaConstantValues});
- } catch (error) {
- console.log("Error in ReLoadDepartamentContainer : " + error);
- }
- }
This function is to close the dialog window.
-
- private closeDialog = (): void => {
- this.setState({ hideDialog: true });
- this.props.onUpdate();
- }
Save the selected data and callback to parent as shown below.
- private saveDialog = (): void => {
- if(this.state.spanID == "spnAreaVal"){
- this.SaveArea();
- }
- }
-
-
- private SaveArea(){
- try{
- let selValues = "";
- $("#AreaSelect" + this.props.id + " option:selected").each(function () {
- var $this = $(this);
- if ($this.length) {
- var selText = $this.text();
- if (selValues) {
- selValues = selValues + "<p>" + selText + "</p>";
- }
- else {
- selValues = "<p>" + selText + "</p>";
- }
- }
- });
- $("#" + this.state.spanID).html(selValues);
- this.setState({ hideDialog: true });
- this.props.onUpdate(this.props.value);
- }catch(error){
- console.log("Error in save area to div : " + error);
- }
- }
Remove HTML <P> tag from HTML string
-
-
- private RemovedPtagString(removedPString): string {
-
- let newString;
-
- try {
-
- let item = removedPString.replace(/<p[^>]*>/g, "").replace(/<\/p>/g, ",");
-
- item = item.replace(",,", ",");
-
- newString = item.indexOf(",") == 0 ? item.substring(1) : item;
-
- newString = newString.slice(0, -1);
-
- } catch (error) { console.log("Error in removedPtagString : " + error ); }
-
- return newString;
-
- }
Use "DialogBox.tsx" component in parent.
Parent Component
Import "DialogBox.tsx" reusable component in your parent file as shown below.
Note
I have placed the DialoBox.tsx file under ReactDialogBox folder and hence the below path.
If you are not using Folder structure you can directly import the child component without giving the folder name.
- import DialogBox from './ReactDialogBox/DialogBox';
Declare the state variable in your parent state as shown below.
- export interface ISpFxRichTextEditorState {
- clicked: boolean;
- listName: string;
- dialogTitle: string;
- spanID: string;
- DialogId: string;
- standard:string;
- isValidArea:string;
- toolpriorityinput:string;
- Safety:string;
- }
Declare the props in your parent props as shown below.
- export interface ISpFxRichTextEditorProps {
- id?:string;
- value?:string;
- dialogOpen?:boolean;
- spanID?:string;
- listName?:string;
- standardText?:string;
- onUpdate?:any;
- standard?:string;
- }
In the parent constructor class, declare the below state variables
- constructor(props: ISpFxRichTextEditorProps, state: ISpFxRichTextEditorState) {
- super(props);
-
- this.state = {
- clicked: false,
- listName: "",
- dialogTitle: "",
- spanID: "",
- DialogId: "",
- standard:"",
- isValidArea:"",
- toolpriorityinput:"",
- Safety:""
-
- };
-
- }
Call the dialog box in your render method of parent component.
- public render(){
- return (
- <div>
- <div className="width3">
- <div className="modal-area">
- <div className="modal-heading">
- <span>
- <b>Area</b>
- {}
- </span>
- <span
- className="add-area float-right document-icons-area "
- onClick={() =>
- this.handleClick(
- "Areas",
- "Select Area",
- "spnAreaVal",
- "areaDlgId"
- )
- }
- >
- <Icon
- iconName="CalculatorAddition"
- className="ms-IconExample"
- />
- <label>Add</label>
- </span>
- </div>
- <div className="modal-adding">
- <div id="spnAreaVal" />
- </div>
- <div className="modal-box">
- {this.state.clicked ? (
- <DialogBox
- id={this.state.DialogId}
- value={this.state.dialogTitle}
- dialogOpen={false}
- onUpdate={this.onUpdate}
- spHttpClient={this.props.spHttpClient}
- siteUrl={this.props.siteUrl}
- listName={this.state.listName}
- spanID={this.state.spanID}
- standardText={this.state.standard}
- />
- ) : null}
- </div>
- </div>
- </div>
- </div>
- );
- }
Handle click event as shown below.
-
- private handleClick(
- listName: string,
- dialogTitle: string,
- spanID: string,
- DlgId: string
- ): void {
- this.setState({
- clicked: true,
- listName: listName,
- dialogTitle: dialogTitle,
- spanID: spanID,
- DialogId: DlgId
- });
- }
"onUpdate" function acts as call back from child component to parent component, which usually transfers the data from child to parent.
In the below function, I am storing the selected checkbox values in state variable named "Safety".
-
- private onUpdate = dlgOutput => {
- try{
- this.setState({
- clicked: false
- });
-
- this.setState({Safety:$("#spnAreaVal").text()});
-
- }catch(error){
- console.log("Error in onUpdate function : " + error);
- }
- }
Complete code of a child component "DialogBox.tsx" is depicted below,
- import * as React from 'react';
- import { ISpFxRichTextEditorProps } from '../ISpFxRichTextEditorProps';
- import { PrimaryButton, DefaultButton, DialogType, Dialog, DialogFooter, TextField, Label } from 'office-ui-fabric-react';
- import * as $ from "jquery";
- import { UrlQueryParameterCollection } from '@microsoft/sp-core-library';
-
- export interface IDialogState {
- hideDialog: boolean;
- spanID:string;
- dialogID: string;
- standard:string;
- isDraggable: boolean;
- areaTemparr:any[];
- multiSelectAreaarr:string[];
- }
-
-
- const areaConstantValues=["Bangalore","Mysore","Kerala","Chennai","Delhi","Mumbai"];
-
-
- export default class DialogBox extends React.Component<ISpFxRichTextEditorProps, any>
- {
-
- public constructor(props: ISpFxRichTextEditorProps) {
- super(props);
- }
-
- public state: IDialogState = {
- hideDialog: this.props.dialogOpen,
- isDraggable: false,
- spanID: this.props.spanID,
- dialogID: this.props.id,
- standard:this.props.standard,
- areaTemparr : [],
- multiSelectAreaarr:$('#' + this.props.spanID).html() != "" ? this.RemovedPtagString($('#' + this.props.spanID).html()).split(",") : []
- };
-
-
- private labelId: string = getId('dialogLabel');
- private subTextId: string = getId('subTextLabel');
-
- public render() {
- const { hideDialog } = this.state;
- let inputchkbox;
- let inputcontrols;
- let selectBox;
-
-
- if(this.state.spanID == "spnAreaVal"){
- inputchkbox = this.state.areaTemparr.map((element: string) => {
- let result = this.state.multiSelectAreaarr.indexOf(element);
- return (
- (result > -1) ? <option value={element} selected>{element}</option>
- : <option value={element}>{element}</option>
- );
- });
- }
-
-
-
- if(this.state.spanID == "spnAreaVal"){
- selectBox = <select id={"AreaSelect" + this.props.id} style={{ width: 250, height: 200 }} size={6} >
- {inputchkbox}
- </select>
- }
-
-
- return (
- <div>
- <Dialog
- hidden={hideDialog}
- onDismiss={this.closeDialog}
- dialogContentProps={{
- type: DialogType.normal,
- title: this.props.value,
- subText: "",
- styles: { title: { backgroundColor: "blue", height: 10, marginBottom: 10, paddingBottom: 22 } }
- }}
- modalProps={{
- titleAriaId: this.labelId,
- subtitleAriaId: this.subTextId,
- isBlocking: false,
- styles: { main: { height: 350, width: 500 } },
- }}>
- {selectBox}
-
- <DialogFooter>
- <PrimaryButton onClick={this.saveDialog} text="Save" />
- <DefaultButton onClick={this.closeDialog} text="Cancel" />
- </DialogFooter>
- </Dialog>
- </div>
-
- );
- }
-
-
- public componentDidMount(): void {
- if(this.state.spanID == "spnAreaVal"){
- this.ReLoadAreaContainer();
- }
- }
-
-
-
- public ReLoadAreaContainer() {
- try {
- this.setState({areaTemparr:areaConstantValues});
- } catch (error) {
- console.log("Error in ReLoadAreaContainer : " + error);
- }
- }
-
-
-
- private closeDialog = (): void => {
- this.setState({ hideDialog: true });
- this.props.onUpdate();
- }
-
-
- private saveDialog = (): void => {
- if(this.state.spanID == "spnAreaVal"){
- this.SaveArea();
- }
- }
-
-
- private SaveArea(){
- try{
- let selValues = "";
- $("#AreaSelect" + this.props.id + " option:selected").each(function () {
- var $this = $(this);
- if ($this.length) {
- var selText = $this.text();
- if (selValues) {
- selValues = selValues + "<p>" + selText + "</p>";
- }
- else {
- selValues = "<p>" + selText + "</p>";
- }
- }
- });
- $("#" + this.state.spanID).html(selValues);
- this.setState({ hideDialog: true });
- this.props.onUpdate(this.props.value);
- }catch(error){
- console.log("Error in save area to div : " + error);
- }
- }
-
-
-
- private RemovedPtagString(removedPString): string {
- let newString;
- try {
- let item = removedPString.replace(/<p[^>]*>/g, "").replace(/<\/p>/g, ",");
- item = item.replace(",,", ",");
- newString = item.indexOf(",") == 0 ? item.substring(1) : item;
- newString = newString.slice(0, -1);
- } catch (error) { console.log("Error in removedPtagString : " + error ); }
- return newString;
- }
-
- }
Complete code of a parent component "Parent.tsx" is depicted below,
- import * as React from 'react';
- import { ISpFxRichTextEditorProps } from './ISpFxRichTextEditorProps';
- import { ISpFxRichTextEditorState } from './ISpFxRichTextEditorState';
- import { UrlQueryParameterCollection } from '@microsoft/sp-core-library';
- import { css, DefaultButton, IButtonProps, IStyle, Label, PrimaryButton, DialogType, Dialog, DialogFooter, format, Icon, TextField } from 'office-ui-fabric-react';
- import DialogBox from './ReactDialogBox/DialogBox';
-
- export default class SpFxRichTextEditor extends React.Component<ISpFxRichTextEditorProps, ISpFxRichTextEditorState> {
-
- constructor(props: ISpFxRichTextEditorProps, state: ISpFxRichTextEditorState) {
- super(props);
- this.state = {
- clicked: false,
- listName: "",
- dialogTitle: "",
- spanID: "",
- DialogId: "",
- standard:"",
- isValidArea:"",
- toolpriorityinput:"",
- Safety:""
-
- };
-
- }
-
-
- private handleClick(
- listName: string,
- dialogTitle: string,
- spanID: string,
- DlgId: string
- ): void {
- this.setState({
- clicked: true,
- listName: listName,
- dialogTitle: dialogTitle,
- spanID: spanID,
- DialogId: DlgId
- });
- }
-
- public render(){
- return (
- <div>
- <div className="width3">
- <div className="modal-area">
- <div className="modal-heading">
- <span>
- <b>Area</b>
- {}
- </span>
- <span
- className="add-area float-right document-icons-area "
- onClick={() =>
- this.handleClick(
- "Areas",
- "Select Area",
- "spnAreaVal",
- "areaDlgId"
- )
- }
- >
- <Icon
- iconName="CalculatorAddition"
- className="ms-IconExample"
- />
- <label>Add</label>
- </span>
- </div>
- <div className="modal-adding">
- <div id="spnAreaVal" />
- </div>
- <div className="modal-box">
- {this.state.clicked ? (
- <DialogBox
- id={this.state.DialogId}
- value={this.state.dialogTitle}
- dialogOpen={false}
- onUpdate={this.onUpdate}
- spHttpClient={this.props.spHttpClient}
- siteUrl={this.props.siteUrl}
- listName={this.state.listName}
- spanID={this.state.spanID}
- standardText={this.state.standard}
- />
- ) : null}
- </div>
- </div>
- </div>
- </div>
- );
- }
-
-
-
- private onUpdate = dlgOutput => {
- try{
- this.setState({
- clicked: false
- });
-
- this.setState({Safety:$("#spnAreaVal").text()});
-
- }catch(error){
- console.log("Error in onUpdate function : " + error);
- }
- }
-
-
- public ReplaceSemiColon(stringValue):string{
- try{
- var htmlString='';
- var temp = [];
- if(stringValue !== ""){
- if(stringValue.indexOf(';') > -1){
- temp = stringValue.split(';')
- $.each(temp,function(index,value){
- htmlString = htmlString + "<p>" + value + "</p>"
- });
-
- }
- }
- return htmlString;
- }catch(error){
- console.log("Error in ReplaceSemiColon : " + error);
- }
- }
-
-
- public ReplaceParaWithSemiColon(stringValue):string{
- try{
- if(stringValue !== ""){
- if(stringValue.indexOf('<p>') >-1 ){
- stringValue = stringValue.replace(/<p>/g, "").replace(/<\/p>/g,";");
- stringValue = stringValue.substr(0, stringValue.length - 1);
- }
- }
- return stringValue;
- }catch(error){
- console.log("Error in ReplaceParaWithSemiColon : " + error);
- }
- }
-
- }
Below are the sample styles used to create multi-selection checkbox.
CSS
- .ms-Dialog-header .ms-Dialog-title {
- background-color:#da291c;
- color: #fff;
- padding: 8px;
- height: 40px;
- box-sizing: border-box;
- line-height: 1;
- font-weight: bold;
- font-size: 20px;
- }
- .ms-Dialog-header .ms-Button {
- color: #fff;
- position: relative;
- top: -6px;
- float: right;
- right: 0px;
- min-width:0em;
- }
- .ms-Dialog-header .ms-Button:hover,.ms-Dialog-header .ms-Button:active, .ms-Dialog-header .ms-Button:focus {
- background: none !important;
- }
- .ms-Dialog-header {
- margin-right: 40px;
- }
- .modal-heading {
- border: 1px solid #ccc;
- padding: 10px;
- background: #f1f1f1;
- }
-
- .modal-adding {
- border: 1px solid #ccc;
- background: #fff;
- padding: 10px;
- min-height: 100px;
- }
-
- .ms-Button-flexContainer {
- float: left;
- }
Output
Please feel free to share your comments.
I hope this helps!!!!