【SharePoint】JSリンクのフィールド名（内部名・プロパティ名）がわかりづらい

SharePointのJSリンクの改修で、フィールド名がわかりづらく結構手間取ったのでそれについてまとめ

## 経緯
SharePointにJSリンクというのがあり、Excelでいう条件付き書式を実現するのに使われている（らしい）。

今回対応したのはSharePoint製社内イントラのリストで共通で使っていたJSリンクが、特定のリストで使えないというイレギュラー。原因を調べたところ、そのリストについてはフィールドの内部名が他と異なっていたことが判明。このイレギュラーなリストに対応するために既存のJSリンクのフィールド名のみ修正した差分を作成することになった。

## わかりづらい点1: 表示上のフィールド名とJSで取得する内部名が違う
今回の例では表示上のフィールド名は`投稿日`、
表示上のフィールド名とJSで取得する内部名が違う、具体的には問題のフィールドは表示上は
大本の原因は以下の2つ

1. フィールドの内部名は作成時の作成時のフィールド名をもとに決定され、以降フィールド名を変えても内部名は変わらない
2. 全角文字は文字化けしていてそのままでは何のフィールドなのかわからない

厄介なのは後者で、ただでさえ読めない置き換えられた文字が、場合によってはさらに[パーセントエンコーディング](https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%82%BB%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0)されていたりもするという…。~~どうせ置き換えるならパーセントエンコーディングいらない文字列にしてくれればいいのに…。~~

### 内部名の取得
列の内部名はというと、列の編集画面のアドレスから見つかる
（詳細は[SharePoint 2013 で列の内部名を調べる方法 | idea.toString();](https://idea.tostring.jp/?p=470)を参照）
今回の問題のフィールドの場合、アドレス
`http://example.com/（中略）&Field=%5Fx767b%5F%5Fx6821%5F%5Fx65e5%5F`となっていたので、
`Field=`に続く`%5Fx767b%5F%5Fx6821%5F%5Fx65e5%5F`が内部名になる。

フィールド名が半角文字なら内部名も文字化けせずフィールド名そのままなのでこれで話は終わりになるところだが、今回は見ての通り全角文字なので案の定文字化けている。

### パーセントエンコーディングのデコード（必要なら）
今思えば今どきのブラウザなら表示の際にはパーセントエンコーディングをデコードしてくれている気もするのだが、今回[^1]のように何かの拍子でパーセントエンコーディングされることがあるので、その場合デコードする必要がある。

```javascript
const str = `%5Fx767b%5F%5Fx6821%5F%5Fx65e5%5F`;
const internalname = decodeURI(str);
console.log(internalname); // "_x767b__x6821__x65e5_"が返ってくる
```
これをデコードすると`_x767b__x6821__x65e5_`という内部名が手に入る。




### 本来の列名の取得
JSリンクで扱う分にはこれで十分だが、やっぱりこれだと何の列だかわからなくてモヤモヤするので変換してみる。
平たく言うとUnicodeらしい。（詳細は[日本語で作成した列の内部名から列名を調べる方法 - Qiita](https://qiita.com/wataruf01/items/f8b70a10830e71ea6178)を参照）

せっかくなので[Unicode（¥uxxx形式）を文字列へアンエスケープ | JavaScript逆引き | Webサイト制作支援 | ShanaBrian Website](https://shanabrian.com/web/javascript/unicode-unescape.php)を参考に内部名の変換用に関数を作って試してみた。

```javascript
function decodeInternalname(internalname) {
    const re = /_x([0-9a-f]{4})_/g;
    const arr = internalname.match(re);
    let result = '';
    for (str of arr) {
        result += String.fromCharCode(str.replace(re, '0x$1'));
    }
    return result;
};

// 問題のフィールド名を確認する
const internalname = '_x767b__x6821__x65e5_'; 
const result = decodeInternalname(internalname);
console.log(result); // "登校日"が返ってくる
```
どうやら`投稿日`というフィールドをつくるはずが`登校日`と変換間違いしたらしい。

そのまま表示されてたら気になるから結果的に文字化けしててよかったと思った。

## わかりづらい点2: ピリオドを含むプロパティ名が存在する。 

今回だけならともかく、同様のイレギュラーが今後も全くないとは言えない（実際もう1件あった）ので、差分の作成に先駆けて、簡単に列名を変えられるように列名を定数に切り分けるという改修をした。

具体的には仮にフィールド名が`FieldName`だとすると、`ctx.CurrentItem.FieldName`といったようになっているのを複数個所修正する必要があったので、これを

```
var FIELD_NAME = "FieldName";
ctx.CurrentItem[FIELD_NAME];
```

といった感じで列名を定数に切り出し、'FIELD_NAME'一か所だけ変えれば済むようにした。

## 問題
そこで問題となったのが以下のようなコード。

```
ctx.CurrentItem.FieldName.desc
```

このフィールド名を変数に置き換えようとすると、以下のようにしたくなるが、これはエラーになる

```
var FIELD_NAME = "FieldName";
（中略）
ctx.CurrentItem[FIELD_NAME].desc // 間違い
```

結論としては、`FieldName.desc`はこれ全体で一つのプロパティ名であって、`FieldName`オブジェクトのプロパティ`desc`**ではない**。
そのため正しくは以下のようにする必要があった。

```
var FIELD_NAME = "FieldName";
（中略）
ctx.CurrentItem[fieldName + ".desc"]
```

### 詳細
SharePointのリストは1フィールドが複数の値を持っていることがある。

具体的な例としてはハイパーリンク型のフィールドで、これはURLと説明文の2つの値を持っている。
この場合のプロパティ名はURLが`FieldName`、説明文が`FieldName.desc`になっているらしい。

[GASのような](https://developers.google.com/apps-script/reference)クラスの詳細がわかるリファレンスがほしいのだけれど見つからなかったので、Chromeのコンソールで調べたところ以下のように確認できた。

```javascript
console.log(location.pathname) //リストを開いていることを確認。
//"/path/to/list/AnyView.aspx"
console.log(GetCurrentCtx().ListData.Row[0]) //　リストの1行目を取得
//{(中略), FieldName: "https://example.com", FieldName.desc: "リンクの説明", (中略)}
```

## その他参考サイト
[リストおよびライブラリの列の種類とオプション - SharePoint](https://support.microsoft.com/ja-jp/office/%E3%83%AA%E3%82%B9%E3%83%88%E3%81%8A%E3%82%88%E3%81%B3%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AE%E5%88%97%E3%81%AE%E7%A8%AE%E9%A1%9E%E3%81%A8%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3-0d8ddb7b-7dc7-414d-a283-ee9dca891df7)
[decodeURI() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/decodeURI)
[String.fromCharCode() - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode)



[^1]: この時はブラウザで確認せず、アドレスをその場でコピぺして作業をしたのでパーセントエンコーディングされてしまったらしい。