チュートリアル: モデル駆動型アプリのフィールド コンポーネントを作成する
このチュートリアルでは、モデル駆動型アプリの field
コンポーネントを作成し、デプロイ、構成、Visual Studio Code を使用してフォーム上でコンポーネントのテストを行います。 このコードコンポーネントは、フォームに一連の選択肢を表示し、各選択肢の値の横にアイコンを表示します。 このコンポーネントは、選択肢列定義 (メタデータ) や列レベル セキュリティなど、モデル駆動型アプリの高度な機能の一部を使用します。
これらに加えて、コード コンポーネントがベスト プラクティスのガイダンスに従っていることも確認する必要があります。
- Microsoft Fluent UI を使用して一貫性とアクセシビリティを管理します
- 設計時と実行時の両方でコード コンポーネント ラベルをローカライズします
- 再利用性を高めるために、コード コンポーネントがメタデータ駆動型であることを確認してください
- コード コンポーネントがフォーム ファクターと利用可能な幅に応じて表示されることを確認し、スペースが限られている場合にはアイコン付きのコンパクトなドロップダウンを表示します
コード
PowerApps-Samples/component-framework/ChoicesPickerControl/ から完全なサンプルをダウンロードできます。
新しい pcfproj
プロジェクトを作成します
注意
開始する前に、すべてのコンポーネントの前提条件を満たしていることを確認してください。
新規 pcfproj
の作成:
コード コンポーネントを保持する新しいフォルダを作成します。 たとえば、
C:\repos\ChoicesPicker
などとします。Visual Studio Code を開き、ファイル > フォルダを開く に移動し、上記手順で作成済みの
ChoicesPicker
フォルダを選択します。 Visual Studio Code のインストール時にウィンドウズ エクスプローラーの拡張機能を追加した場合は、フォルダ内のコンテキスト メニュー オプションコードで開くを使用することもできます。 また、現在のディレクトリがこの場所に設定されている場合、コマンド プロンプトでcode .
を使って任意のフォルダを Visual Studio Code に追加することもできます。新たな Visual Studio Code PowerShell ターミナル (ターミナル > 新規ターミナル) 内で、pac pcf init コマンドを使用して、新しいコードコンポーネント プロジェクトを作成します。
pac pcf init ` --namespace SampleNamespace ` --name ChoicesPicker ` --template field ` --run-npm-install
または、次の短いフォームを使用します:
pac pcf init -ns SampleNamespace -n ChoicesPicker -t field -npm
これにより、必要なモジュールを定義した package.json
を含む、新しい ChoicesPicker.pcfproj
と関連ファイルが現在のフォルダに追加されます。 上記のコマンドでは、必要なモジュールをインストールするための npm install
コマンドも実行されます。
Running 'npm install' for you...
注意
The term 'npm' is not recognized as the name of a cmdlet, function, script file, or operable program.
というエラー メッセージが表示された場合、node.js (LTS 版を推奨) とその他すべての前提条件がインストールされているかどうかを確認してください。
テンプレートには、さまざまな設定ファイルとともに、index.ts
ファイルが含まれています。 これは、コード コンポーネントの開始点であり、コンポーネントの実装で説明されているライフサイクル メソッドが含まれています。
Microsoft Fluent UI をインストールする
UI の作成には Microsoft Fluent UI と React を使用するため、これらを依存関係のあるものとしてインストールする必要があります。 依存関係をインストールするには、次を使用します:
npm install react react-dom @fluentui/react
これにより、モジュールが packages.json
に追加され、node_modules
フォルダにインストールされます。 必要なモジュールは後で npm install
を使って復元するため、node_modules
をソース コントロールにコミットする必要はありません。
Microsoft Fluent UI の利点の一つは、一貫性のある アクセシビリティ の高い UI を提供することです。
eslint
を構成する
pac pcf init が使用するテンプレートは eslint
モジュールをプロジェクトにインストールし、.eslintrc.json
ファイルを追加することで構成します。 Eslint
は、TypeScript および React のコーディング スタイルを構成する必要があります。 詳細については、リンティング - コード・コンポーネントのベスト・プラクティスとガイダンスを参照してください。
マニフェストを編集する
ChoicesPicker\ControlManifest.Input.xml
ファイルは、コード コンポーネントの動作を説明するメタデータを定義します。 コントロール属性には、コンポーネントの名前空間と名前がすでに含まれています。
次のバインドプロパティと入力プロパティを定義する必要があります:
件名 | 使い方 | タイプ | Description |
---|---|---|---|
価値 | bound |
OptionSet | このプロパティは、選択肢列にリンクされます。 コード コンポーネントは現在の値を受け取り、この値が変更されたときに親コンテキストに通知します。 |
アイコンのマッピング | input |
複数行テキスト | このプロパティは、アプリ メーカーがコード コンポーネントをフォームに追加するときに設定されます。 各選択値に使用可能なアイコンを構成する際に使用できる JSON 文字列が含まれています。 |
詳細については次を参照してください: プロパティ要素。
ヒント
属性が別々の行に表示されるように XMLをフォーマットすると、読みやすくなる場合があります。 Visual Studio Code Marketplace で選択した XML フォーマット ツールを見つけてインストールします: xml フォーマット拡張機能を検索。
以下の例は、読みやすくするために、属性を別々の行にしたフォーマットにしています。
既存の sampleProperty を新しいプロパティに置き換える
ChoicesPicker\ControlManifest.Input.xml
を開き、コントロール要素内に以下のプロパティを貼り付けて、既存の sampleProperty
を置き替えます:
<property name="sampleProperty"
display-name-key="Property_Display_Key"
description-key="Property_Desc_Key"
of-type="SingleLine.Text"
usage="bound"
required="true" />
変更内容を保存し、次のコマンドでコンポーネントをビルドします:
npm run build
コンポーネントがビルドされると、以下が表示されます:
自動生成されたファイル
ChoicesPicker\generated\ManifestTypes.d.ts
がプロジェクトに追加されます。 これは、ビルド プロセスの一部としてControlManifest.Input.xml
から生成され、入力/出力プロパティの操作に使用する型を提供します。ビルドの出力が
out
フォルダに追加されます。bundle.js
は、ブラウザ内で実行されるトランスパイルされた JavaScript です。ControlManifest.xml
は、デプロイ時に使用されるControlManifest.Input.xml
ファイルを再フォーマットしたものです。注意
generated
、out
フォルダの内容を直接変更しないでください。 これらはビルドの過程で上書きされます。
ChoicesPicker Fluent UI React コンポーネントを実装する
コード コンポーネントが React を使用している場合、updateView
メソッド内でレンダリングされるルート コンポーネントは 1 つである必要があります。 ChoicesPicker
フォルダの中に、 ChoicesPickerComponent.tsx
という名前の新しい TypeScript ファイルを追加し、以下の内容を追加します:
import { ChoiceGroup, IChoiceGroupOption } from '@fluentui/react/lib/ChoiceGroup';
import * as React from 'react';
export interface ChoicesPickerComponentProps {
label: string;
value: number | null;
options: ComponentFramework.PropertyHelper.OptionMetadata[];
configuration: string | null;
onChange: (newValue: number | undefined) => void;
}
export const ChoicesPickerComponent = React.memo((props: ChoicesPickerComponentProps) => {
const { label, value, options, configuration, onChange } = props;
const valueKey = value != null ? value.toString() : undefined;
const items = React.useMemo(() => {
let iconMapping: Record<number, string> = {};
let configError: string | undefined;
if (configuration) {
try {
iconMapping = JSON.parse(configuration) as Record<number, string>;
} catch {
configError = `Invalid configuration: '${configuration}'`;
}
}
return {
error: configError,
choices: options.map((item) => {
return {
key: item.Value.toString(),
value: item.Value,
text: item.Label,
iconProps: { iconName: iconMapping[item.Value] },
} as IChoiceGroupOption;
}),
};
}, [options, configuration]);
const onChangeChoiceGroup = React.useCallback(
(ev?: unknown, option?: IChoiceGroupOption): void => {
onChange(option ? (option.value as number) : undefined);
},
[onChange],
);
return (
<>
{items.error}
<ChoiceGroup
label={label}
options={items.choices}
selectedKey={valueKey}
onChange={onChangeChoiceGroup}
/>
</>
);
});
ChoicesPickerComponent.displayName = 'ChoicesPickerComponent';
注意
このファイルの拡張子 tsx
は、React で使われる XML スタイルの構文をサポートする TypeScript ファイルです。 ビルド プロセスによって標準の JavaScript にコンパイルされます。
ChoicesPickerComponent の設計メモ
このセクションには、ChoicesPickerComponent
の設計 に関するコメントが含まれます。
機能コンポーネントです
これは React の機能コンポーネントですが、同様にクラス コンポーネントである可能性もあります。 これは、好みのコーディング スタイルに基づくものです。 クラス コンポーネントと機能コンポーネントを同じプロジェクトに混在させることもできます。 関数コンポーネントとクラス コンポーネントは、Reactで使用されている tsx
XML スタイルの構文を使用します。 詳細: 機能コンポーネントとクラス コンポーネント
bundle.js のサイズを最小化する
以下の代わりに、パスベースのインポートを使用して ChoiceGroup
Fluent UI コンポーネントをインポートする場合:
import { ChoiceGroup, IChoiceGroupOption } from '@fluentui/react';
次を使用します:
import { ChoiceGroup, IChoiceGroupOption } from '@fluentui/react/lib/ChoiceGroup';
これにより、バンドルサイズが小さくなり、必要な容量が少なくなり、ランタイムパフォーマンスが向上します。
別の方法としては、ツリー シェイクがあります。
'props' の説明
入力プロップは、updateView
メソッドの index.ts
で提供される以下の属性を持っています。:
prop |
Description |
---|---|
label |
コンポーネントにラベルを付けるために使用されます。 コンポーネントのラベル付けに使用され、モデル駆動型アプリ内で選択された UI 言語を使用して、親コンテキストで提供されるメタデータ フィールドのラベルにバインドされます。 |
value |
マニフェストで定義された入力プロパティにリンクされます。 これは、レコードが新しい場合や、フィールドが設定されていない場合には、null 値となります。 プロパティ値の受け渡しには、undefined ではなく、TypeScript null が使用されます。 |
options |
コード コンポーネントがモデル駆動型アプリの選択肢列にバインドされている場合、プロパティには、利用可能な選択肢を記述した OptionMetadata が含まれます。 これをコンポーネントに渡して、各アイテムをレンダリングできるようにします。 |
configuration |
ンポーネントの目的は、利用可能な各選択肢のアイコンを表示することです。 この構成は、アプリの作成者がコード コンポーネントをフォームに追加する際に提供されます。 このプロパティには、各数字の選択肢の値を Fluent UI のアイコン名にマッピングする JSON の文字列を指定します。 たとえば、{"0":"ContactInfo","1":"Send","2":"Phone"} などとします。 |
onChange |
ユーザーが選択肢の選択を変更すると、React コンポーネントが onChange イベントをトリガーします。 その後、コード コンポーネントが notifyOutputChanged を呼び出し、モデル駆動型プリが新しい値で列を更新できるようにします。 |
制御される React コンポーネント
React コンポーネントには 2 種類あります:
タイプ | Description |
---|---|
制御されない | これらのコンポーネントは、内部の状態を維持し、入力されたプロップを既定の値としてのみ使用します。 |
制御される | コンポーネント プロップによって渡された値をレンダリングします。 onChange イベントでプロップの値が更新されない場合、ユーザーに UI の変更が表示されません。 |
ChoicesPickerComponent
はコントロールされたコンポーネントであるため、モデル駆動型アプリが値を更新すると (notifyOutputChanged
の呼び出し後)、新しい値で updateView
を呼び出し、その値がコンポーネントのプロップに渡されて、更新された値を表示する再レンダリングが行われます。
destructuring の割り当て
props
定数の割り当て: const { label, value, options, onChange, configuration } = props;
destructuring 割り当てを使用します。 このように、レンダリングに必要な属性をプロップから抽出し、使用するたびに props
を前置するのではなく、その属性を使用します。
React コンポーネントとフックを使用する
以下は、ChoicesPickerComponent.tsx
で React コンポーネントとフックを使用する方法を説明しています。
Item | 説明 |
---|---|
React.memo | 入力プロップが変更されない限りレンダリングされないように、機能コンポーネントをラップします。 |
React.useMemo | 入力プロップ options または configuration が変更されたときにのみ、作成されたアイテム配列が変更されるようにします。 これは機能コンポーネントのベスト プラクティスであり、子コンポーネントの不要なレンダリングを減らすことができます。 |
React.useCallback | Fluent UI の ChoiceGroup の値が変化したときに呼び出されるコールバック クロージャを作成します。 この React フックにより、入力プロップ onChange が変更されたときにのみコールバック クロージャーが変更されるようになります。 これは useMemo と共通するパフォーマンスのベスト プラクティスです。 |
構成入力プロパティのエラー動作
JSON 構成入力プロパティの解析に失敗した場合は、items.error
を使用してエラーが表示されます。
ChoicesPicker コンポーネントをレンダリングするように index.ts を更新する
ChoicesPickerComponent をレンダリングするには、生成された index.ts file
を更新する必要があります。
コード コンポーネントの内部で React を使用する場合、ルート コンポーネントのレンダリングは updateView
メソッド内で行われます。 コンポーネントのレンダリングに必要なすべての値は、コンポーネントに渡され、それらが変更されると、再レンダリングされます。
import ステートメントを追加してアイコンを初期化する
index.ts
で ChoicesPickerComponent
を使用する前に、ファイルの先頭に以下を追加する必要があります:
注意
Fluent UI のアイコンセットを使用しているため、initializeIcons
のインポートが必要となります。 テストハーネス内でアイコンを読み込むには、initializeIcons
を呼び出す必要があります。 これらはモデル駆動型アプリの内部ですでに初期化されています。
ChoicesPicker クラスに属性を追加する
コード コンポーネントは、属性を使用してインスタンスの状態を維持します。 (これは React コンポーネントの状態とは異なります)。 index.ts
ChoicesPicker
クラス内で、次の属性を追加します:
次の表でこれらの属性を説明します。
Attribute | Description |
---|---|
notifyOutputChanged |
ユーザーが選択肢の値を変更し、コード コンポーネントがその値を親コンテキストに戻す準備ができたことをモデル駆動型アプリに通知する目的で使用するメソッドへの参照を保持します。 |
rootContainer |
モデル駆動型アプリ内のコード コンポーネントを保持する目的で作成する HTML DOM 要素です。 |
selectedValue |
ユーザーが選択した選択肢の状態を保持し、getOutputs メソッド内で返せるようにします。 |
context |
Power Apps component framework のコンテキストで、マニフェストで定義されたプロパティやその他のランタイム プロパティを読み取り、trackContainerResize などの API メソッドにアクセスする目的で使用されます。 |
init
メソッドを更新する
これらの属性を設定するには、init
メソッドを更新します。
public init(
context: ComponentFramework.Context<IInputs>,
notifyOutputChanged: () => void,
state: ComponentFramework.Dictionary,
container: HTMLDivElement):
void {
// Add control initialization code
}
init
メソッドは、アプリ画面でコード コンポーネントが初期化される際に呼び出されます。
onChange
メソッドを追加する
ユーザーが選択した値を変更した場合は、onChange
イベントから notifyOutputChanged
を呼び出す必要があります。
関数の追加:
onChange = (newValue: number | undefined): void => {
this.selectedValue = newValue;
this.notifyOutputChanged();
};
getOutputs
メソッドを更新する
ヒント
モデル駆動型アプリでクライアント API スクリプトを記述した経験があれば、フォームのコンテキストを使って属性値を更新することに慣れているかもしれません。 コード コンポーネントを、このコンテキストにアクセスさせないようにしてください。 代わりに、notifyOutputChanged
と getOutputs
に依存して、1 つ以上複数の変更された値を提供します。 IOutput
インターフェースで定義されたすべてのバインドされたプロパティを返す必要はなく、値が変更されたプロパティだけを返します。
updateView
メソッドを更新する
updateView
を更新して ChoicesPickerComponent
をレンダリングします:
public updateView(context: ComponentFramework.Context<IInputs>): void {
// Add code to update control view
}
ラベルとオプションを context.parameters.value
から引き出していることに注目してください。value.raw
は選択された数字の選択肢を提供し、値が選択されていない場合は null
を提供します。
destroy 関数を編集する
最後に、コードコンポーネントが破棄されたときに整理する必要があります:
詳細: ReactDOM.unmountComponentAtNode
テスト ハーネスを開始します
すべてのファイルが保存され、端末で使用されていることを確認します:
npm start watch
テスト ハーネスが、選択肢の選択が新しいブラウザ ウィンドウ内にレンダリングされた状態で開始されることがわかります。 最初は、文字列プロパティ configuration
に既定の値 val
があるため、エラーが表示されます。 テスト ハーネスの既定の選択肢 0、1、2 と、以下の Fluent UI のアイコンを対応させるように設定します:
{"0":"ContactInfo","1":"Send","2":"Phone"}
選択したオプションを変更すると、右側のパネルのデータ入力 の値が表示されます。 また、値を変更すると、コンポーネントには関連する値が更新されて表示されます。
読み取り専用、列レベル セキュリティに対応
モデル駆動型のアプリケーション field
コンポーネントを作成する際、アプリケーションでは、列レベル セキュリティにより読み取り専用、またはマスキングされたコントロールの状態を尊重する必要があります。 列が読み取り専用の場合にコード コンポーネントが読み取り専用の UI をレンダリングしない場合、状況によっては (例えば、レコードが非アクティブになっている場合など)、更新されるべきではない列をユーザーが更新してしまう場合があります。 詳細: アクセスを制御する列レベル セキュリティ。
読み取り専用および列レベル セキュリティ用に updateView メソッドを編集する
index.ts
内で、updateView
メソッドを編集して次のコードを追加し、disabled
フラグと masked
フラグを取得します。
public updateView(context: ComponentFramework.Context<IInputs>): void {
const { value, configuration } = context.parameters;
if (value && value.attributes && configuration) {
ReactDOM.render(
React.createElement(ChoicesPickerComponent, {
label: value.attributes.DisplayName,
options: value.attributes.Options,
configuration: configuration.raw,
value: value.raw,
onChange: this.onChange,
}),
this.rootContainer,
);
}
}
バインドされた列に列レベル セキュリティ設定が適用されている場合、モデル駆動型アプリの内部でのみ value.security
が入力されます。
これらの値は、プロップスを介して React コンポーネントに渡すことができます。
ChoicesPickerComponent を編集して、無効なプロパティとマスクされたプロパティを追加する
ChoicesPickerComponent.tsx
内で、disabled
インターフェースに追加することで、masked
およびChoicesPickerComponentProps
プロパティを受け入れることができます:
export interface ChoicesPickerComponentProps {
label: string;
value: number | null;
options: ComponentFramework.PropertyHelper.OptionMetadata[];
configuration: string | null;
onChange: (newValue: number | undefined) => void;
}
ChoicesPickerComponent プロパティを編集する
新しい属性を props に追加します。
export const ChoicesPickerComponent = React.memo((props: ChoicesPickerComponentProps) => {
const { label, value, options, configuration, onChange } = props;
ChoicesPickerComponent return ノード
ChoicesPickerComponent
内で React ノードを返すときに、これらの新しい入力プロップを使用して、ピッカーが無効またはマスキングされていることを確認できます
return (
<>
{items.error}
<ChoiceGroup
label={label}
options={items.choices}
selectedKey={valueKey}
onChange={onChangeChoiceGroup}
/>
</>
);
注意
読み取り専用フィールドや列レベル セキュリティをシミュレートできないため、テスト ハーネスに違いは見られません。 モデル駆動型アプリケーション内にコントロールを配置した後、これをテストする必要があります。
コード コンポーネントをレスポンシブにする
コード コンポーネントは、Web、タブレット、モバイル アプリでレンダリングできます。 使用可能なスペースを考慮することが重要です。 使用可能な幅が制限されている場合に、選択肢コンポーネントをドロップダウンで表示します。
ドロップダウン コンポーネントのインポートとアイコン
ChoicesPickerComponent.tsx
のコンポーネントは、Fluent UI の Dropdown
コンポーネントを使って小さいバージョンをレンダリングするため、それをインポートに追加します:
import { ChoiceGroup, IChoiceGroupOption } from '@fluentui/react/lib/ChoiceGroup';
import * as React from 'react';
formFactor プロップを追加する
新しいプロップ formFactor
に応じて異なるレンダリングを行うようにコード コンポーネントを更新します。 ChoicesPickerComponentProps
インターフェースに以下の属性を追加します:
export interface ChoicesPickerComponentProps {
label: string;
value: number | null;
options: ComponentFramework.PropertyHelper.OptionMetadata[];
configuration: string | null;
onChange: (newValue: number | undefined) => void;
disabled: boolean;
masked: boolean;
}
formFactor を ChoicesPickerComponent プロップに追加する
formFactor
をプロップに追加します。
export const ChoicesPickerComponent = React.memo((props: ChoicesPickerComponentProps) => {
const { label, value, options, configuration, onChange, disabled, masked } = props;
メソッドを追加し、ドロップダウン コンポーネントをサポートするように変更します
ドロップダウン コンポーネントには、異なるレンダリング方法がいくつか必要です。
ChoicesPickerComponent
の上に、以下を追加します:const iconStyles = { marginRight: '8px' }; const onRenderOption = (option?: IDropdownOption): JSX.Element => { if (option) { return ( <div> {option.data && option.data.icon && ( <Icon style={iconStyles} iconName={option.data.icon} aria-hidden="true" title={option.data.icon} /> )} <span>{option.text}</span> </div> ); } return <></>; }; const onRenderTitle = (options?: IDropdownOption[]): JSX.Element => { if (options) { return onRenderOption(options[0]); } return <></>; };
これらのメソッドは、ドロップダウンの値の横に正しいアイコンを表示する目的で
Dropdown
が使用します。新しい
onChangeDropDown
メソッドを追加します。ChoiceGroup
イベント ハンドラーと同様にDropdown
にonChange
メソッドが必要です。 既存のonChangeChoiceGroup
のすぐ下に、新しいDropdown
のバージョンを追加します:const onChangeDropDown = React.useCallback( (ev: unknown, option?: IDropdownOption): void => { onChange(option ? (option.data.value as number) : undefined); }, [onChange], );
レンダリングされた出力を変更する
新しい formFactor
プロパティを使用するように、以下の変更を行います。
return (
<>
{items.error}
{masked && '****'}
{!items.error && !masked && (
<ChoiceGroup
label={label}
options={items.choices}
selectedKey={valueKey}
disabled={disabled}
onChange={onChangeChoiceGroup}
/>
)}
</>
);
formFactor
が大きい場合には ChoiceGroup
コンポーネントを出力し、小さい場合には Dropdown
を使用していることがわかります。
DropdownOptions を返す
最後に ChoicesPickerComponent.tsx
で、オプションのメタデータを ChoicesGroup
で使用したものとは若干異なる方法でマッピングする必要があります。そのため、既存の choices
: options.map
の下にある items
リターン ブロック内で、以下を追加します:
return {
error: configError,
choices: options.map((item) => {
return {
key: item.Value.toString(),
value: item.Value,
text: item.Label,
iconProps: { iconName: iconMapping[item.Value] },
} as IChoiceGroupOption;
}),
};
index.ts を編集する
formFactor
のプロップに応じて選択肢コンポーネントのレンダリング方法が変更されるため、index.ts
内部のレンダー呼び出しから正しい値を渡す必要があります。
SmallFormFactorMaxWidth および FormFactors 列挙型を追加する
index.ts
内の export class ChoicesPicker
クラスのすぐ上に以下を追加します。
const SmallFormFactorMaxWidth = 350;
const enum FormFactors {
Unknown = 0,
Desktop = 1,
Tablet = 2,
Phone = 3,
}
SmallFormFactorMaxWidth
は、コンポーネントが ChoiceGroup
ではなく Dropdown
を使用してレンダリングを開始するときの幅を表わします。 FormFactors
enum
は context.client.getFormFactor
を呼び出す際に便宜的に使用されます。
formFactor を検出するコードを追加する
既存のプロップの配下にある React.createElement
のプロップに以下を追加します:
React.createElement(ChoicesPickerComponent, {
label: value.attributes.DisplayName,
options: value.attributes.Options,
configuration: configuration.raw,
value: value.raw,
onChange: this.onChange,
disabled: disabled,
masked: masked,
}),
サイズ変更の更新をリクエストする
context.mode.allocatedWidth
を使用しているため、利用可能な幅が変更されたときに (updateView
への呼び出しを介して) 更新情報を受信することを、モデル駆動型のアプリに通知する必要があります。 init
メソッド内で context.mode.trackContainerResize
の呼び出しを追加して行います:
public init(
context: ComponentFramework.Context<IInputs>,
notifyOutputChanged: () => void,
state: ComponentFramework.Dictionary,
container: HTMLDivElement):
void {
this.notifyOutputChanged = notifyOutputChanged;
this.rootContainer = container;
this.context = context;
}
テスト ハーネスで試す
すべての変更を保存すると、テスト ハーネスのブラウザ ウィンドウに自動的に反映されます (npm start watch
がまだ残留しているためです)。 ここで、コンポーネントコンテナの幅 の値を 349 と 350 の間で切り替えると、レンダリングの動作が変わります。 また、フォーム ファクターを ウェブ と 電話 で入れ替えても、同じ動作を確認できます。
ローカライズ
複数の言語に対応する場合は、コード コンポーネントに、デザイン文字列とランタイム文字列の両方の翻訳を提供するリソースファイルを保持できます。
その場所
ChoicesPicker\strings\ChoicesPicker.1033.resx
に新しいファイルを追加します。 異なるロケールのラベルを追加する場合は、1033 (en-us
) を選択したロケールに変更してください。Visual Studio Code リソース エディタを使用して、次のように入力します:
件名 価値 ChoicesPicker_Name
選択肢ピッカー (モデル駆動型) ChoicesPicker_Desc
アイコン付きのピッカーとして選択肢を表示します Value_Name
価値 Value_Desc
コントロールをバインドする選択フィールド Configuration_Name
アイコンのマッピング構成 Configuration_Desc
選択値を fluent ui アイコンにマップする構成。 E.g. {"1":"ContactInfo","2":"Send"} それ以外の場合は、次の XML を使用して .resx ファイルの内容を設定します:
<?xml version="1.0" encoding="utf-8"?> <root> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xsd:import namespace="http://www.w3.org/XML/1998/namespace"/> <xsd:element name="root" msdata:IsDataSet="true"> <xsd:complexType> <xsd:choice maxOccurs="unbounded"> <xsd:element name="metadata"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0"/> </xsd:sequence> <xsd:attribute name="name" use="required" type="xsd:string"/> <xsd:attribute name="type" type="xsd:string"/> <xsd:attribute name="mimetype" type="xsd:string"/> <xsd:attribute ref="xml:space"/> </xsd:complexType> </xsd:element> <xsd:element name="assembly"> <xsd:complexType> <xsd:attribute name="alias" type="xsd:string"/> <xsd:attribute name="name" type="xsd:string"/> </xsd:complexType> </xsd:element> <xsd:element name="data"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/> <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/> <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/> <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/> <xsd:attribute ref="xml:space"/> </xsd:complexType> </xsd:element> <xsd:element name="resheader"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required"/> </xsd:complexType> </xsd:element> </xsd:choice> </xsd:complexType> </xsd:element> </xsd:schema> <resheader name="resmimetype"> <value>text/microsoft-resx</value> </resheader> <resheader name="version"> <value>2.0</value> </resheader> <resheader name="reader"> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <data name="ChoicesPicker_Name" xml:space="preserve"> <value>Choices Picker (Model Driven)</value> <comment/> </data> <data name="ChoicesPicker_Desc" xml:space="preserve"> <value>Shows choices as a picker with icons</value> <comment/> </data> <data name="Value_Name" xml:space="preserve"> <value>Value</value> <comment/> </data> <data name="Value_Desc" xml:space="preserve"> <value>The choices field to bind the control to</value> <comment/> </data> <data name="Configuration_Name" xml:space="preserve"> <value>Icon Mapping Configuration</value> <comment/> </data> <data name="Configuration_Desc" xml:space="preserve"> <value>Configuration that maps the choice value to a fluent ui icon. E.g. {"1":"ContactInfo","2":"Send"}</value> <comment/> </data> </root>
ヒント
resx
ファイルを直接編集することはお勧めしません。 Visual Studio Code リソース エディタ、または Visual Studio Code の拡張機能により、これを簡単に行うことができます。
リソース文字列のマニフェストを更新する
以上で、リソース文字列ができたので、以下のように ControlManifest.Input.xml
を更新することで、リソース文字列を参照することができます:
<?xml version="1.0" encoding="utf-8" ?>
<manifest>
<control namespace="SampleNamespace"
constructor="ChoicesPicker"
version="0.0.1"
display-name-key="ChoicesPicker"
description-key="ChoicesPicker description"
control-type="standard">
<external-service-usage enabled="false">
</external-service-usage>
<property name="value"
display-name-key="Value"
description-key="Value of the Choices Control"
of-type="OptionSet"
usage="bound"
required="true"/>
<property name="configuration"
display-name-key="Icon Mapping"
description-key="Configuration that maps the choice value to a fluent ui icon."
of-type="Multiple"
usage="input"
required="true"/>
<resources>
<code path="index.ts"
order="1"/>
</resources>
</control>
</manifest>
以下が表示されます:
display-name-key
とdescription-key
の値は、resx
ファイルの対応するキーを指すようになっています。resources
要素には、コード コンポーネントが参照されたファイルからリソースを読み込むことを示すエントリが追加されています。
コンポーネントで使用する追加の文字列が必要な場合は、resx
に追加し、getString を使用して実行時に文字列を読み込むことができます。 詳しくは、ローカリゼーション API コンポーネントの実装を参照してください。
注意
テスト ハーネスの制限として、リソースファイルを読み込まないことが挙げられます。そのため、コンポーネントを完全にテストするには、コンポーネントを Microsoft Dataverse にデプロイする必要があります。
モデル駆動型アプリにおけるデプロイと構成
テスト ハーネスで基本的な機能をテストした後は、コンポーネントを Microsoft Dataverse にデプロイして、モデル駆動のアプリ内でコード コンポーネントをエンド ツー エンドで完全にテストできるようにする必要があります。
Dataverse 環境で、接頭辞が
samples
の公開元が作成されていることを確認してください:同様に、ご利用の公開元である可能性もあります。ただし、以下の pac pcf push へのコールで公開元の接頭辞パラメータを更新してください。 詳細については、ソリューションの公開元を作成するを参照してください。
公開元の保存後は、ご利用の環境に対して Microsoft Power Platform CLI を認証する準備が整ったことを意味します。コンパイルされたコード コンポーネントを プッシュできます。 コマンドラインでは、以下を使用します:
pac auth create --url https://myorg.crm.dynamics.com
myorg.crm.dynamics.com
は Dataverse 環境の URL に置き換えます。 プロンプトが表示されたら、システム管理者またはカスタマイザー権限でログインします。 これらのロールによって提供される特権は、任意のコードコンポーネントを Dataverse にデプロイするために必要です。コード コンポーネントをデプロイするには、次を使用します:
pac pcf push --publisher-prefix samples
注意
エラー (
Missing required tool: MSBuild.exe/dotnet.exe
) が発生した場合、パス環境変数にMSBuild.exe/dotnet.exe
を追加すか、Developer Command Prompt for Visual Studio Code
を使用します。 Visual Studio2019 for Windows&Macまたビルドツール Visual Studio 2019のどちらかをインストールする必要があります。 前提条件に記載されている通り、必ず.NET build tools
のワークロードを選択してください。このプロセスが完了すると、ご利用の環境にPowerAppTools_samplesという名前の一時的なソリューションが作成されます。
ChoicesPicker
コード コンポーネントがこのソリューションに追加されます。 必要に応じて、コード コンポーネントを後からソリューションに移行できます。 詳しくは、コード コンポーネント アプリケーションの ライフサイクル管理 (ALM) を参照してください。次に、クラシック エディターのメイン フォームに移動し、 優先する連絡方法 > プロパティの変更 > コントロール タブ > コントロールの追加 > 選択肢ピッカーを選択 > 追加 を選択して、連絡先フォームにコード コンポーネントを追加します。
注意
将来的には、モデル駆動型アプリ フォームのコード コンポーネントの構成にはクラシック エディターは必要なくなります。
コンポーネントに以下のプロパティを設定します:
選択肢ピッカーを Web、電話、タブレットの既定として設定します。
編集アイコンを選択して 静的な値にバインドする を選択すると、アイコンのマッピング構成 に以下の文字列が入力されます。
{ "1":"ContactInfo", "2":"Send", "3":"Phone", "4":"Fax", "5":"DeliveryTruck" }
これらは、各選択肢の値に使用される Fluent UI のアイコンです。
表示タブを選択し、フォームにラベルを表示するのチェックを外します。選択肢ピッカーの上にラベルが表示されるため、これは必要ありません。
フォームを保存して公開します。
正しいフォームが選択された状態で、モデル駆動型アプリ内の連絡先レコードを開きます。 標準のドロップダウンコントロールの代わりに、
ChoicesPicker
コード コンポーネントが表示されます。 (コンポーネントを表示するには、ページのハード リロードの実行が必要となる場合があります)。注意
テスト ハーネスでは、モデル駆動型アプリと比較して、テキストの配置が若干異なります。 これは、テスト ハーネスの CSS のルールがモデル駆動型アプリのものとは異なるためです。 そのため、デプロイ後は必ずコード コンポーネントの完全なテストをお勧めします。
Dataverse にデプロイ後のデバッグ
コンポーネントにさらに変更を加える必要がある場合は、毎回デプロイする必要はありません。 その代わり、デバッグ コード コンポーネント で説明した手法でFiddler AutoResponder を作成し、npm start watch
の実行中にローカル ファイル システムからファイルを読み込むようにします。
注意
テストハーネスを使ってすべての機能がテストできるのであれば、Dataverse にデプロイした後のデバッグは必要ない可能性もあります。 ただし、コード コンポーネントを配布する前に、必ず Dataverse の中でデプロイとテストを行うことをお勧めします。
AutoResponderは、次のようになります:
REGEX:(.*?)((?'folder'css|html)(%252f|\/))?SampleNamespace\.ChoicesPicker[\.\/](?'fname'[^?]*\.*)(.*?)$
C:\repos\ChoicesPicker\out\controls\ChoicesPicker\${folder}\${fname}
AutoResponder ファイルを取り込むには、ブラウザのキャッシュを空にして最新の情報に更新する必要があります。 読み込んだ後は、フィドラーがファイルにキャッシュ コントロール ヘッダを追加してキャッシュされないようにするため、ブラウザを更新できます。
変更の完了後は、マニフェストのパッチ バージョンを増分し、pac pcf push を使用して再デプロイできます。
これまでは、最適化されていない開発用のビルドをデプロイしていたため、実行時の動作が遅くなっていました。 ChoicesPicker.pcfproj
を編集することで、pac pcf push を使って最適化されたビルドをデプロイできます。 OutputPath
の配下に、以下を追加します:
<PcfBuildMode>production</PcfBuildMode>
関連記事
Microsoft Power Platform によるアプリケーション ライフサイクル管理 (ALM)
Power Apps Component Framework API の参照
最初のコンポーネントを作成する
コード コンポーネントのデバッグ
注意
ドキュメントの言語設定についてお聞かせください。 簡単な調査を行います。 (この調査は英語です)
この調査には約 7 分かかります。 個人データは収集されません (プライバシー ステートメント)。