3.kintoneからのJavaScript呼び出しとJavaScript内でのAjaxの記載について
ここでは、kintoneへのJavaScriptの適用方法その他の説明は割愛します。はじめよう kintone JavaScript API の記述が大変参考になります。私も最初はここから勉強しました^^
ただし、本稿でもまた、前書きを書かねばなりません。恐縮ですm(_ _)m
それは、kintoneからAmazon API呼び出しのタイミングについてです。冒頭でも挙げたように、本稿ではレコードの詳細画面でAmazon情報を呼び出すのが目的です。しかし、レコード詳細画面の編集時画面での実装については、本稿では触れていません。できなくもないのですが、現在の2014年9月時点のkintone JavaScript APIでは、レコード詳細画面のテキストボックスの値変更時にJavaScriptを呼び出すことができません。そのため、編集時画面でAmazonからの情報を表示させる意味がないと判断し、記載から外させて頂きました。ご了承下さい。ただし、レコード編集画面のフィールド値変更時イベントについては、2015/07/12の定期メンテナンスにおけるkintone API更新で実装されました。が、本稿ではまだ検証に至っておらず、掲載しておりません。ご了承ください。
また、Amazonからの呼び出し自体にも、全く問題がない訳ではありません。Amazon側にリクエストを続けて投げると、Http/1.1 Service Unavailableのエラーが返ってくる場合があります。なので、リクエストはほどほどに・・・ということなのでしょう。このエラー対策については、jQueryのAjaxで、Errorハンドラから再帰呼び出しを試したのですが、calleeとuse strictが両立できていません。なので、時間をおいて再実行ということでご了承ください。
最後にクロスドメイン制約についてです。本稿の目指す実装は、*****.cybozu.com から ****.jp/ または *****.co.jpといった違ったドメインのサーバーにAjaxリクエストを発行します。が、そのような別ドメインへのリクエストは、セキュリティ上脆弱になりかねないため、環境によっては簡単に通信ができないようになっています。本稿ではphpからkintoneへ渡すデータ形式をXMLにしました。JSONPを使えばクロスドメイン制約についても解決できるようです。が、今回はXMLを選択しました。ご了承頂ければと思います。随所にそのあたりについて
あと、jQueryの呼び出しも必要となりますので、kintoneのアプリ設定画面から、JavaScript登録を忘れずに。
さて、気を取り直して実装開始^^
以下は、AmazonCallBookInfoDetail.jsの内容です。200行あるすべてのコードを開示しながら、適宜解説を加えていきたいと思います。
4行目 jQuery.support.cors = true; はクロスドメイン制約をかいくぐるための呪文の一つです。
5行目 kintone.events.on('app.record.detail.show', function(event){
は詳細レコード呼び出し時に書く呪文です。
6行目 実行しているブラウザの種別をここで取得しています。呼び出し先のURLがhttpsから始まっても、画像はhttpが返されることがあり、それを防ぐために一部ブラウザによって画像のアドレスを変更しています。
8行目 表示したkintone詳細レコードのISBNフィールド(フィールド名はF_Isbn)の値を取得しています。
9行目 8行目で取得したIsbnから – ハイフンを除去しています。
10行目 8行目で取得したIsbnから ISBN を除去しています。
11行目 kintoneフォーム上で事前に作成しておいたスペースをここで取得しています。このスペース内にAmazon からの情報を書き込んでいきます。
12行目 kintoneフォーム上で事前に作成しておいたスペースをここで取得しています。このスペース内にAmazon からの大きな画像を表示させます。
15-17行目 本稿で実装する内容では、ユーザ入力データからのサニタイズ処理はあまりありません。唯一この場所で実施しています。数値がどうかをチェックしていますが、ISBNの末尾一桁のチェックディジット結果が X となる場合があり、その場合のみ許可しています。
19行目 12行目で取得した画像表示用イメージの横幅を指定しています。
20行目 11行目で取得したAmazonデータ表示用スペースにユーザを飽きさせないために表示しております。
24行目 先に紹介しましたが、codeとIsbnという2つのパラメーター以外に、nowというパラメーターも追加しています。実はnowパラメーターは以下の処理では使われていません。投げるリクエストURLを常に変化させないとInternet Explorer上でうまく値が戻ってこないという情報から、このような記載を設けています。
25行目 ここは xml を指定しています。おそらくはjsonpでもjsonでもうまくいくことでしょう。
26行目 キャッシュをOFFにしないとInternet Explorerで正常に戻ってこないという情報を基に追加しました。
27行目 非同期処理をONにしています。ここをOFFまたは記載なしにすると、何らかのエラーがAjaxで発生した場合、ブラウザがフリーズしてしまいます。
28行目 10000ミリ秒、つまり10秒間だけphpからの戻り値を待ちましょう。という指定です。
29行目 正常にデータが戻ってきた場合の処理を30行目以下に記載します。関数の引数dataが、成功した場合に受け取るデータが格納された変数です。
30-79行目 商品情報に関するすべてのAmazonからの戻り値を変数として宣言してあります。正直、ここまでやる必要はありません。ありませんとも。
82行目以下は、実際のXMLの内容を基に、DOMの森を探索することになります。なので、以下ではXMLを実際に表示させてみます。
この中で実際に表示させるのに必要なのは、32行目の<Item>タグ以下のデータとなります。
53-101行目までは各種商品イメージの情報が記載されています。この中で53行目から67行目までのSmallImageとMediumImageとLargeImageは。その下の68行目から101行目までの間に同一の情報があります。なので、この部分は取得を省略してもよいでしょう。
102行目以下は主要な情報が並んでいます。これらはほぼ取得すべきところです。ただし、データの種類によってはタグ自体がないものもあります。以下の例でいうと、104行目の<Creator>タグは小説のように著者がある本には登場しません。著者がある本は<Author>タグが替わりをつとめます。また、112行から117行の<Languages>タグや135行目の<ReleaseDate>タグがないデータがあることも確認しています。
もう一件、AmazonからのXMLデータで気を付けるべき点があります。それは書籍とkindle書籍の場合です。kindle書籍はISBN項目を持っていないのですが、Amazonの仕様でISBNに対してデータ検索結果にkindleデータも含まれます。XML上では、2つの<Item>タグが紐付いています。その場合は、JavaScript側で<Item>タグ内をループさせるのですが、<ISBN>タグの有無で判別しています。
大体のXMLデータの構造がおわかり頂けましたでしょうか。先に紹介したAmazonのガイドですが、項目の位置はこちらでガイドされています。ただし、各項目がどのように商品データに関連付いているかはわかりません。なので、上に挙げた項目以外にも存在することもあるかと思います。ご容赦ください。
82行目 jQueryの文法に則り、XMLのデータが格納された変数dataに対し、<item>タグの要素を検索しています。その結果をループしたのが82行目の記述となります。
83行目 条件文が真の場合、84行以下の処理を実行しています。条件文の頭に$(this)とありますが、これは 82行目でループを開始した<item>タグの要素データを指します。<item>タグ内でさらに<ItemAttributes>タグを検索し、さらにその中で<ISBN>タグを検索しています。この構造については、3ページ前のXMLをご参照ください。<ISBN>タグの要素内のテキストを取得しているのが text()です。
つまり、ISBNの要素の有無によって判定を分けています。これは先にXMLの構造を説明する際にも書きましたが、kindle書籍データが含まれている場合の退避処理となります。
84-85行目 <item>タグの要素データ内の<ASIN>タグや<DetailPageURL>タグの要素内データを取得しています。
86行目 82行目で始めた<item>タグ内ループの中で、さらに<ItemLink>タグを検索し、さらに<Description>タグを検索し、それをループしています。
87行目 86行目で始めた<Description>タグ内ループの中で、要素内の文字列が Add to Wish Listに等しいかを尋ねています。
88行目 87行目で条件に合致した場合、<Description>タグと同じレベルの隣のタグ<URL>タグの要素内のデータを取得しています。
90-98行目 87、88行目と同様です。それぞれの<URL>タグの内容を取得しています。
99行目 86行目で始めたループを閉じています。
100行目 82行目で始めた<item>タグ内ループの中で、さらに<ImageSet>タグを検索し、それをループしています。
101-118行目 100行目で始めた<ImageSet>タグ内ループの中で、それぞれの要素タグの内容を取得しています。
119行目 100行目で始めたループを閉じています。
ここまで来たら大分要領がつかめてきたのではないでしょうか。あともう少しで終わりです。お疲れのことでしょう。
私も大分疲れてきました・・・(^^;)
120-146行目 それぞれの要素内データを取得しています。もう説明は不要ですよね。ただ、121行目ですが、Attrという見慣れない記述があります。この部分に対応しているのは XMLの104行目に当ります。<Creator>タグ内に Role="監修"というRole属性の値を取得するために、Attrという命令を発行しています。これを末尾につなげることで、 サイボウズ式編集部 監修 という文字列を組み立てているわけです。
147行目 83行目の<ISBN>タグの有無を判定したif文の終わりです。
148行目 82行目で始めたループを閉じています。
149行目 aws_ASINの値が真かどうかを判定しています。cybozu.com developer networkのTipsの中に(小技)undefined と空文字のスマートな if 文判定がありますが、そちらの記述通り、文字列の取得結果がnull または undefined以外のデータの場合、150行目以下の処理を実行します。
150-173行目 11行目で取得したスペースに対し、innerHTMLを使用して書き込むべき文字列を生成しています。今回の実装では、一つのスペース内に改行で区切ってAmazonからの情報を羅列する手法を採りました。冒頭に記載しましたが、編集時画面に実装し、Isbnの値の変更時に各フィールドに自動入力するほうが望ましいという考えもあります。なお、169行目から173行目は取得したURL文字列を<a>タグの中に含めています。
174行目 6行目でブラウザの種別を取得しました。取得したブラウザ文字列にfirefoxが含まれているかどうか、要は実行しているブラウザの種類を問うているのがこの行です。
175行目 使用ブラウザがfirefoxの場合、12行目で取得したスペースのinnerHTMLに対して書き込む画像の http: を 空白文字 にしています。Firefoxだけが、https:// から https:// の画像を読み込もうとするとエラーが出るため、このような処置を取っています。
176-178行目 使用ブラウザがFirefox以外の場合、12行目で取得したスペースのinnerHTMLに対して書き込む画像のURLをそのまま使用しています
179行目 149行目で判定したaws_ASINの値が偽だった場合に、180~184行目の処理が動作します。なお、Amazon側に渡したISBNコードが不正な値だった場合、XMLは違う種類のメッセージを返します。ほとんどの部分は一緒なのですが、<Items>タグの中の<Item>タグが<Errors>タグに置き換えられています。以下にエラー時のXMLを提示します。
180-184行目 11行目で取得したスペースにエラー情報を含めます。上の図にあるとおりのエラー構造に基づいて、適宜整形して頂ければと思います。
185行目 149行目で判定した条件式の終わりの部分です。
186行目 29行目で判定したAmazonから正常な値が戻ってきたかどうかの判定式の終わりの括弧です。
187行目 Ajax通信に何らかのエラーが生じた場合、処理はこちらのステートメント内で行われます。関数の引数として、XMLHttpRequest、textStatus、errorThrownといった値が返されますので、その値に応じてメッセージを整形されるとよいでしょう。
193-195行目 errorThrownは場所の数だけ配列として戻ってきますので、193~195行では配列の要素数だけ処理を行っています。
196行目 ここでは取得したエラー情報をアラートとしてポップアップ表示しています。Amazon情報を表示するスペースに表示するのもよいでしょう。
197行目 187行目からのエラー時に実行される関数の終わり部分です。
198行目 22行目からの$.ajaxの関数の終わりの括弧です。
200行目 5行目からのkintone詳細レコード表示時に実行される関数を終えるにあたり、戻り値を返しています。
201行目 5行目からのkintone詳細レコード表示時に実行される関数の終わりの括弧です。
203行目 本JavaScript全体の無名関数の終わりの括弧です。
これでJavaScriptの実装は終了です。お疲れ様でした。お互い・・・
さて、JavaScriptを完成させ、あとはこれをkintoneのアプリ管理画面からアップすれば完成!
のはずですが・・・・・実は先ほどのJavaScriptの仕組みでは動かないブラウザがあります。それは。。。。Internet Explorerです。jQueryのAjax関数では、Internet Explorer 9より前のバージョンに対応しておらず、別の仕組みを経由してAjaxを実装する必要があります。なんてこった!またまたクロスドメインではまることになりました。
Googleで検索を行うと、いくつかInternet Explorer 9以前のバージョンでのクロスドメイン実装についてのJavaScriptが存在します。私もいくつか試しましたが、私の環境ではそのうち動いたのが以下の1つだけでした。
MoonScript/jQuery-ajaxTransport-XDomainRequest
なので、今回はこちらのJavaScriptも使わせて頂きました。ありがとうございます。
結果として、kintone上にアップするJavaScriptファイルは3種類となりました。最初にjQuery本体。次にIE9以前対応のクロスドメイン制約対応のJavaScript。最後に今回作成したJavaScriptです。
これで完成です。
と。このように表示が出来ましたでしょうか。レコードを移動すると新たなレコード先でAmazonから呼び出されたデータが表示されます。ただし、あまり短い時間で呼び出すとAmazonからの応答が途絶えてしまうことがあります。その場合は F5でリロードを行って頂ければと思います。
外部認証が必要な複雑なAPIを呼び出す場合、このような方法があるよ、ということを理解頂ければ、さまざまなケースで実装が可能になり、kintoneをより一層ご活用頂くことができることでしょう。ぜひ、お試しください。ただしくれぐれも過度なAmazonへのリクエストはお控えください。1時間当たりのリクエスト回数も上限が定められているようですし・・・・
本件について、文責は全て私、合同会社アクアビットの長井にあります。もし内容についての御質問などございましたら、お寄せいただければ、時間の許す限り、回答させて頂くようにいたします。