19
Sep
2018

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

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”:
  3. Picture Library

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

  6. Scroll down and click on “Create column”:
  7. create column

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

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

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

  17. Once process will be completed, it will show success message like below:
  18. Suggested Message

  19. After that execute following command one by one:
  20. 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<IImageGalleryWebPartProps> {
 
    public render(): void {
        this.domElement.innerHTML = `
     <div class="${ styles.ImageGallery }">
       <div class="${ styles.container }">
         <div id="spListContainer" />
       </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 }"><span>Back</span></button></h2>
    <div class="container" style='width:1000px !important'>    
    <div id="myCarousel" class="carousel slide" data-ride="carousel" data-interval="5000" style='border: 1px solid #0e83cd;'>
 
      <ol class="carousel-indicators">
        <li data-target="#myCarousel" data-slide-to="0" class="active"></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="glyphicon glyphicon-chevron-left"></span>
        <span class="sr-only"><</span>
      </a>
      <a class="right carousel-control" href="#myCarousel" data-slide="next">
        <span class="glyphicon glyphicon-chevron-right"></span>
        <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 data-target="#myCarousel" data-slide-to="${index}" class="active"></li>`;
 
        slides += `
        <div class="item active">
          <img src="${item.ServerRelativeUrl}" alt="Los Angeles" style="width:1000px;height:500px;" />                      
        </div>`;
    }
    else{
        indicat += `
        <li data-target="#myCarousel" data-slide-to="${index}"></li>`;
        slides += `
        <div class="item">
          <img src="${item.ServerRelativeUrl}" alt="Los Angeles" style="width:1000px;height:500px;" />                      
        </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 serrel="${item.ServerRelativeUrl}" class="tiles ${ styles.albumblock }">
            <img class="img-responsive" serrel="${item.ServerRelativeUrl}" src="${item.ListItemAllFields.AlbumPicURL}" alt="${item.Name}" title="${item.Name}">
            <div serrel="${item.ServerRelativeUrl}" class="${ styles["album-info"] }"> 
              <h3 serrel="${item.ServerRelativeUrl}">${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<ISPListAlbums> {
    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<ISPAlbumImages> {
    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
  2. Gulp bundle –ship
    Gulp package-solution --ship
    
  3. Now open the Image-Gallery folder in Explorer and go to SharePoint, go to solution
    “\Image-Gallery\sharepoint\solution”
  4. There will be file named “image-gallery.sppkg”.
  5. Open App Catalog site, go to Site Contents  Apps for SharePoint  Upload the .sppkg file
  6. Go to Site Content of the communication site, and add an application “image-gallery”.
  7. Add web part to your communication site like we did on workbench.


Comments

3 thoughts on “Client Side Web Part – Image Gallery using SPFx in Modern Communication Site”

  1. “Hey, I went through your write up, I appreciate you effort absolutely.

    Hereby, I have a tricky requirement in context. Please help me out with it – Can we customize the modern user experience look using client-side web parts like users used to do using ‘script editor’ web part?

    Thanks in Advance!”

    1. Thanks for going through the post. COming to your query.
      “Yes, the user can customize the modern user experience, using the client-side web part along with the ‘extensions’. The user just needs to select ‘Extension’ instead of web part while scaffolding web part using Yeoman generator. There are three types of customizer user can select which are as below:
      – Application Customizers: It adds scripts inside the page and it can accesses Adds scripts to the page, and accesses HTML element placeholders and the user can customize them.
      – Field Customizers: It provides a way to view modified fields.
      – Command Sets: User can this extension to extended new actions and it provides a client-side code to implement new actions.

      Hope that tends helpful!”

  2. Seems you shared a complete set of information for developing the client side web part in Modern Communication Site. I wish to know that can SPFx framework be used to create the client-side web part in SharePoint on-premises?

Leave a Reply

Your email address will not be published. Required fields are marked *