Related Service

Mobile App Development

A comprehensive approach to building reliable software

Learn More
Published: 19 September 2018

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
  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.

    No JavaScript web framework

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

    Suggested Message

  17. 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<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

  • Leave a message...

Related Articles

Microsoft Graph with SharePoint Framework

28 January 2019

One question that comes to our mind while require accessing Azure Active Directory resources is whether we should make use...

Microsoft Graph and Azure Active Directory Graph API

26 December 2018

In our previous blog, we have covered the basic knowledge about Microsoft Graph and Azure Active Directory Graph API. From...

Microsoft Recurrence Flow for Office 365

12 December 2018

Huge organizations have many requirements for the automation of tasks in one or another way. It is always beneficial for...