Power BI カスタムビジュアル開発 : DataViewUtils で DataView の各種オブジェクトをパースする

# DataViewUtils
`DataViewUtils` は DataView オブジェクトのパースを容易にするユーティリティです。特に DataViewObjectsParser は多くのサンプルでも使われるモジュールで、Objects (書式) の扱いが格段にやり易くなるため、必須の知識となります。

# インストール

このツールは `pbiviz new` でプロジェクトを作成した場合は、既定で入っています。入っていない場合のみ以下手順で追加。

1\. npm コマンドでインストール。

```shell
npm install powerbi-visuals-utils-dataviewutils --save
```
2\. tsconfig.json の files に型情報を追加。

```json:tsconfig.json
{
  "compilerOptions": {
  ...
  },
  "files": [
    "node_modules/powerbi-visuals-utils-dataviewutils/lib/index.d.ts",
    ...
  ]
}
```

3\. pbiviz.json の externalJS に追加。

```json:pbiviz.json
{
  ...
  "externalJS": [
    "node_modules/powerbi-visuals-utils-dataviewutils/lib/index.js",
    ...
  ],
  ...
}
```

# 機能

- [DataViewObjectsParser](#dataviewobjectsparser) : フォーマットパネルのプロパティパース
- [DataRoleHelper](#datarolehelper) : DataView 内のデータロールの確認
- [DataViewObjects](#dataviewobjects) : DataView 内の Objects のパース
- [DataViewObject](#dataviewobject) : DataView 内の Object のパース
- [converterHelper](#converterhelper) : DataView 内のカテゴリやシリーズ、列の確認など

# DataViewObjectsParser

`DataViewObjectsParser` はビジュアルのプロパティ管理を容易にする機能を提供。多くのサンプルでも同様の手法が利用されているため、重要な機能。

`powerbi.extensibility.utils.dataview.DataViewObjectsParser` モジュールで以下の機能を提供

- [getDefault](#getdefault)
- [parse](#parse)
- [enumerateObjectInstances](#enumerateobjectinstances)


## getDefault

この静的関数は DataViewObjectsParser インスタンスの DataViewObjectsParser を返す。

```typescript
static getDefault(): DataViewObjectsParser;
```

## parse
`DataViewObjectsParser` のフォーマットパネルのプロパティの値をパース。 

```typescript
static parse<T extends DataViewObjectsParser>(dataView: DataView): T;
```

## enumerateObjectInstances

この静的関数は DataViewObjectsParser で使えるようにプロパティの一覧を返す。

```typescript
static enumerateObjectInstances(dataViewObjectParser: DataViewObjectsParser, options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumeration;
```

### DataViewObjectsParser の例

以下の様に capabilities の objects 定義があるとする。

```json:capabilities.json
{
    "dataRoles": [
        {
            "displayName": "サンプルメジャー",
            "name": "myMeasure",
            "kind": "Measure"
        }
    ],        
    "objects": {
        "myProperty": {
            "displayName": "サンプルプロパティ",
            "properties": {
                "show": {
                    "displayName": "表示する",
                    "type": {
                        "bool": true
                    }
                },
                "textColor": {
                    "displayName": "色",
                    "type": {
                        "fill": {
                            "solid": {
                                "color": true
                            }
                        }
                    }
                }           
            }
        },
        "anotherProperty": {
            "displayName": "サンプルプロパティ２",
            "properties": {
                "show": {
                    "displayName": "表示する",
                    "type": {
                        "bool": true
                    }
                },
                "textAlignment": {
                    "displayName": "テキストの配置",
                    "type": {
                        "formatting": {
                            "alignment": true
                        }
                    }
                }           
            }
        }
    }
}
```

この場合 visual.ts では objects を以下の様に扱える。

```typescript
module powerbi.extensibility.visual {
    "use strict";

    import DataViewObjectsParser = powerbi.extensibility.utils.dataview.DataViewObjectsParser;
    import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;

    // 自分のプロパティ用のクラス
    class MyProperty {
        // 各プロパティは json と同じ名前にする
        public textColor: string = "red"; // 既定の値
        public show: boolean = true; // 既定の値
    }

    class AnotherProperty {
        // 各プロパティは json と同じ名前にする
        public textAlignment: string = "center"; // 既定の値
        public show: boolean = true; // 既定の値
    }

    class PropertiesParser extends DataViewObjectsParser {
        // オブジェクトの指定
        public myProperty: MyProperty = new MyProperty();
        public anotherProperty: AnotherProperty = new AnotherProperty();
    }


    export class Visual implements IVisual {

        private area: d3.Selection<HTMLElement>
        private host: IVisualHost;
        private settings: VisualSettings;
        private propertiesParser: PropertiesParser;

        constructor(options: VisualConstructorOptions) {
            // カスタムビジュアルを配置しているホストの情報を取得
            this.host = options.host;
            // カスタムビジュアルのエリアを取得
            this.area = d3.select(options.element);
            this.area.append("h1").attr("class","color");
            this.area.append("h1").attr("class","alignment");
        }

        public update(options: VisualUpdateOptions) {
            debugger;

            // 各オブジェクトのプロパティ取得
            this.propertiesParser = PropertiesParser.parse<PropertiesParser>(options.dataViews[0]);
            let currentColor = this.propertiesParser.myProperty.textColor;
            let currentTextAlginment = this.propertiesParser.anotherProperty.textAlignment;

            this.area.select(".color").text(currentColor);
            this.area.select(".alignment").text(currentTextAlginment);
        }

        private static parseSettings(dataView: DataView): VisualSettings {
            return VisualSettings.parse(dataView) as VisualSettings;
        }

        public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
            return PropertiesParser.enumerateObjectInstances(this.propertiesParser, options);
        }

    }
}
```
# DataRoleHelper
`DataRoleHelper` は DataView 内のデータロールに関する確認機能を提供

`powerbi.extensibility.utils.dataview.DataRoleHelper` モジュールで以下の機能を提供

- [getMeasureIndexOfRole](#getmeasureindexofrole)
- [getCategoryIndexOfRole](#getcategoryindexofrole)
- [hasRole](#hasrole)
- [hasRoleInDataView](#hasroleindataview)
- [hasRoleInValueColumn](#hasroleinvaluecolumn)

## getMeasureIndexOfRole

名前でメジャーデータロールを取得し、インデックスを返す。

```typescript
function getMeasureIndexOfRole(grouped: DataViewValueColumnGroup[], roleName: string): number;
```

### getMeasureIndexOfRole 例

以下の例は myMeasure メジャーデータロールのインデックスを取得。取得出来た場合はインデックス番号、できない場合は -1 が返る。ポイントは categorical.values に対して grouped 関数を呼ぶと DataViewValueColumnGroup[] が返る点。

```typescript
import DataRoleHelper = powerbi.extensibility.utils.dataview.DataRoleHelper;

public update(options: VisualUpdateOptions) {
    try
        let index = DataRoleHelper.getMeasureIndexOfRole(options.dataViews[0].categorical.values.grouped(), "myMeasure");
    }
    catch (err){
        
    }
...
```

## getCategoryIndexOfRole

名前でカテゴリデータロールを取得し、インデックスを返す。

```typescript
function getCategoryIndexOfRole(categories: DataViewCategoryColumn[], roleName: string): number;
```

### getCategoryIndexOfRole 例

以下の例は myCategory カテゴリデータロールのインデックスを取得。取得出来た場合はインデックス番号、できない場合は -1 が返る。

```typescript
import DataRoleHelper = powerbi.extensibility.utils.dataview.DataRoleHelper;

public update(options: VisualUpdateOptions) {
    try
        let index = DataRoleHelper.getCategoryIndexOfRole(options.dataViews[0].categorical.categories, "myCategory");
    }
    catch (err){
    }
...
```

## hasRole

metadata に指定したロールが存在するか確認する。

```typescript
function hasRole(column: DataViewMetadataColumn, name: string): boolean;
```

### hasRole 例

以下の例では myCategory が metadata に存在するか確認。

```typescript
import DataRoleHelper = powerbi.extensibility.utils.dataview.DataRoleHelper;

public update(options: VisualUpdateOptions) {
    let myCategoryExists = options.dataViews[0].metadata.columns.some((column) => {
        return DataRoleHelper.hasRole(column, "myCategory");
    });
...
```

## hasRoleInDataView

dataView に指定したロールが存在するか確認する。

```typescript
function hasRoleInDataView(dataView: DataView, name: string): boolean;
```

### hasRoleInDataView 例

以下の例では myCategory が dataView に存在するか確認。

```typescript
import DataRoleHelper = powerbi.extensibility.utils.dataview.DataRoleHelper;

public update(options: VisualUpdateOptions) {
    let myCategoryExists = DataRoleHelper.hasRoleInDataView(options.dataViews[0], "myCategory");
...
```

## hasRoleInValueColumn

DataView の結果の値に指定したロールが存在するか確認する。

```typescript
function hasRoleInValueColumn(valueColumn: DataViewValueColumn, name: string): boolean;
```

### hasRoleInValueColumn 例

以下の例では myCategory が結果の値に存在するか確認。

```typescript
import DataRoleHelper = powerbi.extensibility.utils.dataview.DataRoleHelper;

public update(options: VisualUpdateOptions) {
    let myCategoryExists = options.dataViews[0].categorical.values.some((column) => { 
        return DataRoleHelper.hasRoleInValueColumn(column, "myCategory") });
...
```

# DataViewObjects 

`DataViewObjects` は Object の値を取得する機能を提供

`powerbi.extensibility.utils.dataview.DataViewObjects` モジュールで以下の機能を提供

- [getValue](#getvalue)
- [getObject](#getobject)
- [getFillColor](#getfillcolor)
- [getCommonValue](#getcommonvalue)

## getValue

Object の値を取得。Fill など値が型の場合は型を取得

```typescript
function getValue<T>(objects: DataViewObjects, propertyId: DataViewObjectPropertyIdentifier, defaultValue?: T): T;
```

### getValue の例

以下の例では、myProperty オブジェクトの textColor プロパティを取得。戻り値は Fill オブジェクト。結果がない場合は black を初期値として設定。

```typescript
import DataViewObjects = powerbi.extensibility.utils.dataview.DataViewObjects;

public update(options: VisualUpdateOptions) {
    let myPropertyTextColor = DataViewObjects.getValue(options.dataViews[0].metadata.objects, {
        objectName: "myProperty",
        propertyName: "textColor"
    }, "black");
...
```

## getObject

Objects から特定の Object を取得。

```typescript
function getObject(objects: DataViewObjects, objectName: string, defaultValue?: IDataViewObject): IDataViewObject;
```

### getObject の例

以下の例では myProperty オブジェクトを取得。

```typescript
import DataViewObjects = powerbi.extensibility.utils.dataview.DataViewObjects;

public update(options: VisualUpdateOptions) {
    let myPropertyObj = DataViewObjects.getObject(options.dataViews[0].metadata.objects,"myProperty");
...
```

## getFillColor

Object の solid カラープロパティを取得 (fill の場合のみ)。GetValue と異なり色の値が返る。

```typescript
function getFillColor(objects: DataViewObjects, propertyId: DataViewObjectPropertyIdentifier, defaultColor?: string): string;
```

### getFillColor の例

以下の例では、myProperty オブジェクトの textColor の色の値を取得。オブジェクトがない場合は black を初期値として設定。

```typescript
import DataViewObjects = powerbi.extensibility.utils.dataview.DataViewObjects;

public update(options: VisualUpdateOptions) {
    this.myPropertyTextColor = DataViewObjects.getFillColor(options.dataViews[0].metadata.objects, {
        objectName: "myProperty",
        propertyName: "textColor"
    }, "black");
...
```

## getCommonValue

色か他の値かに関わらず利用できる共通関数。

```typescript
function getCommonValue(objects: DataViewObjects, propertyId: DataViewObjectPropertyIdentifier, defaultValue?: any): any;
```

### getCommonValue 例

以下の例では、myProperty オブジェクトの textColor の色の値を取得。オブジェクトがない場合は black を初期値として設定。

```typescript
import DataViewObjects = powerbi.extensibility.utils.dataview.DataViewObjects;

public update(options: VisualUpdateOptions) {
    this.myPropertyTextColor = DataViewObjects.getFillColor(options.dataViews[0].metadata.objects, {
        objectName: "myProperty",
        propertyName: "textColor"
    }, "black");
...
```

# DataViewObject

`DataViewObject` は Object の値を取得する機能を提供

`powerbi.extensibility.utils.dataview.DataViewObject` モジュールで以下の機能を提供

- [getValue](#getvalue)
- [getFillColorByPropertyName](#getfillcolorbypropertyname)

## getValue

Object のプロパティの値を名前で取得。

```typescript
function getValue<T>(object: IDataViewObject, propertyName: string, defaultValue?: T): T;
```

### getValue の例

以下の例では myProperty オブジェクトの textColor プロパティを取得。Fill 型の結果が返る。

```typescript
import DataViewObject = powerbi.extensibility.utils.dataview.DataViewObject;
public update(options: VisualUpdateOptions) {
    this.myPropertyTextColor = DataViewObject.getValue(
        options.dataViews[0].metadata.objects.myProperty, "textColor");
...
```

## getFillColorByPropertyName

Object の solid カラープロパティを取得 (fill の場合のみ)。GetValue と異なり色の値が返る。

```typescript
function getFillColorByPropertyName(object: IDataViewObject, propertyName: string, defaultColor?: string): string;
```

### getFillColorByPropertyName の例

以下の例では myProperty オブジェクトの textColor プロパティの色の値を取得。

```typescript
import DataViewObject = powerbi.extensibility.utils.dataview.DataViewObject;
public update(options: VisualUpdateOptions) {
    this.myPropertyTextColor = DataViewObject.getFillColorByPropertyName(
        options.dataViews[0].metadata.objects.myProperty, "textColor");
...
```

# converterHelper

`converterHelper`は DataView 内のカテゴリやシリーズ、列の確認などの機能を提供。

`powerbi.extensibility.utils.dataview.converterHelper` モジュールで以下の機能を提供

- [categoryIsAlsoSeriesRole](#categoryisalsoseriesrole)
- [getSeriesName](#getseriesname)
- [getMiscellaneousTypeDescriptor](#getmiscellaneoustypedescriptor)
- [isImageUrlColumn](#isimageurlcolumn)
- [isWebUrlColumn](#isweburlcolumn)
- [hasImageUrlColumn](#hasimageurlcolumn)

## categoryIsAlsoSeriesRole

カテゴリに配置したフィールドがシリーズでもあるか確認。

```typescript
function categoryIsAlsoSeriesRole(dataView: DataViewCategorical, seriesRoleName: string, categoryRoleName: string): boolean;
```

### categoryIsAlsoSeriesRole の例

以下の例では、myCategory に配置されたフィールドが myGrouping にもいるか確認。

```typescript
import converterHelper = powerbi.extensibility.utils.dataview.converterHelper;

public update(options: VisualUpdateOptions) {
    let categoryIsAlsoSeriesRole = converterHelper.categoryIsAlsoSeriesRole(options.dataViews[0].categorical, 
        "myGrouping","myCategory");
...
```

## getSeriesName

metadata の列より、シリーズの名前を取得。

```typescript
function getSeriesName(source: DataViewMetadataColumn): PrimitiveValue;
```

### getSeriesName の例

以下の例では、初めの列からシリーズ名を取得。

```typescript
import converterHelper = powerbi.extensibility.utils.dataview.converterHelper;

public update(options: VisualUpdateOptions) {
    let seriesName = converterHelper.getSeriesName(options.dataViews[0].metadata.columns[0]);
...
```

## getMiscellaneousTypeDescriptor

metadata の列が MiscellaneousType の場合、MiscellaneousTypeDescriptor を取得。

```typescript
function getMiscellaneousTypeDescriptor(source: DataViewMetadataColumn): MiscellaneousTypeDescriptor;
```

### getMiscellaneousTypeDescriptorの例

以下の例では、初めの列から MiscellaneousTypeDescriptor を取得。

```typescript
import converterHelper = powerbi.extensibility.utils.dataview.converterHelper;

public update(options: VisualUpdateOptions) {
    let seriesName = converterHelper.getMiscellaneousTypeDescriptor(options.dataViews[0].metadata.columns[0]);
...
```

## isImageUrlColumn

列が image url を含むか確認。

```typescript
function isImageUrlColumn(column: DataViewMetadataColumn): boolean;
```

### isImageUrlColumn の例

以下の例では初めの列が image url を含むか確認。

```typescript
import converterHelper = powerbi.extensibility.utils.dataview.converterHelper;

public update(options: VisualUpdateOptions) {
    let containsImageUrl = converterHelper.isImageUrlColumn(options.dataViews[0].metadata.columns[0]);
...
```

## isWebUrlColumn

列が web url を含むか確認。

```typescript
function isWebUrlColumn(column: DataViewMetadataColumn): boolean;
```

### isWebUrlColumn の例

以下の例では初めの列が web url を含むか確認。

```typescript
import converterHelper = powerbi.extensibility.utils.dataview.converterHelper;

public update(options: VisualUpdateOptions) {
    let containsWebUrl = converterHelper.isWebUrlColumn(options.dataViews[0].metadata.columns[0]);
...
```

## hasImageUrlColumn

DataView に image url を含む列があるか確認。

```typescript
function hasImageUrlColumn(dataView: DataView): boolean;
```

### hasImageUrlColumn の例

以下の例では DataView に image url を含む列があるか確認。

```typescript
import converterHelper = powerbi.extensibility.utils.dataview.converterHelper;

public update(options: VisualUpdateOptions) {
    let containsWebUrl = converterHelper.hasImageUrlColumn(options.dataViews[0]);
...
```

# まとめ

値のパースは面倒な点が多いので、ユーティリティをうまく使いたいです。また多くのサンプルはユーティリティベースのため、読めるようになるためにも必要な知識です。是非自分のカスタムビジュアルでも使ってみてください。

[目次に戻る](https://qiita.com/kenakamu/items/f57ab7f73d6ea10bc9d9)

# 参照
[Microsoft Power BI visuals DataViewUtils](https://github.com/Microsoft/powerbi-visuals-utils-dataviewutils)
[How to install](https://github.com/Microsoft/powerbi-visuals-utils-dataviewutils/blob/master/docs/usage/installation-guide.md)
[Usage Guide](https://github.com/Microsoft/powerbi-visuals-utils-dataviewutils/blob/master/docs/usage/usage-guide.md)