サンプルプロジェクトとユースケース > ユースケース: 3D-Guided Service Instructions > 3D-Guided Service Instructions 302: 単純な ThingWorx Service の Vuforia Studio への追加 > GetPriceAvailability の呼び出しと serviceInvokeComplete イベントリスナーの使用
  
GetPriceAvailability の呼び出しと serviceInvokeComplete イベントリスナーの使用
getPriceAvailability サービスを Vuforia Studio に追加した後、このサービスをエクスペリエンスから呼び出してデータを操作できるようにする必要があります。そのためには、3D-Guided Service Instructions 301のコードを更新する必要があります。また、新しいイベントリスナーをコードに追加し、このサービスのデータにエクスペリエンスからアクセスできるようにする必要があります。
* 
このセクションに記載されているコードの完全版は、GitHub の Appendix 1 で見つかります。
1. 「Home.js」を開きます。
2. userpick イベントリスナーで、以下の変更を行います。
a. priceString 変数を削除します。データにはモデルの属性からではなく、Vuforia StudioshoppingThing からアクセスすることになるからです。
b. meta という名前のオブジェクトを作成してアプリケーションスコープを設定し、スクリプト内のさまざまな関数からこのオブジェクトにアクセスできるようにします。partNameinstructionNamepartNumber の各変数を、meta オブジェクトのプロパティとして使用できるように変更します。
//
// create an object with the properties below that are based on attribute names from Creo Illustrate for this model. Use metadata.get to obtain the data from the JSON properties for this occurrence.
$scope.meta = {
partName : metadata.get(pathId, 'Display Name'),
instructionName : metadata.get(pathId, 'illustration'),
partNumber : metadata.get(pathId, 'partNumber'),
} // $scope.meta end
c. price 変数と priceInfo アプリケーションパラメータは、priceString 変数に直接依存するので削除します。
d. meta オブジェクトからの partNamepartNumber の呼び出しに使用する新しい構文と一致するように、itemName アプリケーションパラメータと itemNumber アプリケーションパラメータの定義を編集します。次に、target という名前の新しい変数を作成し (アプリケーションスコープを設定して)、これを userpick イベントの targetName 変数に設定します。
//
// set itemName app parameter to be equal to the partName variable, same relationship with itemNumber and partNumber and priceInfo and price.
// Set the itemCount to 1 for the purpose of this section, since it is not hooked up to an actual inventory.
$scope.app.params.itemName = $scope.meta.partName;
$scope.app.params.itemNumber = $scope.meta.partNumber;
$scope.app.params.itemCount = 1;

$scope.target = targetName;
e. 以下のコードを追加して、shoppingThing から getPriceAvailability サービスをトリガーできるようにします。このコードは twx.app.fn.triggerDataService ('<TWX エンティティ>', '<TWX サービス>', '<パラメータ>') という形式を使用します。triggerDataService 関数は ThingWorxVuforia Studio が提供するサービスを呼び出します。ThingWorx のエンティティやサービスとともに、サービスが受け取る入力パラメータが呼び出されます。ここでは shoppingThing という Thing を呼び出します。これにはすべての部品情報、呼び出すサービス (getPriceAvailability)、選択した部品の部品番号のパラメータ (pid) が保持されます。このサービスの出力は、serviceInvokeComplete イベントリスナーを呼び出す非同期コールバックを使用して呼び出します。serviceInvokeComplete イベントリスナーはこのセクションの以降の手順で作成します。
//
// call the getPriceAvailability ThingWorx service based on partNumber
twx.app.fn.triggerDataService('shoppingThing', 'getPriceAvailability', {pid: $scope.meta.partNumber})
f. 以下のポップアップの if 文は、userpick イベントリスナーから serviceInvokeComplete イベントリスナーに移動します。PTC Metadata API の閉じ括弧、catch 文、userpick の閉じ括弧をコピーして、サービストリガーの下に貼り付けます。ポップアップの disassemble 関数の下にコピーしたコードは削除してください。削除しないと、余分なブラケットが残ります。
}) //end brackets for PTC API and .then
//
//catch statement if the promise of having a part with metadata is not met
.catch( (err) => { console.log('metadata extraction failed with reason : ' +err) })
}) //end brackets for userpick function. Will continue to move throughout code
3. userpick イベントリスナーを更新した後は、serviceInvokeComplete イベントリスナーを作成します。このイベントリスナーは、getPriceAvailability サービスが完了した後にのみアクティブ化されます。
a. 以下のイベントリスナーのコードを、ポップアップの if 文の上に追加します。if 文の先頭から disassemble 関数の末尾までコードを選択し、インデントを解除して行頭を適切に調整します。
* 
この行の横に赤い円に囲まれた「X」が表示されるのは、この行の閉じ括弧がまだ作成されていないからです。これは想定内の動作です。
$scope.$on('getPriceAvailability.serviceInvokeComplete', function(evt) {
b. データが ThingWorx から動的に追加されるようになると、ポップアップを呼び出すプロセスが変わります。serviceInvokeComplete イベントリスナーの下に、rowData という名前の変数を作成します。この変数は getPriceAvailability サービスによって作成されたインフォテーブルの最新行を呼び出します。
//
// variable holding all data for the current row in the infotable
var rowData = twx.app.mdl['shoppingThing'].svc['getPriceAvailability'].data.current
c. 次に、選択したオブジェクトの価格を特定するスクリプトを追加します。price 変数は、ThingWorx の情報に応じて、選択行の部品の価格に $ を付けた文字列として設定されます。選択行の部品が使用できない場合は、‘UNAVAILABLE’ に設定されます。price 変数が使用する情報は条件演算子を使用して作成します。部品が使用可能かどうかを検証してから、部品の価格が確認されるようにします。これは同じ条件を持つ if 文を使用して作成することもできます。同様のロジックを使用して、カートに含まれるアイテムの金額を合計する priceInfo アプリケーションパラメータを作成します。
//
// price is going to be the variable that is referenced in the popup, while the app parameter priceInfo will be used for adding the total in the cart
var price = rowData.avail === true ? '$' + rowData.price
: 'UNAVAILABLE';
$scope.app.params.priceInfo = rowData.avail === true ? parseFloat(rowData.price)
: undefined ;
d. meta という名前の変数を作成します。この変数を使用して $scope.meta オブジェクトをローカルオブジェクトとしてイベントリスナーに取り込み、この値に簡単にアクセスできるようにします。
//
// create a variable to bring the $scope.meta object into this event listener as a local object
let meta = $scope.meta
e. ポップアップに shoppingThing の情報が追加され、部品が使用可能かどうかを特定できるようになるので、ポップアップのテンプレートは関数 (後で作成します) を使用して設定するようにします。このため、現時点では以前に作成した if 文の条件と閉じ括弧を削除し、ポップアップを呼び出すコードのインデントを解除します。
f. これまで if 文の else ケースで使用していたポップアップのコードを削除します。template プロパティの値も削除します。この値は次の手順で置き換えます。これらのコード行の横にエラーインジケータが表示されますが、テンプレートの値を設定すると解決します。
g. template プロパティに setTemplate 関数 (後で作成します) を呼び出す値を指定します。この関数には選択した部品の meta オブジェクトと price 変数の入力情報を渡します。ポップアップを呼び出すコードは以下のようになります。
//
// adds an ionic popup when a part is clicked
$scope.popup = $ionicPopup.show({
//
//call the function for setting the template
template: $scope.setTemplate(meta, price),
//
// set the scope for the popup
scope: $scope
}); //end of ionic popup
h. disassemble 関数では、モデルのアニメーションシーケンスを設定する modelObjectmodel プロパティと instruction プロパティを更新する必要があります。上の手順で作成した $scope.target 変数にここからアクセスし、meta オブジェクトに格納されている新しい場所の情報で instructionName を更新します。
//
// function to be bound to the Disassemble button in the popup
$scope.disassemble = function () {
//
// set an object that targets the model and its instruction property
var modelObject = { model: $scope.targetName,
instruction: 'l-Creo 3D - ' + meta.instructionName + '.pvi' };
//
// set the sequence for the quadcopter to be the name of the associated instruction
$scope.view.wdg.quadcopter.sequence = modelObject.instruction
} //disassemble function end
i. 閉じ括弧と丸括弧を追加し、getPriceAvailability サービスの呼び出しを完了します。
}) // getPriceAvailability end
4. エクスペリエンスでの Ionic ポップアップの使用プロセスを合理化するには、別の userpick イベントリスナーを追加する必要があります。このイベントリスナーは、別の部品をクリックする前に閉じていないポップアップを閉じるために使用されます。トリガーされると、このイベントリスナーは、選択されている次の部品で新しいポップアップとシェードを開く前に、最初にクリックした部品のポップアップを閉じてから、そこからのシェーダを除去します。
//
// userpick event listener that will take place when you click on another part without choosing an option in the popup
$scope.$on('userpick', function (event, targetName, targetType, eventData) {

//
// close the popup and remove the shader from the part even when you didn't chose an option from the popup
$scope.popup.close()

$scope.hiliteOff()

}) // closing userpick end
5. エクスペリエンスを更新する最後の手順として、上記で言及した setTemplate 関数を作成します。この関数は、ポップアップに部品の情報を表示するときに使用される template 変数を設定します。この関数には、部品に逆アセンブリシーケンスが関連付けられているかどうか、また部品が shoppingThing で使用可能かどうかを特定するためのロジックを追加します。
a. clearCart 関数の下に setTemplate という名前の関数を作成し、metaprice の入力情報を渡します。
//
// function for setting the template for the Ionic popup
$scope.setTemplate = function (meta, price) {

} // setTemplate end
b. 最初に、選択した部品に逆アセンブリシーケンスが関連付けられているかどうかを特定するためのロジックを、この関数に追加します。instr 変数は条件演算子を使用して、metainstructionName プロパティが指定されているかどうかを特定します。これは、これまで使用していた if 文の役割を果たします。シーケンスが関連付けられている場合はボタンが作成されます。このボタンをクリックすると、disassemble 関数がトリガーされ、シェーダーがオフになり、ポップアップが閉じられます。シーケンスが関連付けられていない場合、instr は空の文字列になります。
//
// if there is a disassembly sequence associated with the part, create a Disassemble button in the popup, if not, no button will appear
var instr = meta.instructionName.length > 0 ? '<div class="btndisassemble" ng-click="hiliteOff();popup.close();disassemble();">Disassemble</div>'
: '';
c. さらに、部品が使用可能かどうかを特定するためのロジックを追加する必要があります。shoppingThing の情報に基づいて部品に価格が関連付けられているかどうかを特定する条件演算子の結果を格納するための addTo 変数を作成します。価格が関連付けられている場合は、ポップアップに価格とクリック可能な Add to Cart ボタンが表示されます。このボタンをクリックすると、addToCart 関数がトリガーされます。価格が関連付けられていない場合は、ポップアップに価格のみが表示されます。
//
// if price != unavailable, define an add to cart button and have the price displayed in the popup, if it is unavailable, just display price
var addTo = price != 'UNAVAILABLE' ? price + '&nbsp; </div><div class="btnadd" ng-click="hiliteOff();popup.close();addToCart();">Add to Cart</div>'
: price ;
d. ポップアップに使用するボタンを特定した後、以前のポップアップ用に作成したものと同じ template 変数を作成します。ポップアップには選択した部品の数量、部品番号、部品名、価格と、部品に適したボタンが表示されます。ポップアップを閉じる Continue ボタンを再び追加します。template 変数に return 文を追加し、関数を実行すると結果が出力されるようにします。これで、setTemplate 関数が完成しました。
//
// build the template for the popup
var template = '<div>' + $scope.app.params.itemCount + 'x  ' + meta.partNumber +
'&nbsp;</br>' + meta.partName +
'&nbsp;</br>' + addTo + instr +
'<div class="btncontinue" ng-click="hiliteOff();popup.close();">Continue</div>' ;

//
// return the template variable when this function is called
return template
6. 「プレビュー」をクリックします。
a. クワッドコプターの底部をクリックし、ポップアップオプションを表示します。部品は使用可能ですが、逆アセンブリシーケンスは関連付けられていません。Continue をクリックし、ポップアップを閉じます。
* 
Continue ボタンをクリックしないで別の部品をクリックすると、ポップアップは開いたままになり、積み重なって表示されます。
b. いずれかのローターを選択してポップアップを表示します。逆アセンブリシーケンスは関連付けられており、部品は使用可能です。
c. クワッドコプターの上部を選択してポップアップを表示します。逆アセンブリシーケンスは関連付けられておらず、部品は使用できません。
いずれかのモーターを選択してポップアップを表示します。逆アセンブリシーケンスは関連付けられていますが、部品は使用できません。
7. これで、このセクションは完了です。このセクションのほかのすべてのコードは、3D-Guided Service Instructions 301と同じです。部品をクリックすると、上に示した 4 つのポップアップのいずれかのバージョンが表示されます。また、該当する場合は、部品をカートに追加できます。「userInput」ウィジェットを使用して部品をサーチすることもできます。
次に、ThingWorx を使用した永続的なショッピングカートの作成に進みます。