Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
[This topic is pre-release documentation and is subject to change.]
This tutorial shows how to build a code component for model-driven apps that is dependent on libraries that are contained in another component. Learn more about the dependent libraries preview
Goal
Follow the steps in this tutorial to create a library control and a control that depends on it. This tutorial contains the following steps:
- Build the library component: Create a component that only contains the reusable library. For simplicity, this control only contains the reusable library. There's no reason it couldn't also provide functionality.
- Build the dependent control: Create a component that uses the library defined in the library control and add it to a form of a model-driven app to verify that it works.
- Load dependent library on demand: Expand on the example to make the dependent component load the library resource on demand rather than have the framework load the library when the control loads.
Prerequisites
You should already know how to:
- Create and build a code component
- Package a code component
- Add code components to a model-driven app
1. Build the library component
This component doesn't provide any capabilities by itself. It's simply a container for the library.
The first step is to create a new component using the pac pcf init command:
pac pcf init -n StubLibrary -ns SampleNamespace -t field -npm
Define the library
You need a new declaration file (d.ts) to describe the objects and functions contained in your library. Create a new file in the root folder of your project named
myLib.d.ts
:declare module 'myLib' { export function sayHello(): string; }
We are going to expose our library as an UMD module, and we need to put the variable in the global scope. For this we need a new declaration file (d.ts). Create a new file in the root folder of your project named
global.d.ts
:/* eslint-disable no-var */ declare global { var myLib: typeof import('myLib'); } export { };
Update tsconfig.json to allow UMD modules and javascript code as follows:
Add the library
In your new control folder, add a new folder to contain your libraries libs
for this example create a new JavaScript file. This example uses a library named myLib-v_0_0_1.js
that has a single sayHello
function.
// UMD module pattern
var myLib = (function (exports) {
"use strict";
function sayHello() {
return "Hello from myLib";
}
exports.sayHello = sayHello;
return exports;
})(/** @type {import('myLib')} */ ({}));
Add Configuration data
Add a file named
featureconfig.json
in the root folder of the project.Add the following text to the
featureconfig.json
file:{ "pcfAllowCustomWebpack": "on", "pcfAllowLibraryResources": "on" }
Add a new
webpack.config.js
file in the root folder of your project. This configuration data ensures that the libraries aren't bundled with the control output. Bundling isn't necessary because they're already packaged separately when you build the project./* eslint-disable */ "use strict"; module.exports = { externals: { "myLib": "myLib" }, }
Add a reference to the library under the
resources
in the control manifest.
Add the library to window
The last step is to edit the index.ts
of the control to bind the library to the window.
import { IInputs, IOutputs } from "./generated/ManifestTypes";
export class StubLibrary
implements ComponentFramework.StandardControl<IInputs, IOutputs>
{
constructor() {
// Empty
}
public init(
context: ComponentFramework.Context<IInputs>,
notifyOutputChanged: () => void,
state: ComponentFramework.Dictionary,
container: HTMLDivElement
): void {
// Add control initialization code
}
public updateView(context: ComponentFramework.Context<IInputs>): void {
// Add code to update control view
}
public getOutputs(): IOutputs {
return {};
}
public destroy(): void {
// Add code to cleanup control if necessary
}
}
The library project should look like this:-
Build and package the library component
To finish the library component, complete the following steps as usual:
2. Build the dependent control
Now that you have a library control, you need a control to depend on it.
Create a new component using this command:
pac pcf init -n DependencyControl -ns SampleNamespace -t field -fw react -npm
Add a new feature control file in the root folder of your project called
featureconfig.json
containing the following text:{ "pcfResourceDependency": "on" }
Add the dependent resource in the control manifest.
Use the
schemaName
of the dependent control[solution prefix]_[namespace].[control name]
which you can find in thesolution.xml
file for the dependent component. The XML in the solution.xml file might look like this:<RootComponents> <RootComponent type="66" schemaName="samples_SampleNamespace.StubLibrary" behavior="0" /> </RootComponents>
<resources>
<code path="index.ts"
order="1" />
<platform-library name="React"
version="16.14.0" />
<platform-library name="Fluent"
version="9.46.2" />
</resources>
Add Global.d.ts
Since the StubLibrary is exposed as an UMD module, we need to put the variable in the global scope. For this we need a new declaration file (d.ts). Create a new file in the root folder of your project named global.d.ts
:
/* eslint-disable no-var */
interface MyLib {
sayHello(): string;
}
declare global {
var myLib: MyLib;
}
export { };
Use the library function
Update the component HelloWorld.tsx
file so that it uses a function from the dependent library. The library is loaded into the Window
object at runtime.
import * as React from 'react';
import { Label } from '@fluentui/react-components';
export interface IHelloWorldProps {
name?: string;
}
export class HelloWorld extends React.Component<IHelloWorldProps> {
public render(): React.ReactNode {
return (
<Label>
{this.props.name}
</Label>
)
}
}
Build and package the dependent component
To finish the dependent component, complete the following steps as usual:
Add the component to a form
Navigate to the form and you should see the component show the text
Hello from myLib from Dependency
.
3. Load dependent library on demand
You can expand on this example by changing the dependent component to load the library resource on demand rather than have the framework load the library when the component loads. On demand load behavior is useful if the libraries being used by the control are large and would increase the load time of the form.
Specify on demand load behavior
To specify on demand load behavior, modify the control manifest of the component created in 2. Build the dependent control.
<resources>
<dependency type="control"
name="samples_SampleNamespace.StubLibrary"
order="1" />
<code path="index.ts"
order="2" />
<platform-library name="React" version="16.14.0" />
<platform-library name="Fluent" version="9.46.2" />
</resources>
Modify the dependent component to load library on demand
Modify the HelloWorld.tsx
to add a state and methods to update it once the dependency loads.
import * as React from 'react';
import { Label } from '@fluentui/react-components';
export interface IHelloWorldProps {
name?: string;
}
export class HelloWorld extends React.Component<IHelloWorldProps> {
public render(): React.ReactNode {
return (
<Label>
{ window.myLib.sayHello() + " from Dependency" || "Hello World"}
</Label>
)
}
}
Update index.ts
When the script is loaded on demand, you need to make slight adjustments to how the component is created and initialized. For example, new variables for references to the context and the container to update the state.
Most importantly add a getActions
method to react to the On Load and request the dependent control to be loaded.
import { IInputs, IOutputs } from "./generated/ManifestTypes";
import { HelloWorld, IHelloWorldProps } from "./HelloWorld";
import * as React from "react";
export class DependencyControl implements ComponentFramework.ReactControl<IInputs, IOutputs> {
private notifyOutputChanged: () => void;
constructor() {
// Empty
}
public init(
context: ComponentFramework.Context<IInputs>,
notifyOutputChanged: () => void,
state: ComponentFramework.Dictionary
): void {
this.notifyOutputChanged = notifyOutputChanged;
}
public updateView(context: ComponentFramework.Context<IInputs>): React.ReactElement {
const props: IHelloWorldProps = { name: 'Power Apps' };
return React.createElement(
HelloWorld, props
);
}
public getOutputs(): IOutputs {
return { };
}
public destroy(): void {
// Add code to cleanup control if necessary
}
}
Final steps
- Update the version number of the control in the
ControlManifest.Input.xml
and the version in theSolution.xml
- Rebuild, package, deploy, and publish the solution with the updated control.
Verify results
Now, when the page loads you see the control load with Loading...
displayed.
Once the page loads, the control updates to display Hello from myLib Dependency On Demand Load
.