次の方法で共有


チュートリアル: モデル駆動型アプリのフィールド コンポーネントを作成する

このチュートリアルでは、モデル駆動型アプリの field コンポーネントを作成し、デプロイ、構成、Visual Studio Code を使用してフォーム上でコンポーネントのテストを行います。 このコードコンポーネントは、フォームに一連の選択肢を表示し、各選択肢の値の横にアイコンを表示します。 このコンポーネントは、選択肢列定義 (メタデータ) や列レベル セキュリティなど、モデル駆動型アプリの高度な機能の一部を使用します。

これらに加えて、コード コンポーネントがベスト プラクティスのガイダンスに従っていることも確認する必要があります。

  1. Microsoft Fluent UI を使用して一貫性とアクセシビリティを管理します
  2. 設計時と実行時の両方でコード コンポーネント ラベルをローカライズします
  3. 再利用性を高めるために、コード コンポーネントがメタデータ駆動型であることを確認してください
  4. コード コンポーネントがフォーム ファクターと利用可能な幅に応じて表示されることを確認し、スペースが限られている場合にはアイコン付きのコンパクトなドロップダウンを表示します

ChoicesPicker コンポーネント。

コード

PowerApps-Samples/component-framework/ChoicesPickerControl/ から完全なサンプルをダウンロードできます。

新しい pcfproj プロジェクトを作成します

注意

開始する前に、すべてのコンポーネントの前提条件を満たしていることを確認してください。

新規 pcfproj の作成:

  1. コード コンポーネントを保持する新しいフォルダを作成します。 たとえば、C:\repos\ChoicesPicker などとします。

  2. Visual Studio Code を開き、ファイル > フォルダを開く に移動し、上記手順で作成済みの ChoicesPicker フォルダを選択します。 Visual Studio Code のインストール時にウィンドウズ エクスプローラーの拡張機能を追加した場合は、フォルダ内のコンテキスト メニュー オプションコードで開くを使用することもできます。 また、現在のディレクトリがこの場所に設定されている場合、コマンド プロンプトで code . を使って任意のフォルダを Visual Studio Code に追加することもできます。

  3. 新たな 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 版を推奨) とその他すべての前提条件がインストールされているかどうかを確認してください。

pac pcf init を使用してコード コンポーネントを作成する。

テンプレートには、さまざまな設定ファイルとともに、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 ファイルを再フォーマットしたものです。

    注意

    generatedout フォルダの内容を直接変更しないでください。 これらはビルドの過程で上書きされます。

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.tsChoicesPickerComponent を使用する前に、ファイルの先頭に以下を追加する必要があります:

import { IInputs, IOutputs } from "./generated/ManifestTypes";

注意

Fluent UI のアイコンセットを使用しているため、initializeIcons のインポートが必要となります。 テストハーネス内でアイコンを読み込むには、initializeIcons を呼び出す必要があります。 これらはモデル駆動型アプリの内部ですでに初期化されています。

ChoicesPicker クラスに属性を追加する

コード コンポーネントは、属性を使用してインスタンスの状態を維持します。 (これは React コンポーネントの状態とは異なります)。 index.ts ChoicesPicker クラス内で、次の属性を追加します:

export class ChoicesPicker implements ComponentFramework.StandardControl<IInputs, IOutputs> {

次の表でこれらの属性を説明します。

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 メソッドを更新する

public getOutputs(): IOutputs {
    return {};
}

ヒント

モデル駆動型アプリでクライアント API スクリプトを記述した経験があれば、フォームのコンテキストを使って属性値を更新することに慣れているかもしれません。 コード コンポーネントを、このコンテキストにアクセスさせないようにしてください。 代わりに、notifyOutputChangedgetOutputs に依存して、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 関数を編集する

最後に、コードコンポーネントが破棄されたときに整理する必要があります:

public destroy(): void {
    // Add code to cleanup control if necessary
}

詳細: 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;

メソッドを追加し、ドロップダウン コンポーネントをサポートするように変更します

ドロップダウン コンポーネントには、異なるレンダリング方法がいくつか必要です。

  1. 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 が使用します。

  2. 新しい onChangeDropDown メソッドを追加します。

    ChoiceGroup イベント ハンドラーと同様に DropdownonChange メソッドが必要です。 既存の 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 enumcontext.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 がまだ残留しているためです)。 ここで、コンポーネントコンテナの幅 の値を 349350 の間で切り替えると、レンダリングの動作が変わります。 また、フォーム ファクターウェブ電話 で入れ替えても、同じ動作を確認できます。

trackContainerResize.

ローカライズ

複数の言語に対応する場合は、コード コンポーネントに、デザイン文字列とランタイム文字列の両方の翻訳を提供するリソースファイルを保持できます。

  1. その場所 ChoicesPicker\strings\ChoicesPicker.1033.resx に新しいファイルを追加します。 異なるロケールのラベルを追加する場合は、1033 (en-us) を選択したロケールに変更してください。

  2. 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>

以下が表示されます:

  1. display-name-keydescription-key の値は、resx ファイルの対応するキーを指すようになっています。
  2. resources 要素には、コード コンポーネントが参照されたファイルからリソースを読み込むことを示すエントリが追加されています。

コンポーネントで使用する追加の文字列が必要な場合は、resx に追加し、getString を使用して実行時に文字列を読み込むことができます。 詳しくは、ローカリゼーション API コンポーネントの実装を参照してください。

注意

テスト ハーネスの制限として、リソースファイルを読み込まないことが挙げられます。そのため、コンポーネントを完全にテストするには、コンポーネントを Microsoft Dataverse にデプロイする必要があります。

モデル駆動型アプリにおけるデプロイと構成

テスト ハーネスで基本的な機能をテストした後は、コンポーネントを Microsoft Dataverse にデプロイして、モデル駆動のアプリ内でコード コンポーネントをエンド ツー エンドで完全にテストできるようにする必要があります。

  1. Dataverse 環境で、接頭辞が samples の公開元が作成されていることを確認してください:

    新しい公開元の追加。

    同様に、ご利用の公開元である可能性もあります。ただし、以下の pac pcf push へのコールで公開元の接頭辞パラメータを更新してください。 詳細については、ソリューションの公開元を作成するを参照してください。

  2. 公開元の保存後は、ご利用の環境に対して Microsoft Power Platform CLI を認証する準備が整ったことを意味します。コンパイルされたコード コンポーネントを プッシュできます。 コマンドラインでは、以下を使用します:

    pac auth create --url https://myorg.crm.dynamics.com
    

    myorg.crm.dynamics.com は Dataverse 環境の URL に置き換えます。 プロンプトが表示されたら、システム管理者またはカスタマイザー権限でログインします。 これらのロールによって提供される特権は、任意のコードコンポーネントを Dataverse にデプロイするために必要です。

  3. コード コンポーネントをデプロイするには、次を使用します:

    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 のワークロードを選択してください。

  4. このプロセスが完了すると、ご利用の環境にPowerAppTools_samplesという名前の一時的なソリューションが作成されます。 ChoicesPicker コード コンポーネントがこのソリューションに追加されます。 必要に応じて、コード コンポーネントを後からソリューションに移行できます。 詳しくは、コード コンポーネント アプリケーションの ライフサイクル管理 (ALM) を参照してください。

    PowerAppsTools_sample 一時的ソリューション。

  5. 次に、クラシック エディターメイン フォームに移動し、 優先する連絡方法 > プロパティの変更 > コントロール タブ > コントロールの追加 > 選択肢ピッカーを選択 > 追加 を選択して、連絡先フォームにコード コンポーネントを追加します。

    注意

    将来的には、モデル駆動型アプリ フォームのコード コンポーネントの構成にはクラシック エディターは必要なくなります。

  6. コンポーネントに以下のプロパティを設定します:

    • 選択肢ピッカーを Web、電話、タブレットの既定として設定します。

    • 編集アイコンを選択して 静的な値にバインドする を選択すると、アイコンのマッピング構成 に以下の文字列が入力されます。

      {
          "1":"ContactInfo",
          "2":"Send", 
          "3":"Phone",
          "4":"Fax",
          "5":"DeliveryTruck"
      }
      

      これらは、各選択肢の値に使用される Fluent UI のアイコンです。

      コントロール プロパティ

    • 表示タブを選択し、フォームにラベルを表示するのチェックを外します。選択肢ピッカーの上にラベルが表示されるため、これは必要ありません。

  7. フォームを保存して公開します。

  8. 正しいフォームが選択された状態で、モデル駆動型アプリ内の連絡先レコードを開きます。 標準のドロップダウンコントロールの代わりに、 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 分かかります。 個人データは収集されません (プライバシー ステートメント)。