SharePoint Framework End User Managed Placeholder Content Part 4

While this was originally planned as a 3-part series, something recently happened that has caused us to need to make some changes to our code. The cause of this effect is the release of the SharePoint Framework 1.2.0 version, which includes Release Candidate code for SharePoint extensions.

Series Recap:

  1. Part 1 – Create the Content Repository for managing your Placeholder content
  2. Part 2 – Inject your managed Placeholder content into your pages
  3. Part 3 – Cache placeholder content in localStorage
  4. Part 4 (New!) – Update code for the 1.2.0 SharePoint Framework Extensions Release Candidate

Rather than going back and editing all of the previous posts that showed the code utilized during the preview of the SharePoint extensions, I wanted to instead create a new post that highlights the differences between preview and RC.

Let’s take a look at our finished code, first, in case you’re primarily interested in just replacing the contents of your PlaceholdersExtensionApplicationCustomizer.ts file:

import { override } from '@microsoft/decorators';
import { Log } from '@microsoft/sp-core-library';
import * as pnp from 'sp-pnp-js';
import { BaseApplicationCustomizer, PlaceholderName, PlaceholderContent } from '@microsoft/sp-application-base';
import { PlaceholderItems, IPlaceholderItem } from './PlaceholderItems';
import * as strings from 'PlaceholdersExtensionApplicationCustomizerStrings';

const LOG_SOURCE: string = 'PlaceholdersExtensionApplicationCustomizer';

/**
 * If your command set uses the ClientSideComponentProperties JSON input,
 * it will be deserialized into the BaseExtension.properties object.
 * You can define an interface to describe it.
 */
export interface IPlaceholdersExtensionApplicationCustomizerProperties { }

/** A Custom Action which can be run during execution of a Client Side Application */
export default class PlaceholdersExtensionApplicationCustomizer
  extends BaseApplicationCustomizer<IPlaceholdersExtensionApplicationCustomizerProperties> {

  @override
  public onInit(): Promise<void> {
    Log.info(LOG_SOURCE, `Initialized ${strings.Title}`);

    //Configure PnP JS Core
    pnp.setup({
      spfxContext: this.context
    });

    try {
      //Grab the placeholders that the page currently offers
      let pagePlaceHolders: ReadonlyArray<PlaceholderName> = this.context.placeholderProvider.placeholderNames;

      //Get our list of placeholders
      PlaceholderItems.GetItems(this.context.pageContext.web.id.toString()).then((data: IPlaceholderItem[]) => {

        //Loop through returned placeholders
        data.forEach((element: IPlaceholderItem) => {

          //Look for a matching placeholder in the list of official placeholders
          let index: Number = pagePlaceHolders.indexOf(PlaceholderName[element.Title]);
          if (index !== -1) {

            //Grab the placeholder
            let currentPlaceholder: PlaceholderContent = this.context.placeholderProvider.tryCreateContent(PlaceholderName[element.Title]);

            //Insert our content
            currentPlaceholder.domElement.innerHTML = element.SPFxContent.replace(/\u200B/g, '');
          }
        });
      }
      );
    }
    finally {
    }

    return Promise.resolve<void>();
  }
}

Now, let’s dive into the key changes that were made, so that you can understand how the code has evolved from our original implementation.

  1. The first thing we’ll see change in this file is that we’re now including the ‘PlaceholderName’ class from the SP application base. This is to handle the type of data that comes back to us when we’re looking at the available placeholders on the page.
    import { BaseApplicationCustomizer, PlaceholderName, PlaceholderContent } from '@microsoft/sp-application-base';
    
  2. The second change is the addition of code to initiate PNP in our extension. Without this code, the extension will not work properly on all pages within your site. (Thanks, Chris Kent, for this suggestion.)
    //Configure PnP JS Core
        pnp.setup({
          spfxContext: this.context
        });
    
  3. Next, I want to highlight that we’re no longer using onRender to place the code that outputs our content into the page’s available placeholders. The reason is that this method no longer exists in the RC code, so we instead need to move that code into the existing onInit method.
  4. Our next change is a game changer, as we’ve gone from retrieving an array of string values to determine the available placeholders, to instead being returned an array of enum values that list the available placeholders.
    //Grab the placeholders that the page currently offers
    let pagePlaceHolders: ReadonlyArray<PlaceholderName> = this.context.placeholderProvider.placeholderNames;
    
  5. As we look at the title of entries in our SPFx Placeholders list, we now have to change our code to see if there is an enum representation of that value, instead of simply checking to see if the string exists. That is done via the below, where we attempt to find a value within the enum that equals the title of our list item.
    //Look for a matching placeholder in the list of official placeholders
    let index: Number = pagePlaceHolders.indexOf(PlaceholderName[element.Title]);
    
  6. Once we’ve found a match, we now utilize a new method (tryCreateContent) to grab the placeholder and set it’s value. Note that this method also removes the need to handle the onDispose event previously found in the preview code.
    //Grab the placeholder
    let currentPlaceholder: PlaceholderContent = this.context.placeholderProvider.tryCreateContent(PlaceholderName[element.Title]);
    
  7. The last thing we’ll do is another tip offered by Chris Kent, where we replace a stray character SharePoint often stores within our placeholder content, in order to ensure our content renders properly.
    //Insert our content
    currentPlaceholder.domElement.innerHTML = element.SPFxContent.replace(/\u200B/g, '');
    
  8. The last change isn’t in your code, but you’ll want to update your ‘PageHeader’ and ‘PageFooter’ entries in the SPFx Placeholders list to ‘Top’ and ‘Bottom’, respectively, as they’ve been renamed. What’s nice about this solution is that changes like these are easy to account for, and we’ll still be able to add more placeholders in our list once Microsoft makes additional ones available.

That’s essentially it! Once we’ve made these changes, we’re now ready to continue utilizing our feature with the 1.2.0 Release Candidate code.

Cheers,
Matt

Matt Jimison

Microsoft 365 Geek - Husband, father, lover of basketball, football, smoking / grilling, music, movies, video games, and craft beer!

Leave a Reply

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