お世話になっております。
kintoneでモバイルUIの使い勝手を良くするためのカスタマイズを行おうとして以下の問題に直面しました。原因または代替案についてアドバイスをいただけないでしょうか。
[環境] iPhone Safari version 12.0
[モバイルUI] kintoneの新しいモバイルUI(プレビュー版)を使用
[問題の概要]
新UIの詳細画面の下部に表示される[編集]ボタンが効かなくなる。
ただし、[編集]ボタンを押してからリロードすると編集画面が表示される。
また、画面左上のハンバーガーボタンも効かなくなる。
[問題の詳細]
そもそも mobile.app.record.detail.show イベントがPromiseをサポートしていないか、Promiseで対処しようとしていることが誤りなのかもしれないのですが、以下のようにPromiseを使用したコードを記述すると概要に記述した問題が発生
します。
[再現コード]
// 極力短くて再現可能なコードにしています。
(function() {
“use strict”;
kintone.events.on(“mobile.app.record.detail.show”, function(event) {
var param = {
‘app’: event.appId, “fields”: [“$id”],
};
return kintone.api(‘/k/v1/records’, ‘GET’, param).then(function(resp) {
var records = resp.records
alert(‘TEST’)
return event;
}).catch(function(resp) {
event.error = resp.message;
return event;
});
});
})();
[背景]
kintoneのモバイルUIの詳細画面に、前後のレコードに移動するためのボタンまたはリンクが存在しないため、前後のレコードをクエリで特定してからURLを決定し、詳細画面の上部にリンクを表示しようとしています。
リンクの表示自体はできたのですが、[編集]ボタンやハンバーガーボタンが効かなくなって困っています。
実際にはalert()の部分でリンクを作ってヘッダーに設置してる感じですかね?
PC版でもモバイル版でも共通なんですが、detail.showなどshow系のイベントハンドラでpromiseをreturnすることはできません。submit系のハンドラと同じ感覚で書くと動かないんです。
Promise返せるようになった方が便利なので、kintone のアップデートには期待したいですけどね。現状はどうしても無理なので。
ただ今回の要件としては、eventオブジェクト自体を変更する必要はなさそうなので、
return kintone.api(…)からreturn外して
kintone.api(…)だけにすれば、うまくいきませんか?
ちなみにMacを持ってたら、MacのSafariをiPhoneモードで表示するとコンソールとかも出せるので、デバッグしやすいですよ!貼ってもらったコードでコンソール出すと「Promiseは返せません」的な英語エラーが出てると思います。
赤座 さん
早速ご回答いただきありがとうございます。
また、デバッグのポイントについてもアドバイスをくださりありがとうございます。
Macを持っていないので教えて頂いた方法は試せないのですが、Chromeでのデバッグ方法を模索してみます。
さて、本題に戻りますが、ご推察のとおりalert()の部分でリンクを作ってヘッダーに設置しています。骨子を抜き出しつつ(エラー判定なども除いて)動作する状態にしたコードを書きましたので、本コメントの末尾に記載いたします。
色々と試してみたのですが、returnから外してもうまくいかず、Promise前提の書き方(asyncとawaitを使った書き方 or 元々のreturnを使った書き方)にしないとresp1.recordsがundefinedになってしまい期待通りの結果にはなりませんでした。
後述のコードを「asyncとawaitを外した状態で動くコードにすること」と「非同期処理でspace.innerHTMLを変更するコードにすること」を両立できれば良いはずなのですが、力量不足で両立できていないので引き続き対応方法を検討してみます。
// 実施内容の骨子(エラー判定およびクエリ条件は省略)
(function() {
“use strict”;
kintone.events.on(“mobile.app.record.detail.show”, async function(event) {
var param = {
‘app’: event.appId, “fields”: [“$id”],
//‘query’: 実際には前後のレコードを抽出するための検索条件を記述する
};
var space = kintone.mobile.app.getHeaderSpaceElement();
var resp1 = await kintone.api(‘/k/v1/records’, ‘GET’, param);
var records = resp1.records;
var nextUrl = ‘/k/m/’ + event.appId + ‘/show?record=’ + records[0].$id.value
space.innerHTML = ‘<div><a href="’ + nextUrl + ‘">次へ</a></div>’
return event;
});
})();
パソコンでも、こんなURLにアクセスすれば普通にモバイル版デバッグできますね。
https://ドメイン.cybozu.com/k/m/アプリID/
僕も手元で動かして、thenバージョン、asyncバージョン両方動かせました。
まずthenバージョンですが、うまく伝わってなかったかもしれません。
kintone.api().then().catch()の形はそのまんまで、returnだけをやめるという意味でした。
これでうまく動きます。
(function() {
"use strict";
kintone.events.on("mobile.app.record.detail.show", function(event) {
var param = {
'app': event.appId, "fields": ["$id"],
};
kintone.api('/k/v1/records', 'GET', param).then(function(resp) {
var records = resp.records
// thenの中で全部やればうまくいく
var space = kintone.mobile.app.getHeaderSpaceElement();
var nextUrl = '/k/m/' + event.appId + '/show?record=' + records[0].$id.value
space.innerHTML = '<div><a href="' + nextUrl + '">次へ</a></div>'
}).catch(function(resp) {
// event.errorをPromiseで使うのは諦める
console.error(resp)
alert(resp.message);
});
// eventをreturnするならPromiseの外側で
return event;
});
})();
asyncの場合は、イベントハンドラのコールバック関数をasyncにしちゃうと
その時点で「必ずPromiseを返すコールバック関数」になるのでNGですね。
ちょっとネストが深くなって気持ち悪いんですが、
「普通のイベントハンドラの中でasync即時関数を実行」すると、
イベントハンドラのコールバック自体はPromiseにならないのでまともに動きます。
(function() {
"use strict";
kintone.events.on("mobile.app.record.detail.show", function(event) {
// 即時関数としてもう一段async functionを作る
(async function() {
var param = {
'app': event.appId, "fields": ["$id"],
//'query': 実際には前後のレコードを抽出するための検索条件を記述する
};
var space = kintone.mobile.app.getHeaderSpaceElement();
var resp1 = await kintone.api('/k/v1/records', 'GET', param);
var records = resp1.records;
var nextUrl = '/k/m/' + event.appId + '/show?record=' + records[0].$id.value
space.innerHTML = '<div><a href="' + nextUrl + '">次へ</a></div>'
})();
// eventをreturnするならasync(Promise)の外側で
return event;
});
})();
Promise、なかなか奥が深いですよねー。参考になれば!
アドバイスをいただいたおかげで[編集]ボタンやハンバーガーボタンに悪影響を与えることなく、モバイルUIのレコード詳細画面の上部に前後のレコードに移動するためのリンクを設置することができました。
もっと綺麗に書けそうですが、成功したコード(骨子以外の処理は除く)を以下に記します。
おかげさまで実アプリへの組み込みも完了しました。
(function() {
“use strict”;
kintone.events.on(“mobile.app.record.detail.show”, function(event) {
var param1 = {
‘app’: event.appId, “fields”: [“$id”],
//‘query’: 前レコードの検索条件
};
var param2 = {
‘app’: event.appId, “fields”: [“$id”],
//‘query’: 次レコードの検索条件
};
var space = kintone.mobile.app.getHeaderSpaceElement();
var promise1 = kintone.api(‘/k/v1/records’, ‘GET’, param1);
var promise2 = kintone.api(‘/k/v1/records’, ‘GET’, param2);
Promise.all( [promise1, promise2] ).then((resps) => {
var records1 = resps[0].records;
var records2 = resps[1].records;
var prevUrl = ‘/k/m/’ + event.appId + ‘/show?record=’ + records1[0].$id.value; // 実際は適切な前レコードの番号を特定してrecord=の値にする
var nextUrl = ‘/k/m/’ + event.appId + ‘/show?record=’ + records2[0].$id.value; // 実際は適切な次レコードの番号を特定してrecord=の値にする
space.innerHTML = ‘<div style=“text-align:center”><a href="’ + prevUrl + ‘“><< 前へ</a> | <a href=”’ + nextUrl + ‘">次へ >></a></div>’
}).catch(error => {
alert(error.message)
});
});
})();