我在Web IDE中创建了一个Master Detail模板应用程序。 (见图)
我有一个包含许多EntitySets的OData服务(ZSV_SURVEY_SRV)。
Master(左侧)调用我的EntitySet SurveySet 并加载找到的所有调查。
详细信息(右侧)调用我的EntitySet QuestionSet ,我刚刚获得它,所以它加载了所有找到的问题。我真正需要做的是只返回主要部分中选择的调查特有的问题。
这是我第一次使用Master Detail应用程序模板,而且我有点迷失了从一个与另一个EntitySet中的数据相关的EntitySet中提取数据。
尝试了不同的绑定变体,但是现在只需将它设置为从QuestionSet中读取所有内容。
明细视图:
<mvc:View xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" xmlns:l="sap.ui.layout" xmlns:f="sap.ui.layout.form"
xmlns:semantic="sap.m.semantic" xmlns:footerbar="sap.ushell.ui.footerbar" controllerName="managesurveys.controller.Detail">
<semantic:DetailPage id="page" navButtonPress="onNavBack" showNavButton="{device>/system/phone}" title="{Name}" busy="{detailView>/busy}"
busyIndicatorDelay="{detailView>/delay}">
<semantic:content>
<!-- Start of Survey Details Form -->
<f:Form editable="true" class="sapUiSmallMarginTop">
<f:formContainers>
<!-- Survey Details -->
<f:FormContainer>
<f:formElements>
<!-- Survey Name -->
<f:FormElement label="{i18n>surveyName}">
<f:fields>
<Input value="{Name}" width="100%" id="__inputSurveyName" liveChange="validateForm"/>
</f:fields>
</f:FormElement>
<!-- Description -->
<f:FormElement label="{i18n>description}">
<f:fields>
<TextArea value="{SurveyDesc}" id="__areaSurveyDescription"/>
</f:fields>
</f:FormElement>
</f:formElements>
</f:FormContainer>
<f:FormContainer>
<f:formElements>
<f:FormElement label="{i18n>reporting}">
<f:fields>
<RadioButtonGroup width="100%" selectedIndex="-1" id="__group0" columns="3">
<buttons>
<RadioButton selected="true" text="{i18n>onSubmission}" id="__reportingOnSubmission" select="onReportingSelected"/>
<RadioButton text="{i18n>onDate}" id="__reportingOnDate" select="onReportingDateSelected"/>
<RadioButton text="{i18n>none}" id="__reportingNo" select="onNoReportingSelected"/>
</buttons>
</RadioButtonGroup>
</f:fields>
</f:FormElement>
<f:FormElement label="" id="__reportDateElement" visible="false">
<f:fields>
<DatePicker id="__reportDate" placeholder="{i18n>enterDate}" change="onReportingDateChange"/>
</f:fields>
</f:FormElement>
</f:formElements>
</f:FormContainer>
</f:formContainers>
<f:layout>
<f:ResponsiveGridLayout/>
</f:layout>
</f:Form>
<!-- Categories -->
<!--<IconTabBar items="{/CategorySet}" id="iconTabBar" enableTabReordering="true" class="sapUiResponsiveContentPadding" expandable="false">-->
<!--<items>-->
<!--<IconTabFilter text="{CategoryDesc}">-->
<List id="list" mode="SingleSelectMaster" delete="handleDelete" items="{path:'/QuestionSet', templateShareable:true}"
includeItemInSelection="true" selectionChange="onQuestionSelect" headerText="{i18n>questions}">
<CustomListItem>
<l:Grid>
<Text text="ID ({SurveyId}) - Question ({QuestionDesc})"
class="sapUiTinyMarginTop sapUiLargeMarginBegin">
<layoutData>
<l:GridData span="XL9 L9 M9 S9"/>
</layoutData>
</Text>
<Button icon="sap-icon://navigation-up-arrow">
<layoutData>
<l:GridData span="XL1 L1 M1 S1"/>
</layoutData>
</Button>
<Button icon="sap-icon://navigation-down-arrow">
<layoutData>
<l:GridData span="XL1 L1 M1 S1"/>
</layoutData>
</Button>
<Button icon="sap-icon://delete" type="Reject">
<layoutData>
<l:GridData span="XL1 L1 M1 S1"/>
</layoutData>
</Button>
</l:Grid>
</CustomListItem>
</List>
<!--</IconTabFilter>-->
<!--</items>-->
<!--</IconTabBar>-->
</semantic:content>
<!-- Footer -->
<semantic:positiveAction>
<semantic:PositiveAction text="{i18n>btnSaveSurvey}" id="btnSurveySave" press="onSaveSurvey" enabled="false"/>
</semantic:positiveAction>
</semantic:DetailPage>
详情控制器:
sap.ui.define([
"managesurveys/controller/BaseController",
"sap/ui/model/json/JSONModel",
"managesurveys/model/formatter",
"sap/m/MessageBox",
"sap/m/MessageToast",
"managesurveys/libs/underscore"
],function(BaseController,JSONModel,formatter,MessageBox,MessageToast,UnderScoreJS){ “使用严格”;
return BaseController.extend("managesurveys.controller.Detail", {
formatter: formatter,
/* =========================================================== */
/* lifecycle methods */
/* =========================================================== */
onInit: function() {
// Model used to manipulate control states. The chosen values make sure,
// detail page is busy indication immediately so there is no break in
// between the busy indication for loading the view's meta data
var oViewModel = new JSONModel({
busy: false,
delay: 0,
questions: [{
order: 1,
title: "",
criteria: "",
category: "",
competency: "",
showScore: false,
addImage: false,
questionType: 0,
minimumCommentsLength: 10,
additionalQuestionTitle: "1",
additionalQuestionDesc: "2",
additionalQuestionType: 1,
numberOfAnswers: 3,
answers: [{
sequence: 1,
label: "Yes",
points: 2,
imageFlag: false,
image: "/images/happy-1.svg",
mandatoryComments: false,
additionalQuestion: false
}, {
sequence: 2,
label: "No",
points: 0,
imageFlag: false,
image: "/images/sceptic.svg",
mandatoryComments: false,
additionalQuestion: false
}, {
sequence: 3,
label: "N/A",
points: -2,
imageFlag: false,
image: "/images/angry-2.svg",
mandatoryComments: false,
additionalQuestion: false
}]
}]
});
this.getRouter().getRoute("object").attachPatternMatched(this._onObjectMatched, this);
this.setModel(oViewModel, "detailView");
this.getOwnerComponent().getModel().metadataLoaded().then(this._onMetadataLoaded.bind(this));
},
/* =========================================================== */
/* begin: internal methods */
/* =========================================================== */
/**
* Binds the view to the object path and expands the aggregated line items.
* @function
* @param {sap.ui.base.Event} oEvent pattern match event in route 'object'
* @private
*/
_onObjectMatched: function(oEvent) {
var sObjectId = oEvent.getParameter("arguments").objectId;
this.getModel().metadataLoaded().then(function() {
var sObjectPath = this.getModel().createKey("SurveySet", {
SurveyId: sObjectId
});
this._bindView("/" + sObjectPath);
}.bind(this));
},
/**
* Binds the view to the object path. Makes sure that detail view displays
* a busy indicator while data for the corresponding element binding is loaded.
* @function
* @param {string} sObjectPath path to the object to be bound to the view.
* @private
*/
_bindView: function(sObjectPath) {
// Set busy indicator during view binding
var oViewModel = this.getModel("detailView");
// If the view was not bound yet its not busy, only if the binding requests data it is set to busy again
oViewModel.setProperty("/busy", false);
this.getView().bindElement({
path: sObjectPath,
events: {
change: this._onBindingChange.bind(this),
dataRequested: function() {
oViewModel.setProperty("/busy", true);
},
dataReceived: function() {
oViewModel.setProperty("/busy", false);
}
}
});
},
_onBindingChange: function() {
var oView = this.getView(),
oElementBinding = oView.getElementBinding();
// No data for the binding
if (!oElementBinding.getBoundContext()) {
this.getRouter().getTargets().display("detailObjectNotFound");
// if object could not be found, the selection in the master list
// does not make sense anymore.
this.getOwnerComponent().oListSelector.clearMasterListSelection();
return;
}
var sPath = oElementBinding.getPath(),
oResourceBundle = this.getResourceBundle(),
oObject = oView.getModel().getObject(sPath),
sObjectId = oObject.SurveyId,
sObjectName = oObject.SurveyDesc,
oViewModel = this.getModel("detailView");
this.getOwnerComponent().oListSelector.selectAListItem(sPath);
oViewModel.setProperty("/saveAsTileTitle", oResourceBundle.getText("shareSaveTileAppTitle", [sObjectName]));
oViewModel.setProperty("/shareOnJamTitle", sObjectName);
oViewModel.setProperty("/shareSendEmailSubject",
oResourceBundle.getText("shareSendEmailObjectSubject", [sObjectId]));
oViewModel.setProperty("/shareSendEmailMessage",
oResourceBundle.getText("shareSendEmailObjectMessage", [sObjectName, sObjectId, location.href]));
},
_onMetadataLoaded: function() {
// Store original busy indicator delay for the detail view
var iOriginalViewBusyDelay = this.getView().getBusyIndicatorDelay(),
oViewModel = this.getModel("detailView");
// Make sure busy indicator is displayed immediately when
// detail view is displayed for the first time
oViewModel.setProperty("/delay", 0);
// Binding the view will set it to not busy - so the view is always busy if it is not bound
oViewModel.setProperty("/busy", true);
// Restore original busy indicator delay for the detail view
oViewModel.setProperty("/delay", iOriginalViewBusyDelay);
},
// Question radio button selected, ready for delete question
onQuestionSelect: function(oEvent) {
console.log("Question Selected");
this.getView().byId("btnSurveySave").setEnabled(true);
var oRouter = sap.ui.core.UIComponent.getRouterFor(this);
oRouter.navTo("questiondetails");
//var oContext = oEvent.getParameters().listItem.getBindingContext(this.MODEL_SITE);
//var oData = oContext.getModel().getProperty(oContext.getPath());
//this.getRouter().navTo("QuestionDetails", {QuestionId: oData.QuestionId});
},
// Delete Question Message Box
onDeleteQuestion: function() {
var that = this;
var oViewModel = this.getModel("masterView");
MessageBox.show("Are you sure you want to delete this Question?", {
icon: MessageBox.Icon.ERROR,
title: "Delete Question",
actions: [MessageBox.Action.YES, MessageBox.Action.NO],
onClose: function(oAction) {
if (oAction === MessageBox.Action.YES) {
that.getModel().remove("/SurveySet", {
SurveyId: oViewModel.SurveyId
}, {
success: function() {
MessageToast.show("Survey successfully deleted", {
duration: 2000
});
},
error: function() {
MessageToast.show("Error deleting Survey", {
duration: 2000
});
}
});
}
}
});
},
// Save Survey
onSaveSurvey: function() {
MessageToast.show("Survey successfully saved", {
duration: 2000
});
}
});
});
答案 0 :(得分:0)
您的问题非常常见,并且只与主/明细面应用程序无关。
实际上,所需的解决方案就像修复UI5代码一样简单,但在oData模型中修复了混合(因此,您必须在SEGW事务中调整SAP Gateway项目)+服务实现+ UI5代码。
您需要在实体类型之间创建关联+导航属性。
假设您的实体类型名称是Survey and Question。
这可以修复你的odata模型
您的oData调用类似于:
Get all surveys
GET /sap/opu/odata/sap/ZSV_SURVEY_SRV/SurveySet
Get a specific survey
GET /sap/opu/odata/sap/ZSV_SURVEY_SRV/SurveySet(1)
Get all questions from a survey
GET /sap/opu/odata/sap/ZSV_SURVEY_SRV/SurveySet(1)/ToQuestions
假设您已使用网关服务生成来实现项目,则必须在功能模块中添加一个必需的导入参数来获取您的问题(使用QuestionSet的GetEntitySet操作映射的参数)。此参数将表示调查的ID。您还必须使用imporing参数映射SurveyId属性。因此,您在SurveyId Propety的映射中将有两行。一行将有一个指向右侧的箭头(对于导入参数),另一行将有一个指向左侧的箭头(对于导出的内部表的调查ID列)
假设您没有使用服务生成但服务实现(当您在DPC_EXT类中手动开发ABAP代码时),您将需要使用以下方法调用来读取调查ID:
io_tech_request_context->get_converted_keys(
IMPORTING
es_key_values = ls_converted_keys ).
您还必须在UI5代码中进行小修复。
除了在列表聚合绑定中使用第二个实体集之外,就像这样......
<List items="{path:'/QuestionSet', templateShareable:true}" >
您必须使用导航属性的完全名称
<List items="{path:'/ToQuestions' }" >
或只是
<List items="{/ToQuestions}" >
我打赌你的场景中不需要templateShareable。
因此,详细信息页面将与'/ SurveySet(1)'绑定,因为您已经调用了.bindElement方法
this._bindView("/" + sObjectPath);
因此,相对于此绑定,您的问题表将被绑定到/ ToQuestions导航属性。