I have created a Master Detail template app in Web IDE. (See image)
I have an OData service (ZSV_SURVEY_SRV) with many EntitySets.
Master (left side) calls my EntitySet SurveySet and loads all Surveys found.
Detail (right side) calls my EntitySet QuestionSet and I've just currently got it so it loads all Questions found. What I really need to do is bring back only the Questions specific to the Survey selected in the Master section.
It's the first time I've used the Master Detail app template and I'm a little lost with to pull data from one EntitySet that is related to data in another EntitySet.
Tried different variations of binding, but just have it set back to read all from the QuestionSet for now.
Detail View:
<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>
Detail Controller:
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) { "use strict";
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
});
}
});
});