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
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:
And let's start with the web part implementation. As a template we'll use Web Part with React Framework.
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:
We also need to implement component's state to store selected task:
Now we can modify render method to get templates from the factory and render them inside the component.
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.
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:
Cool, isn't it? We use one dynamic React component from another! Now, let's implement default templates factory that will work with the components mentioned above:
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:
Now we can implement ITemplateFactory interface in our default export - TemplatesLibrary class:
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
Next, let's implement TemplateFactoryLoader class that will asynchronously load needed template factory. Additionally, it will cache last loaded factory to avoid additional traffic:
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!
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.