示例项目和用例 > 用例:3D-Guided Service Instructions > 3D-Guided Service Instructions 302:将简单 ThingWorx 服务添加到 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 Studio 中的 shoppingThing 访问数据,而不是从模型中的属性访问数据。
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. 编辑 itemNameitemNumber 应用程序参数的定义,使其与从 meta 对象调用 partNamepartNumber 的新语法保持一致。然后,创建名为 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 服务>’, ‘<parameter>’)。triggerDataService 函数调用 ThingWorxVuforia Studio 提供的服务,其中会调用 ThingWorx 实体和服务,以及服务所采用的任何输入参数。在我们的示例中,所调用的事物是 shoppingThing,其中包含所有零件信息、被调用的服务 (getPriceAvailability) 和参数 (pid),该参数是用于选定零件的零件号的参数。此服务的输出使用异步回调的方式进行调用,即调用 serviceInvokeComplete 事件侦听器。本节后续步骤中会创建 serviceInvokeComplete 事件侦听器。
//
// call the getPriceAvailability ThingWorx service based on partNumber
twx.app.fn.triggerDataService('shoppingThing', 'getPriceAvailability', {pid: $scope.meta.partNumber})
f. 将以下弹出窗口 userpick 语句从 serviceInvokeComplete 事件侦听器移入 if 事件侦听器中。复制 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 函数,需要更新 modelObject 中用于设置模型动画序列的 modelinstruction 特性。我们将从此处访问先前创建的 $scope.target 变量,并将 instructionName 更新为其在 meta 对象中的新位置。
//
// 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 函数下方,使用 metaprice 作为输入参数创建一个名为 setTemplate 的函数。
//
// 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. 此外,还必须添加用于确定零件是否可用的逻辑。将 addTo 变量创建为一个条件运算符的结果,该运算符可根据 shoppingThing 中的信息确定零件是否有关联的价格。如果有,则会在弹出窗口中显示该价格以及一个单击后可触发 addToCart 函数的 Add to Cart 按钮。如果没有,则只有价格会添加到弹出窗口中。
//
// 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 中的相同。您应能够单击零件,随即应显示弹出窗口,并且弹出窗口中所示内容应与上述四个选项之一相同。如有相应按钮,您应能够将零件添加到购物车中。此外,您还应能够使用 userInput 小组件搜索零件。
在此用例的最后一节中,您将使用 ThingWorx 创建持久化购物车