CategoryDevelopment

Fixing IE11 Compatibility Problems in SPFx with Babel

I recently ran into an annoying problem in one of my SPFx solutions; while it worked perfectly in Chrome, the Web Part didn’t even load in IE11. After a bit of digging, it turned out that the “SCRIPT1002” errors I was getting were caused by one of the npm packages I was using, which used the “class” keyword – a keyword introduced by ES6 but not supported by IE11. Worse still, while there are many polyfills for ES6 features, this is not possible for the “class” keyword.

The common solution to this kind of problem is to use a transpiler; a module that compiles existing code (even inside your node_modules folder) into JavaScript readable by older browsers. Babel-loader is a pretty well-known transpiler package that works with webpack – and here’s how you get it to work in your SPFx solution:

To use babel-loader in your SPFx solution, you first have to install it;

npm install -D babel-loader @babel/core @babel/preset-env webpack

Next, in your gulpfile.js, insert the following code just before the line that says build.initialze(gulp);

build.configureWebpack.mergeConfig({
  additionalConfiguration: (generatedConfiguration) => {
    generatedConfiguration.module.rules.push(
      {
        test: /\.m?js$/, use:
        {
          loader: "babel-loader",
          options:
          {
            presets: [["@babel/preset-env",
              {
                targets: {
                  "ie": "11"
                }
              }]]
          }
        }
      }
    );

    return generatedConfiguration;
  }
});

By doing this, you add an additional rule to your webpack configuration that tells the babel-loader to transpile your files into an IE11 compatible syntax.
Note that most default babel-loader configurations will add an “exclude” rule for the node_modules folder, but in this case we want Babel to transpile our npm packages since they were causing our problems.

Responsive SPFx Controls with Fabric UI React

Have you ever built a control that you want to have behaving differently depending on whether you’re working on a mobile device or a desktop? For example, showing a callout when you’ve got a full resolution desktop screen available but using a screen covering panel on your mobile phone? Well, Fabric UI React continues to be a life saver and makes implementing this almost ridiculously easy with its ResponsiveMode utlility.

On a high resolution screen, the extension renders a Callout

On small screens, the extension uses a full-screen panel

The first thing you need to do is import the necessary interfaces and classes;

import { IWithResponsiveModeState, ResponsiveMode, withResponsiveMode } from 'office-ui-fabric-react/lib/utilities/decorators/withResponsiveMode';

Next, create an extension for your control’s Props interface to also implement IWithResponsiveModeState

export interface INavigationCalloutInternalProps extends INavigationCalloutProps, IWithResponsiveModeState { }

Finally, add the ‘@withResponsiveMode’ decorator to your class and make sure that your control uses the extended Props interface;

@withResponsiveMode
export class NavigationCallout extends React.Component<INavigationCalloutInternalProps, INavigationCalloutState> {

Now we’re all set to make our control change its behavior based on screen size. In our render() function, we can now do the following:

const isSmall = this.props.responsiveMode! &lt;= ResponsiveMode.medium;
{ isSmall ? (
      <Callout></Callout>
   ) : (
      <Panel></Panel>
   )
}

Pro Tip: where did my deleted SharePoint Hosted Add-In listitem go to?

A client for whom we had built a SharePoint Hosted Add-in consisting of multiple inter-connected SharePoint lists, deleted a list item from one of the lists. This list item was used in a couple of the other lists as a lookup field. The problem with this constellation is that you cannot simply recreate the same list item and hope for the lookup field to be fixed, as this connection relies on the list item’s ID. Unfortunately though, you cannot set the ID field for a new list item.

The solution seemed to be simple however: open up the recycle bin for the Add-in (Add-in URL plus ‘_layouts/15/RecycleBin.aspx’), find the deleted item and recover it. We didn’t immediately realize that a SharePoint Hosted Add-in does not contain a recycle bin!

After some consideration we figured that we should check the recycle bin of the site containing the Add-in, and whaddayaknow, there was the missing list item! We could simply recover the list item from the recycle bin, restoring all of the broken relationships in the SharePoint Hosted Add-in as well. Come to think about it: the name ‘SharePoint Hosted Add-in’ kind of gives it away already: check the containing site for stuff that’s missing!

Working with SPFx projects in Visual Studio 2017 (or: how to avoid Visual Studio Code)

I like the SharePoint Framework. It’s new, it’s fast and it’s miles ahead of what we’ve had before. I even like Node.js, NPM, Yeoman and the rest of the development ecosystem that comes with it. What I don’t like though, is Microsoft’s insistence that I should use Visual Studio Code. Even the name bothers me – Visual Studio Code – what the hell, Microsoft; what did you think I was using your previous versions of Visual Studio for? Novels? Why can’t I stick to good old Visual Studio 2017 with its plethora of features and extensions?

It’s still possible to keep working in Visual Studio 2017 of course, although you won’t have the comfort of Project Templates to help you along. My solution is as follows:

  • Create an empty Class Library Project
  • Open a command window to the path of the project (not the solution)
  • Run the SPFx Yeoman Generator (yo @microsoft/sharepoint)
  • Keep working in Visual Studio 2017 stubbornly

Note: Visual Studio 2017 has some issues with React so don’t expect to be able to rely on IntelliSense to show you where the errors in your code are. I’ll update this post if I ever find a solution to this, but for the time being I’m just happy to be able to work in the IDE of my choice.

Running the project

Wouldn’t it be nice though if pressing F5 would automatically execute a ‘gulp serve’ command? Well; that’s easy to set up too.

In the Properties of your project, navigate to the Debug tab and set the Launch property to ‘Executable’.

Next, enter ‘cmd’ as your executable.

Finally, set the Application arguments to

/K gulp serve

This will tell Visual Studio to open a command window and execute gulp serve when the project is started.

The Visual Studio Project Template

Wouldn’t it be nice though if there was a pre-made Visual Studio Project template and you didn’t have to  go through all these steps to run your SPFx project? Well, there is – kind of. The SPFx Project Template Extension – which basically runs the steps outlined in this blog post but automated in the form of a project template – is very promising but doesn’t seem to be actively maintained and only works for webparts, not extensions. As such, for the time being I can’t really recommend it, but it’s a good initiative to retain the ease-of-use found in Visual Studio 2017  for SPFx projects.

What happened to the SP Online Social REST API?

Last month I encountered a strange issue with the SharePoint Online REST API when trying to implement a feature for a social intranet we are building for one of our customers. The feature depends on the SocialRestFollowingManager resource, as described on this page on docs.microsoft.com.

I had already used this particular piece the REST API in other projects in SharePoint 2013 Server using the client-side coding library. Everything seemed to be working at first sight, as I could leverage this functionality to follow objects in SharePoint Online: sites, tags and documents.

The snag I hit was when I was trying to follow and unfollow documents specifically. As we have probably all noticed, the following of a single document from a document library has disappeared from the context menus in the SharePoint Online modern experience. This might already be an indication that Microsoft is not really willing to continue supporting this otherwise brilliant feature. We have had a lot of positive customer feedback on our implementation of this feature, where we can create a centrally located pinboard containing the direct links to all of the SharePoint content that you are following.

Whatever! Dive in to the code already!

When we take a look at the code, the funny thing is that it seems to work at first. The following of a document is done through the SharePoint UI: by switching a SharePoint document library to the classic view and using the context menu to follow the item (‘Volgen’ is Dutch for ‘Follow’):

Now for the ‘unfollowing’:

We use the function stopFollowDocument  to unfollow this document. In this function, we create an async ajax call to the social REST API, located under /_api/social.following containing the json-ized object (our document’s information) that needs to be unfollowed. The call is executed and the ajax call proceeds to its ‘success’-function. So far so good! But when we proceed to check if the document is now ‘unfollowed’, which we can do through the REST API url /_api/social.following/my/followed(types=2), we can see that actually nothing happened: the document is still being followed! Let’s bear in mind that the same code does work for tags and sites, but as shown, not for documents:

Here you haz teh Code

var documentUrl = "https://TENANTNAME.sharepoint.com/Gedeelde%20%20Documenten/demofile.txt";
var followingManagerEndpoint = "https://TENANTNAME.sharepoint.com/_api/social.following";

// Make the current user stop following a document.
// The request body includes a SocialActorInfo object that represents
// the document to stop following.
function <strong>stopFollowDocument</strong>() {
  $.ajax( {
    url: followingManagerEndpoint + "/stopfollowing",
    type: "POST",
    data: JSON.stringify( {
      "actor": {
        "__metadata": {
          "type":"SP.Social.SocialActorInfo"
        },
        "ActorType":1,
        "ContentUri":documentUrl,
        "Id":null
      }
    }),
    headers: {
      "accept":"application/json;odata=verbose",
      "content-type":"application/json;odata=verbose",
      "X-RequestDigest":$("#__REQUESTDIGEST").val()
    },
    success: function () {
      alert('The user has stopped following the document.');
    },
    error: requestFailed
  } );
}
 
function requestFailed(xhr, ajaxOptions, thrownError) {
  alert('Error:\\n' + xhr.status + '\\n' + thrownError + '\\n' + xhr.responseText);
}

// Make the current user start following a document.
// The request body includes a SocialActorInfo object that represents
// the document to follow.
// The success function reads the response from the REST service.
function <strong>followDocument</strong>() {
  $.ajax( {
    url: followingManagerEndpoint + "/follow",
    type: "POST",
    data: JSON.stringify( {
      "actor": {
        "__metadata": {
          "type":"SP.Social.SocialActorInfo"
        },
        "ActorType":1,
        "ContentUri":documentUrl,
        "Id":null
      }
    } ),
    headers: {
      "accept":"application/json;odata=verbose",
      "content-type":"application/json;odata=verbose",
      "X-RequestDigest":$("#__REQUESTDIGEST").val()
    },
    success: function (responseData) {
      stringData = JSON.stringify(responseData);
      jsonObject = JSON.parse(stringData);
      var statusMessage = {
        0 : 'The user has started following the document. ',
        1 : 'The user is already following the document. ',
        2 : 'An internal limit was reached. ',
        3 : 'An internal error occurred. '
      }
      alert(statusMessage[jsonObject.d.Follow] + 'Status code = ' + 
      jsonObject.d.Follow);
    },
    error: requestFailed
  } );
}

 

 

Now for the other way ‘round: when I use the followDocument() call to try to follow the document (whether it was already followed or not), I get a 404 error back through the error function ‘requestFailed’:

followDocument -> Error:\n404\n\n{“error”:{“code”:”3, Microsoft.Office.Server.Social.SPSocialException”,”message”:{“lang”:”en-US”,”value”:”The target of the action was not found. Internal typename: Microsoft.Office.Server.UserProfiles.FollowedContentException. Internal errorcode: 12.”}}}

Isn’t this weird? What’s up Microsoft, are you secretly getting rid of (parts of) this API?

Workaround

Fortunately we can work around this issue by leveraging the SP.Social.SocialFollowingManager in our code instead of the REST API:


var context = SP.ClientContext.get_current();
var socialManager = new SP.Social.SocialFollowingManager(context);
var socialObject = new SP.Social.SocialActorInfo();
socialObject.set_actorType(objectType);
var result = socialManager.follow(socialObject);
context.executeQueryAsync(…);

 

Conclusion

It seems wise to start refactoring your ‘social’ code for SharePoint Online to use the SP.Social.SocialFollowingManager instead of using ajax REST API calls to the /_api/social.following endpoint. Still I haven’t seen an official statement of Microsoft saying that this feature will be discontinued or whether this might be a bug. Tech support of MS wasn’t able to give me an answer to this question.

Upgrading SPFx 1.0 React WebParts

When the SharePoint Framework first came out I spent a good amount of time porting over ‘old’ webparts to the new Modern standard. When someone discovered a bug in one of these SPFx 1.0 apps some time ago it was the perfect occasion to upgrade these to 1.4.1, especially since it is now possible to embed all client side content (images, javascript) inside the app itself, making the process of putting the scripts onto a CDN no longer necessary. What I quickly discovered though is that this isn’t as easy as just updating a few packages in NPM. To spare you the trouble of having to find this out for yourself, follow this guide to easily update your webparts.

Step 0: How not to update

At first I thought that updating would be as simple as just using NPM to update each individual package. This turned out to be the wrong choice, since package updates are not the only thing that has changed since the first release of SPFx and it doesn’t always necessarily use the latest version of 3rd party packages.

There’s a (relatively) easy way though; let the Yeoman scaffold tell you which packages to use. The steps below will guide you through the process of getting the latest generator and checking which changes you need to make to your existing project.

Step 1: Updating Yeoman and the SharePoint Generator

First of all we’ll update both Yeoman and the SPFx generator. Use the following commands in a command console window:

npm install yo@latest -g
npm install @microsoft/generator-sharepoint@latest -g

Step 2: Generate a new webpart

Navigate the command console to a temp folder and use Yeoman to generate a new app scaffold:

yo @microsoft/sharepoint

When Yeoman asks you what type of component you want to create, choose the same type as the app you’re going to upgrade.

Step 3: Copy the packages.json content

Now that Yeoman has generated a new app scaffold, we can open its package.json file to see exactly which packages/versions are currently required for the latest version of SPFx.

Next, find the package.json file in the project you’re trying to upgrade and make sure that the packages and versions under dependencies and devDependencies match. In my case I just shamelessly copy/pasted the entire file content (make sure you change the name afterwards) but if you added any additional packages which are not part of SPFx by default you might have to be a bit more careful.
Once you’ve updated your file, open a command window to the project you’re updating and use the following commands;

gulp clean

The command above cleans up the SharePoint build folders.

npm install

Next, tell NPM to install your packages, which it will do according to your new packages.json.

gulp build

Now we’ll attempt to build the webpart. You might be disappointed to learn that you’re still getting errors – but this leads us to the next step…

Step 4: Fix errors

Even though you’ve updated all your packages, you’ll probably still be getting plenty of errors in your build. This is the trickiest step, since what you’ll have to do to fix them is heavily dependent on your coding style and the changes that have been in the latest SPFx packages. So the changes that I had to make might not be the same for you, but I’ll at least share some pitfalls I’ve encountered to spare you the same troubles.

React Components

One of the build breaking changes in React is that when extending Components, you can no longer use void as the parameter for the component state. In my original webpart there were a lot of components defined like this:

export class Filter extends React.Component<IFilterProps, void> {

…which should be changed to:

export class Filter extends React.Component<IFilterProps, {}> {
Node_modules Folder

If your code still doesn’t build and you’re pulling out your hair in frustration, there might be some old files lingering in your node_modules folder. Try deleting the node_modules folder for your project and running npm install again.

Step 5: Success!

If your code builds again, congratulations! If not, I hope that I’ve at least managed to get you a few steps ahead. For me, the steps above were enough to update my SPFx 1.0 webpart to 1.4.1, but of course I can’t guarantee that the same will be true for everyone.

© 2019 ROphelders.com

Theme by Anders NorénUp ↑