Client Side Web Part – Image Gallery using SPFx in Modern Communication Site

Published: 19 September 2018
modern communication sites

In the modern era, most of the enterprises are moving their SharePoint development towards the cloud and they started migrating their intranet sites/contents to SharePoint online (Office 365). We see several updates in SharePoint online – Office 365, they are providing more user-friendly and lightweight components in the sites which are known as modern experience. Microsoft introduced the communication site as a new feature which is completely in new/modern user experience.

When SharePoint developers need customization, we used to deploy full trust C# assembly earlier in on-premises environment but it does not work in Office 365, so we used to inject JavaScript for customization and most favorite web part of SharePoint consultants is Script Editor, but Script Editor web part is also not a ‘Safe For Scripting’ web part.

To work with the customization and custom solution in modern user experience/communication sites, SPFx client-side web parts are being used. Further information can be covered in SharePoint consulting services.

We will create here Image Gallery web part, which displays albums as a thumbnail image and a click on each album displays carousel image slider related to that album. Albums and images will be fetched from the picture library, where each folder will be considered as an album and images inside the folder will be displayed in a slider. We will also add a custom column for the album’s thumbnail image. So let’s start step by step to create a dynamic image gallery web part in communication site.

Step 1 – To Create Picture Library

  1. Go to Site Contents, click on New App.
  2. Click on Picture Library, give it name “Image Lib”:

    Picture Library

  3. Open created picture library and click on setting icon on upper right corner and click on Library Setting.

    Library setting

  4. Scroll down and click on “Create column”:

    create column

  5. Give column name “AlbumPicURL”, select type ‘Single line of text’, and click on Okay.

    single line of text

  6. Create few folders in this Image Lib Picture Library.
  7. Using Quick Edit, add URL of an image in AlbumPicURL property for each folder, that image will be viewed as thumbnail.

    Album Pic URL

Note: For security reasons we have erased URL, make sure to add full URL of the image.

Step 2 – To Create SPFx web part

We – SharePoint Programmers, assume that following prerequisites installed in your machine:

  • Nodejs
  • Yeoman
  • Gulp

For more information about setting up development environment refer this link:

https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-development-environment

    1. Open PowerShell.
    2. Create a new project folder
md Image-Gallery
    1. Go to project folder
cd Image-Gallery
    1. Create a new web part by running following command:
yo @microsoft/sharepoint
  1. Enter ‘Image-Gallery’ as your solution name, and then select Enter.
  2. Select ‘SharePoint Online only (latest)’, and select Enter.
  3. Select ‘Use the current folder’ for where to place the files.
  4. Select ‘N’ to require the extension to be installed on each site explicitly when it’s being used.
  5. Select Web Part as the client-side component type to be created.
  6. Enter ‘Image-Gallery’ as your web part name, and then select Enter.
  7. Accept the default ‘Image-Gallery’ description as your web part description, and then select Enter.
  8. Accept the default No JavaScript web framework as the framework you would like to use, and then select Enter.

    No JavaScript web framework

  9. Once process will be completed, it will show success message like below:

    Suggested Message

  10. After that execute following command one by one:
    gulp trust-dev-cert
    npm install jquery --save
    npm install @types/jquery --save
    npm install bootstrap --save
    npm install @types/bootstrap --save

Step 3 – To Write Code for Image Gallery

♦ Here we will replace 3 files with the following code, lets replace each file one by one. To edit web part in visual code, enter below command in command window:

Code.

1. ImageGalleryWebPart.module.scss

  • From the left pane, src webpart imageGallery ImageGalleryWebPart.module.scss

    image gallery web part

  • Replace the following code to this file and save it.

    ImageGalleryWebPart.module.scss

    @import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
     
    .albumblock{
      position: relative;
        overflow: hidden;
        padding-bottom: 50px;
        background-color: #f5f5f5;
        width: 32%;
        float: left;
        height: 100px !important;
        margin-left: 1%;
        margin-top: 3%;
        cursor: pointer;
    }
     
    .album-info{
      background-color: rgba(0,0,0,0.65);
      color: #515155;
      font-size: 1.2em;
      color: white;
      padding: 10px;
      position: absolute;
      bottom: -7px;
      left: 0;
      right: 0;
      font-family: sans-serif;
      font-size: 16px;
      text-align: center;
      font-weight: normal;
      height: 33px;
      cursor: pointer;
     
      h3{
        margin: 0;
        cursor: pointer;
        padding: 0 0px 5px 0px;
        font-size: 1.2em;
        vertical-align: middle;
        font-family: DubaiBold;
      }
    }
     
    .imgGalleyTitle{
      float: left;color: white !important;
      margin: 0.2em !important;
      font-size: 22px !important;
    }
     
    .btnBackStyle{
      float: right !important;
      width: 100px !important;
      background-color: rgb(64, 82, 171) !important;
      color: white !important;
      height: 30px !important;
      text-transform: uppercase !important;
      cursor: pointer !important;
      border: 3px solid !important;
      margin: 0.7em !important;
    }
     
    .btnBackStyle:hover{  
      background-color:  white !important;
      color: rgb(64, 82, 171) !important;  
      background-color: 0e83cd !important;
    }
     
    .ImageGallery {
      .container {
        max-width: 1000px;
        margin: 0px auto;
        box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
      }
     
      .row {
        @include ms-Grid-row;
        @include ms-fontColor-white;
        background-color: $ms-color-themeDark;
        padding: 20px;
      }
     
      .list {
        color: #333333;
        font-family: 'Segoe UI Regular WestEuropean', 'Segoe UI', Tahoma, Arial, sans-serif;
        font-size: 12px;
        font-weight: normal;
        box-sizing: border-box;
     
        line-height: 50px;
        list-style-type: none;
        width: 25%;
     
       }
     
       .listItem {
        color: #333333;
        box-shadow: 0 4px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
        vertical-align: center;
        font-family: 'Segoe UI Regular WestEuropean', 'Segoe UI', Tahoma, Arial, sans-serif;
        font-size: 14px;
        font-weight: normal;
        box-sizing: border-box;
        margin: 0;
        padding: 0;
        float: left;
        box-shadow: none;
        padding-top: 12%;
        *zoom: 1;
        height: 100px; 
        margin: 10;
        padding: 10;    
        margin: auto;   
        position: relative;
       }
     
      .column {
        @include ms-Grid-col;
        @include ms-lg10;
        @include ms-xl8;
        @include ms-xlPush2;
        @include ms-lgPush1;
      }
     
      .title {
        @include ms-font-xl;
        @include ms-fontColor-white;
      }
     
      .subTitle {
        @include ms-font-l;
        @include ms-fontColor-white;
        width: 100%;
        height: 100%;
      }
     
      .description {
        @include ms-font-l;
        @include ms-fontColor-white;
      }
     
      .button {
        // Our button
        text-decoration: none;
        height: 32px;
     
        // Primary Button
        min-width: 80px;
        background-color: $ms-color-themePrimary;
        border-color: $ms-color-themePrimary;
        color: $ms-color-white;
     
        // Basic Button
        outline: transparent;
        position: relative;
        font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
        -webkit-font-smoothing: antialiased;
        font-size: $ms-font-size-m;
        font-weight: $ms-font-weight-regular;
        border-width: 0;
        text-align: center;
        cursor: pointer;
        display: inline-block;
        padding: 0 16px;
     
        .label {
          font-weight: $ms-font-weight-semibold;
          font-size: $ms-font-size-m;
          height: 32px;
          line-height: 32px;
          margin: 0 4px;
          vertical-align: top;
          display: inline-block;
        }
      }
    }

2. Config.json

  • From the left pane, Config Config.json

    Config.json

    {
      "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
      "version": "2.0",
      "bundles": {
        "image-gallery-web-part": {
          "components": [
            {
              "entrypoint": "./lib/webparts/imageGallery/ImageGalleryWebPart.js",
              "manifest": "./src/webparts/imageGallery/ImageGalleryWebPart.manifest.json"
            }
          ]
        }
      },
      "externals": {
        "jquery": {
          "path": "node_modules/jquery/dist/jquery.min.js",
          "globalName": "jquery"
        },    
        "bootstrap": {
          "path": "/Style%20Library/cwp/bootstrap/js/bootstrap.min.js",
          "globalName": "bootstrap",
          "globalDependencies": [
            "jquery"
          ]      
        }    
      },
      "localizedResources": {
        "ImageGalleryWebPartStrings": "lib/webparts/imageGallery/loc/{locale}.js"
      }
    }

3. ImageGalleryWebPart.ts

  • Replace your tenant URL and Site Collection Name in yellow highlighted code.
  • From left pane, src webpart imageGallery ImageGalleryWebPart.ts
    <u><strong>ImageGalleryWebPart.ts</strong></u>
    import { Version } from '@microsoft/sp-core-library';
    import {
      BaseClientSideWebPart,
      IPropertyPaneConfiguration,
      PropertyPaneTextField
    } from '@microsoft/sp-webpart-base';
    import { escape } from '@microsoft/sp-lodash-subset';
    import { SPComponentLoader } from '@microsoft/sp-loader';
     
    import styles from './ImageGalleryWebPart.module.scss';
    import * as strings from 'ImageGalleryWebPartStrings';
     
    SPComponentLoader.loadCss('https://{tenant URL}/sites/{Site Collection Name}/Style%20Library/cwp/bootstrap/css/bootstrap.min.css');
     
     import('jquery'); 
    require('bootstrap');
     
    import {
      SPHttpClient,
      SPHttpClientResponse   
    } from '@microsoft/sp-http';
     import {
      Environment,
      EnvironmentType
    } from '@microsoft/sp-core-library';
    export interface IImageGalleryWebPartProps {
        description: string;
    }
     
    export interface ISPListAlbums {
        value: ISPListAlbum[];
    }
     
     export interface ISPListAlbum {
        Name: string;
        Id: string;  
        ServerRelativeUrl: string;
     
        ListItemAllFields:{AlbumPicURL:string;};
     }
     
     export interface ISPAlbumImages {
        value: ISPAlbumImage[];
     }
     
     export interface ISPAlbumImage {
        Name: string;
        Id: string;  
        ServerRelativeUrl: string;
     }
     
    export default class ImageGalleryWebPart extends BaseClientSideWebPart {
     
        public render(): void {
            this.domElement.innerHTML = `
    <div class="${ styles.ImageGallery }">
    <div class="${ styles.container }">
    <div id="spListContainer"> </div>
    </div>
    </div>`;
     
    this._renderListAlbumsAsync();
    }
     
    private _renderListAlbumsAsync(): void {
        // Local environment
        if (Environment.type == EnvironmentType.SharePoint || 
                 Environment.type == EnvironmentType.ClassicSharePoint) {
                     this._getAlbumData()
                       .then((response) => {
                           console.log(response.value);
                           this._renderListAlbum(response.value);          
                           var spanTiles=this.domElement.querySelectorAll('div.tiles'), i;
                           for(i=0;i<spanTiles.length;i++)
                           {
                               spanTiles[i].addEventListener('click', (event) => {
                                   this._ClickCalled(event);});
                           }
     
     
                       });        
                 }
        }
     
    public _renderAlbumImagesAsync(albumPath :string): void {
        // Local environment        
        this._getAlbumImages(albumPath)
          .then((response) => {
              console.log("images");
              console.log(response.value);
              this._renderAlbumImages(response.value);    
     
     
              $("#myCarousel").carousel({interval: 5000});          
          });        
     
    }
     
    private _renderAlbumImages(items: ISPAlbumImage[]): void {
        let html: string = '';
    html =`
    <h2 style="background-color: #4052ab; height: 50px;"><span class="${ styles.imgGalleyTitle }">Image Gallery</span><button id="btnBack" class="${ styles.btnBackStyle }">Back</button></h2>
    <div class="container" style="width: 1000px !important;">
    <div id="myCarousel" class="carousel slide" style="border: 1px solid #0e83cd;" data-ride="carousel" data-interval="5000">
    <ol class="carousel-indicators">
    <li class="active" data-target="#myCarousel" data-slide-to="0"> </li>
    <li data-target="#myCarousel" data-slide-to="1"> </li>
    <li data-target="#myCarousel" data-slide-to="2"> </li>
    </ol>
    <div class="carousel-inner"> </div>
    <a class="left carousel-control" href="#myCarousel" data-slide="prev"> <span class="sr-only"><</span> </a> <a class="right carousel-control" href="#myCarousel" data-slide="next"> <span class="sr-only" style="float: right !important;">></span> </a></div>
    </div>`;
     
    this.domElement.innerHTML=html;
    var btnBack=this.domElement.querySelector('#btnBack');
     
    btnBack.addEventListener('click', () => {
        this.render();
    });
     
     
    let slides: string = '',indicat: string = ''; 
    let index: number = 0; 
    items.forEach((item: ISPAlbumImage) => {            
     
        if(index==0)
        {
     
            indicat += `
    </li>
    <li class="active" data-target="#myCarousel" data-slide-to="${index}"> </li>
    <li>
    <pre lang="javascript">`;
     
            slides += `<div class="item active"><img style="width: 1000px; height: 500px;" src="${item.ServerRelativeUrl}" alt="Los Angeles" /></div>
    <pre lang="javascript">`;
        }
        else{
            indicat += `</li>
    <li data-target="#myCarousel" data-slide-to="${index}"> </li>
    <li>`;
            slides += `
    <div class="item"><img style="width: 1000px; height: 500px;" src="${item.ServerRelativeUrl}" alt="Los Angeles" /></div>`;
        }
        index++;  
    });
     
    const indicators: Element = this.domElement.querySelector('.carousel-indicators');
    indicators.innerHTML = indicat;    
     
    const listContainer: Element = this.domElement.querySelector('.carousel-inner');
    listContainer.innerHTML = slides;    
    }
     
    private _renderListAlbum(items: ISPListAlbum[]): void {
        let html: string = '';
    items.forEach((item: ISPListAlbum) => {            
        if(item.Name!="Forms" && item.Name.indexOf("_") ===-1)
        {
     
     
            html +=`
    <div class="tiles ${ styles.albumblock }"><img class="img-responsive" title="${item.Name}" src="${item.ListItemAllFields.AlbumPicURL}" alt="${item.Name}" />
    <div class="${ styles[">
    <h3>${item.Name}</h3>
    </div>
    </div>`;
        }
    });
     
    const listContainer: Element = this.domElement.querySelector('#spListContainer');
    listContainer.innerHTML =`<ul>`+ html+`</ul>`;    
    }
     
    private _ClickCalled(event) : void{    
        this._renderAlbumImagesAsync(event.target.attributes.serrel.value);
     
    }
     
    private _getAlbumData(): Promise {
        return this.context.spHttpClient.get(this.context.pageContext.site.absoluteUrl + "/_api/web/getFolderByServerRelativeUrl('Imagelib')/Folders?$select=Name,ServerRelativeUrl,Id,ListItemAllFields/AlbumPicURL&$expand=ListItemAllFields", SPHttpClient.configurations.v1)
          .then((response: SPHttpClientResponse) => {        
              return response.json();
          });
    }
     
    private _getAlbumImages(albumPath :string): Promise {
        return this.context.spHttpClient.get(this.context.pageContext.site.absoluteUrl + "/_api/web/getFolderByServerRelativeUrl('"+albumPath+"')/Files", SPHttpClient.configurations.v1)
          .then((response: SPHttpClientResponse) => {        
              return response.json();
          });
    }
     
    protected get dataVersion(): Version {
        return Version.parse('1.0');
    }
     
    protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
        return {
            pages: [
              {
                  header: {
                      description: strings.PropertyPaneDescription
                  },
                  groups: [
                    {
                        groupName: strings.BasicGroupName,
                        groupFields: [
                          PropertyPaneTextField('description', {
                              label: strings.DescriptionFieldLabel
                          })
                        ]
                    }
                  ]
              }
            ]
        };
    }
    }// JavaScript source code

♦ After editing the code of the above three mentioned files, run below command:

Gulp serve

♦ Open URL: /_layouts/15/workbench.aspx
♦ Add web part.
Add Web Part

Web Part

♦ For security reasons, we have hide URL. Now we will look how to deploy this web part to SharePoint from development environment.

Step 4 – To Deploy Web Part to SharePoint

    1. Execute following commands one by one
Gulp bundle –ship
Gulp package-solution --ship
  1. Now open the Image-Gallery folder in Explorer and go to SharePoint, go to solution
    “\Image-Gallery\sharepoint\solution”
  2. There will be file named “image-gallery.sppkg”.
  3. Open App Catalog site, go to Site Contents Apps for SharePoint Upload the .sppkg file
  4. Go to Site Content of the communication site, and add an application “image-gallery”.
  5. Add web part to your communication site like we did on workbench.

 

Comments