6/25/2019

In the first post we've already discussed the objective, as well as basics, or strategy, of how to create fully functional React templates (with props, events handling etc.) and dynamically load them using new SharePoint Framework project type - Library Component.
This post covers implementation details: main web part, dynamic template factory loading, and library component with alternative UI.
If you don't want to read the post - the code is available here. Feel free to use it in any way you want.
Previous post - Basics

Web Part

As mentioned in the previous post, we'll be creating 2 different UIs for Tasks list.
The default one, implemented as part of client side web part, will look like that:
And alternative one, implemented as a separate Library Component:

Initial Preparation

And let's start with the web part implementation.
As a template we'll use Web Part with React Framework.

Contracts

As mentioned in the first post, there are "contracts" that must be used in all implementations of components. And in this solution all of them are defined in CommonTypes.ts file that should be copied in the projects. So, let's create common folder in web part's code directory and copy the file there.
Now we have interfaces for templates factory, as well as for all dynamic React components: Tasks List, Task, and Task Details.

Web Part's Main Component

Now let's modify web part's main component (in this example - TasksTemplates) to render what we really need instead of default "Learn more" UI.
At this point of time we know that there is some ITemplateFactory interface with getTemplateComponent method. We don't know (yet) how to instantiate it, but we can use the interface definition to dynamically get needed templates and render them in TasksTemplates component.
So, first, let's update ITasksTemplatesProps to expect templates factory as well as list of tasks to be rendered:

Two additional interesting things to mention.
First, when getting templates from the factory we're casting them to React.ComponentClass<IProps>. It allows us to use TypeScript's type checking for dynamic component's props.
Second, TaskList component has a property taskTemplate: React.ComponentClass<ITaskProps>. And using such a property we can pass one template to another. So, if needed, we can even combine templates from different sources and pass them one to another.

TaskList, Task, TaskDetails - Default Implementation

Next step is to implement default templates for TaskList,Task and TaskDetails components.
There is nothing interesting in Task and TaskDetails implementations. You can find them in GitHub repo for the solution.
But let's look at TaskList implementation. As shown above, one of the properties for the list is taskTempalte that contains a component to render each task in the list.
To use it we need to apply the same approach as in TasksTemplates component - we'll get the value from props and store it in capitalized variable:

Library Component with Alternative Templates

Library Component is a new type of SPFx project that is GAd in version 1.9.0. It allows developers to create some "shared" code that is deployed to App Catalog and referenced by other SPFx components (Web Parts, Extensions, and even other Library Components).
If you run SPFx Yeoman generator version 1.9.0 or higher, you'll see a new option - Library.
This option will generate new SPFx project with a well-know structure - same as for web parts and extensions.
The main differences here is not empty index.ts file in the root of src folder. In case of Library Component it actually shows what will be exported from the library. In default implementations it's a class with a single property called name to return library's name:

It's important to mention that while scaffolding a Library Component you need to set Do you want to allow the tenant admin the choice of being able to deploy the solution to all sites immediately without running any feature deployment or adding apps in sites? to Yes if you want it to be available across tenant.
Let's modify this class to be a template factory.
First, similarly to web part, we need to copy our "contracts" - CommonTypes.ts to the project. And, again, I'll copy it in common folder.
Next - implement all used components - TaskList,Task and TaskDetails. The implementation is pretty standard so no need to post it here. The only thing to mention - by default Library Component project doesn't reference React library. So, for our implementation we'll need to install additional modules:

And that's it! We have our alternative UI implementation! Pretty easy, right?
Now we can package Library Component solution, deploy it to App Catalog and make it available for other SPFx solutions in the tenant.

Secret Ingredient: SPComponentLoader

Now we have the web part, we have default UI for our Tasks list, we have a separate library with alternative UI. But how will we select the appropriate templates factory and load templates?
The answer is simple - SPComponentLoader. (Unfortunately, class definition was removed from the documentation for some reason. But you can find some mentions of it here, here, here and here. There is also a separate NPM package for the loader sp-loader). We can use this class to either load any JavaScript file, or load an SPFx component by its ID. The latter is exactly what we can use for Library Components as they're still SPFx components with defined ID.
For that purposes let's define a componentId property in our web part. If the property is set then we'll load the component by ID using SPComponentLoader, otherwise we'll just use default template factory and default UI.

Final step is to request template factory from our web part. Note, that loadTemplateFactory is an asynchronous method. To use it during web part's rendering we'll need to override isRenderAsync property to return true and call this.renderCompleted(); to notify that we're finished with the rendering:

Now we can switch the UI by providing a single property in web part's property pane!
To test that, run the web part in SharePoint-hosted workbench, copy the id from Component Library's manifest (in this sample it is cd6e18e2-a8f3-4f97-9f07-7dfcecdd47ed) and paste it into Component Id property.
Voi-la!

Conclusion

SharePoint Framework gives us incredible and powerful instruments to develop customizations.
And Library Component project type is a new step forward to simplify our implementation, code reusing and sharing.
Together with SPComponentLoader it allows us to implement React templates with dynamic loading of components. Similarly, it can be used to develop different data adapters, dynamic behaviors, etc.
I'm really excited and looking forward to including Library Component in GA version of SPFx.

2 comments:

Hey this was a great read! I'm curious how you would handle the situation where different templates have different web part property configurations. Ex: We want to allow the user to configure than PanelSize of the TaskDetails template. Would you just expand your "contract" so that in addition to a React compononent, you also return something like "getPropertyPaneGroups()"?