Developing a List-Detail Application

Objectives
After completing this lesson, you will be able to:

After completing this lesson, you will be able to:

  • Develop a List-Detail Application

Implement a List-detail Application

Business Example

In this exercise, you learn how to implement a List-detail Application with SAPUI5. You also learn how to handle the processing of asynchronous service requests in the UI using JavaScript promises.

Prerequisites

Before you start this exercise, you should have downloaded the prepared files from Github: https://github.com/SAP-samples/sapui5-development-advanced-learning-journey. You should have already uploaded the metadata.xml file to a new /home/user/projects/Training files/ folder as shown in the previous exercise: Create a SAPUI5 Application Project.

Note

SAP Business Technology Platform and its tools are cloud services. Due to the nature of cloud software, step procedures and naming of fields and buttons may differ from the exercise solution.

Note
In the following exercises, replace <L> with the course group and ## with your user group.
Caution
Be aware that in some cases the initial code or code of previous implementation steps is not shown. In the following steps, do not delete any existing code or code that you have added in previous steps if not explicitly asked. Furthermore, be aware that the screenshots show the solutions from student00 namespace you should then replace with your own student## one.

Task 1: Create the Basic Components of the List-Detail Application

Steps

  1. Log into your training SAP Business Application Studio.

    1. Perform this step as shown in a previous exercise.

  2. Create a new SAP Fiori Freestyle project using the following information:

    Select Template and Target Location

    FieldValue
    TemplateSAP Fiori application

    Template Selection

    FieldValue
    Template TypeSAP Fiori
    TemplateBasic

    Datasource and Service Selection

    FieldValue
    Data sourceUpload a Metadata Document
    Metadata file path/home/user/projects/Training files/metadata.xml

    View name

    FieldValue
    View nameList

    Project Attributes

    FieldValue
    Module nameux402_listdetail
    Application titleList-detail App
    Application namespacestudent##.com.sap.training.ux402.listdetail
    DescriptionA simple list-detail app.
    Project folder path/home/user/projects
    Minimum SAPUI5 versionLet it on the default value
    1. Choose FileNew Project from Template in the SAP Business Application Studio.

    2. Choose SAP Fiori application and select Start

    3. Choose SAP Fiorias Template Type.

    4. Choose Basic and select Next.

    5. Choose the Data Source and Service Selection following Options:

      • As Data source, select Upload a Metadata Document.
      • As Metadata file path, enter /home/user/projects/Training files/metadata.xml or select the file in the search field and choose OK.

      Choose Next to continue.

    6. In the View name field, enter List, and then choose Next.

    7. For the Project Attributes, enter the following values:

      • As Module name, enter ux402_listdetail.
      • As Application title, enter List-detail Application.
      • As Application namespace, enter student##.com.sap.training.ux402.listdetail.
      • As Description, enter List-detail Application.
      • As Project folder path, enter /home/user/projects.
      • Do not change anything for Minimum SAPUI5 version.
      • Let all radio buttons selected on No.
    8. Select Finish.

  3. Upload the JSON files for mock data into a new webapp/localService/data folder.

    1. In the context menu of the webapp/localService folder, select New folder....

    2. A new line appears where you can enter the new folder name: data. Press Enter to confirm.

    3. In the context menu of the webapp/localService/data folder, select Upload....

    4. Select the three mockdata JSON files and click Open.

  4. Add the listed views of type XML in the view folder and the corresponding controllers of type JavaScript in the controller folder of your project. Use Copy and then Paste in the context menu of the List.view.xml and the List.controller.js files to create the files. Rename them to the listed view and controller file names. In the created view files, change the controllerName attribute. In the created controller files, change the controller name in the extend function.

    View and Controller file Names

    View file nameController File Name
    Detail.view.xmlDetail.controller.js
    DetailObjectNotFound.view.xmlDetailObjectNotFound.controller.js
    NotFound.view.xmlNotFound.controller.js
    1. Select the List.view.xml file in the view folder of your project and from the context menu, choose Copy.

    2. Select the view folder of your project and from the context menu, choose Paste.

    3. Select the created List.view_copy.xml file and from the context menu, choose Rename. Replace List.view_copy.xml with Detail.view.xml and press the Enter key (on your keyboard).

    4. In the opened Detail.view.xml file, edit the controllerNameattribute and replace List with Detail. Save your changes.

    5. Select the List.controller.js file in the controller folder of your project and from the context menu, choose Copy.

    6. Select the controller folder of your project, and from the context menu, choose Paste.

    7. Select the created List.controller_copy.js file and from the context menu, choose Rename. Replace List.controller_copy.xml with Detail.controller.js and press the Enter key (on your keyboard).

    8. In the opened Detail.controller.js file, change the controller name in the extend function by replacing List with Detail. Save your changes.

    9. Repeat steps a to f and create the DetailObjectNotFound and NotFound views with the corresponding controllers.

  5. Open the Application main view and add an sap.f.FlexibleColumnLayout element inside the App element with the listed properties and values:

    AttributeValue
    idlayout
    layout{mainView>/layout}
    backgroundDesignTranslucent
    1. Open the App.view.xml file in the view folder of your project.

    2. Add the sap.f namespace with the prefix f to the App.view.xml file. Add following code after xmlns="sap.m":

      Code snippet
      xmlns:f="sap.f"
      Expand
    3. Add an sap.f.FlexibleColumnLayout element inside the App element with the given attribute-value pairs :

      Code snippet
      <f:FlexibleColumnLayout
       id="layout"
       layout="{mainView>/layout}"
       backgroundDesign="Translucent">
      </f:FlexibleColumnLayout>
      
      Expand
    4. Save your changes. Your code should now look like the following:

      Code snippet
      
      <mvc:View controllerName="student00.com.sap.training.ux402.listdetail.ux402listdetail.controller.App"
       xmlns:html="http://www.w3.org/1999/xhtml"
       xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
       xmlns="sap.m" 
       xmlns:f="sap.f">
       <App id="app">
       <f:FlexibleColumnLayout
       id="layout"
       layout="{mainView>/layout}"
       backgroundDesign="Translucent">
       </f:FlexibleColumnLayout>
       </App>
      </mvc:View>
      
      
      Expand

Task 2: Prepare the Internationalized Texts for the Application

Steps

  1. Change the listed key-value pairs in the i18n.properties file of your project.

    Title and Description key-value pairs within the i18n.properties File

    KeyValue
    titleExercise: List-Detail App
    appTitleExercise: List-Detail App
    appDescriptionA simple list-detail app
    1. Perform this step as shown in a previous exercise.

  2. For the List View, add the listed key-value pair to the i18n.properties file of your project.

    List View Key-value Pair within the i18n.properties File

    KeyValue
    listTitleCarriers
    1. Perform this step as shown in a previous exercise.

  3. For the Detail View, add the listed key-value pairs to the i18n.properties file of your project.

    Detail View key-value pairs within the i18n.properties File

    KeyValue
    detailTitleCarrier
    currencyTitleCurrency
    urlTitleUrl
    tableHeaderTextConnections
    tableNoDataTextNo connections available
    idColumnTextNumber
    cityFromColumnTextFrom
    cityToColumnTextTo
    1. Perform this step as shown in a previous exercise.

  4. For the NotFound View, add the listed key-value pairs to the i18n.properties file of your project.

    NotFound View Key-value Pairs within the i18n.properties File

    KeyValue
    notFoundTitleNot Found
    notFoundTextThe requested resource was not found
    1. Perform this step as shown in a previous exercise.

  5. For the DetailObjectNotFound View, add the listed key-value pair to the i18n.properties file of your project.

    DetailObjectNotFound View Key-value Pairs within the i18n.properties File

    KeyValue
    noObjectFoundTextThis Carrier is not available
    1. Perform this step as shown in a previous exercise.

Task 3: Check and Configure the Routing

Steps

  1. Check the generated routing configuration and make sure that the following routing configuration parameters are configured correctly. Additionally, remove the clearControlAggregation configuration parameter.

    Routing Configuration

    Configuration ParameterValues
    routerClasssap.m.routing.Router
    viewTypeXML
    asynctrue
    viewPathstudent##.com.sap.train­ ing.ux402.listdetail. ux402listdetail.view
    controlAggregationbeginColumnPages
    controlIdlayout
    1. Open the manifest.json file.

    2. Locate the routing configuration config.

    3. Make sure that the listed configuration parameters are included.

    4. Remove the clearControlAggregation configuration parameter.

    5. Your implementation should look like the following:

      Code snippet
      
      "config": {
       "routerClass": "sap.m.routing.Router",
       "viewType": "XML",
       "async": true,
       "viewPath": "student00.com.sap.training.ux402.listdetail.ux402listdetail.view",
       "controlAggregation": "beginColumnPages",
       "controlId": "layout"
      },
      
      
      Expand
  2. Check the TargetList target against the listed attributes and values and rename it to masterlist.

    List Target

    AttributeValue
    viewTypeXML
    transitionslide
    viewNameList
    viewLevel1
    viewIdlist
    controlAggregationbeginColumnPages
    1. Find the Targetlist target and check to ensure that it has all the listed attributes and values and rename it to masterlist.

    2. Your implementation should look like the following code:

      Code snippet
      
      "targets": {
       "masterlist": {
       "viewType": "XML",
       "transition": "slide",
       "ControlAggregation": "beginColumnPages",
       "viewId": "list",
       "viewName": "List",
       "viewLevel": 1
       }
      
      
      Expand
  3. Define a target with the name carrierdetails with the listed attributes and values.

    carrierdetails Target

    Configuration ParameterValue
    viewTypeXML
    transitionslide
    viewNameDetail
    viewLevel2
    viewIdcarrierdetails
    controlAggregationmidColumnPages
    1. Add the carrierdetails target with the listed attributes and values:

      Code snippet
      
      "carrierdetails": {
       "viewType": "XML",
       "transition": "slide",
       "viewLevel": 2,
       "controlAggregation": "midColumnPages",
       "viewName": "Detail",
       "viewId": "carrierdetails"
      }
      Expand
  4. Delete the RouteList route. Then define a default route with the name masterlist. Assign the route to the masterlist and carrierdetails targets. In addition, set greedy to falseand set the titleTarget to an empty string.

    1. Replace the RouteList definition with the following code:

      Code snippet
      
      "routes": [
       {
       "name": "masterlist",
       "pattern": "",
       "titleTarget": "",
       "greedy": false,
       "target": [
       "masterlist"
       ] 
       }
      ]
      Expand
  5. Create a new route with the name carrierdetails and assign the masterlist and carrierdetails targets to the route. Add the pattern Carriers/{objectId} to that route. In addition, set greedy to false and set the titleTarget to an empty string.

    1. Add the following code after the masterlist route definition:

      Code snippet
      ,
      {
       "name": "carrierdetails",
       "pattern": "Carriers/{objectId}",
       "titleTarget": "",
       "greedy": false,
       "target": [
       "masterlist",
       "carrierdetails"
       ]
      }
      Expand
  6. Define a target with the name notFoundwith the listed attributes and values.

    notFound target

    Configuration ParameterValue
    viewTypeXML
    transitionslide
    controlAggregationmidColumnPages
    clearControlAggregationtrue
    viewNameNotFound
    viewIdnotFound
    1. Add the notFound target with the listed attributes and values after the carrierdetails target definition:

      Code snippet
      
      ,
      "notFound": {
       "viewType": "XML",
       "transition":"slide",
       "controlAggregation": "midColumnPages",
       "clearControlAggregation": true,
       "viewName": "NotFound",
       "viewId": "notFound"
      }
      Expand
  7. Define a target with the name detailObjectNotFound with the listed attributes and values.

    detailObjectNotFound target

    Configuration ParameterValue
    viewTypeXML
    transitionslide
    controlAggregationmidColumnPages
    clearAggregationtrue
    viewNameDetailObjectNotFound
    viewIddetailObjectNotFound
    1. Add the detailObjectNotFound target with the listed attributes and values after the notFound target definition:

      Code snippet
      
      ,
      "detailObjectNotFound": {
       "viewType": "XML",
       "transition":"slide",
       "controlAggregation": "midColumnPages",
       "clearControlAggregation": true,
       "viewName": "DetailObjectNotFound",
       "viewId": "detailObjectNotFound"
      }
      Expand
  8. Extend the routing configuration in the descriptor by adding a bypassed property and setting its target to masterlist and notFound. Then save your changes.

    1. Add the bypassed property and set its target to list and notFound in the routing configuration by adding the following code at the end of the config definition:

      Code snippet
      
      "bypassed": {
       "target": [
       "masterlist",
       "notFound"
       ]
      }
      Expand
    2. Your implementation should look like the following code:

      Code snippet
      
      "routing": {
       "config": {
       "routerClass": "sap.m.routing.Router",
       "viewType": "XML",
       "async": true,
       "viewPath": "student00.com.sap.training.ux402.listdetail.ux402listdetail.view",
       "controlAggregation": "beginColumnPages",
       "controlId": "layout",
       "bypassed": {
       "target": [
       "masterlist",
       "notFound"
       ]
       }
       },
      
      
      Expand
    3. Save your changes.

  9. Check if the init function of your component contains the router initialization. If this is not the case, initialize the router of your application.

    1. Open the Component.js file.

    2. Navigate to the init function.

    3. Ensure that the router is initialized:

      Code snippet
      
      init: function () {
       // call the base component's init function
       UIComponent.prototype.init.apply(this, arguments);
      
       // enable routing
       this.getRouter().initialize();
      
       // set the device model
       this.setModel(models.createDeviceModel(), "device");
      }
      
      
      Expand

Task 4: Define the List View Content

In this task, you define the list view content for your application. The view will implement a sap.f.DynamicPage object. The page will show a list of Carriers fetched from the entity set UX_C_Carrier_TP.

Steps

  1. Open the List.view.xml file, remove the page element and add the namespace sap.f with the XML-alias f.

    1. Update the code as follows:

      Code snippet
      
      <mvc:View controllerName="student##.com.sap.training.ux402.listdetail.ux402listdetail.controller.List"
       xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
       xmlns="sap.m"
       xmlns:f="sap.f">
      Expand
  2. Add an sap.f.DynamicPage element with sap.f.content-aggregation to the view with the following properties.

    PropertyValue
    iddynamicPageOverviewId
    headerExpandedtrue
    toggleHeaderOnTitleClicktrue
    1. Add the following code inside the View definition:

      Code snippet
      <f:DynamicPage id="dynamicPageOverviewId" headerExpanded="true" toggleHeaderOnTitleClick="true">
       <f:content>
      
       </f:content> 
      </f:DynamicPage> 
      
      Expand
  3. Create an sap.m.List control inside the sap.f.content-Aggregation and assign the following values to the List element:

    Attribute-value Pairs for the sap.m.List Control 

    AttributeValue
    idlist
    items{/UX_C_Carrier_TP}
    busyIndicatorDelay0
    growingtrue
    growingThreshold20
    growingScrollToLoadtrue
    mode{= ${device>/system/phone} ? 'None': 'SingleSelectMaster'}
    selectionChangeonSelect  
    1. Add the following code inside the content aggregation:

      Code snippet
      <List id="list"
       items="{/UX_C_Carrier_TP}"
       busyIndicatorDelay="0"
       growing="true"
       growingThreshold="10"
       growingScrollToLoad="true"
       mode="{= ${device>/system/phone} ? 'None' : 'SingleSelectMaster'}"
       selectionChange="onSelect">
      
      </List>
      Expand
  4. Add the items aggregation to the sap.m.List-control and add a control type of sap.m.ObjectListItem to the items-aggregation with the following attributes:

    Attribute-value Pairs for the sap.m.ObjectListItem Element

    AttributeValue
    idobjectListItem
    title{Carrname}
    intro{Carrid}
    type{= ${device>/system/phone} ? 'Active' : 'Inactive'}
    pressonSelect
    1. Extend your code with the following implementation inside the List element:

      Code snippet
      <items>
       <ObjectListItem
       id="objectListItem"
       title="{Carrname}"
       intro="{Carrid}"
       type="{= ${device>/system/phone} ? 'Active' : 'Inactive'}"
       press="onSelect">
       </ObjectListItem>
      </items>
      Expand
    2. Your implementation should now look like the following:

      Code snippet
      
      <mvc:View controllerName="student00.com.sap.training.ux402.listdetail.ux402listdetail.controller.List"
       xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
       xmlns="sap.m"
       xmlns:f="sap.f">
       <f:DynamicPage id="dynamicPageOverviewId" headerExpanded="true" toggleHeaderOnTitleClick="true">
       <f:content> 
       <List id="list"
       items="{/UX_C_Carrier_TP}"
       busyIndicatorDelay="0"
       growing="true"
       growingThreshold="10"
       growingScrollToLoad="true"
       mode="{= ${device>/system/phone} ? 'None' : 'SingleSelectMaster'}"
       selectionChange="onSelect">
       <items>
       <ObjectListItem id="objectListItem"
       title="{Carrname}"
       intro="{Carrid}"
       type="{= ${device>/system/phone} ? 'Active' : 'Inactive'}"
       press="onSelect">
       </ObjectListItem>
       </items>
       </List> 
       </f:content>
       </f:DynamicPage>
      </mvc:View>
      
      
      Expand

Task 5: Define the Detail View content

In this task, you define the detail view content for your application. The view should show the details of the selected carrier using the sap.uxap.ObjectPageDynamicHeaderTitle element and sap.uxap.headerContent aggregation. The table that will be implemented shows the available flights for the selected carrier. 

Steps

  1. Open the Detail.view.xml file, remove the page element and add the namespaces sap.uxap with the XML-alias ux and sap.ui.layout with the XML-alias layout.

    1. Update the code as follows:

      Code snippet
      
      <mvc:View controllerName="student##.com.sap.training.ux402.listdetail.ux402listdetail.controller.Detail"
       xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
       xmlns="sap.m"
       xmlns:ux="sap.uxap"
       xmlns:layout="sap.ui.layout">
      Expand
  2. Add an sap.uxap.ObjectPageLayout element to the view. Add the attribute id with the value objectPageLayout.

    1. Add the following code inside the View definition:

      Code snippet
      <ux:ObjectPageLayout id="objectPageLayout">
      
      </ux:ObjectPageLayout>
      Expand
  3. Add an sap.uxap.headerTitle, sap.uxap.headerContent and sap.uxap.sections aggregation to the sap.uxap.ObjectPageLayout control.

    1. Add the following code inside the ObjectPageLayout element:

      Code snippet
      <ux:headerTitle>
      
      </ux:headerTitle>
      <ux:headerContent>
      
      </ux:headerContent>
      <ux:sections>
      
      </ux:sections>
      Expand
  4. Add an sap.uxap.ObjectPageDynamicHeaderTitle control to the headerTitle aggregation. For the attribute id, add the value objectPageDynamicHeader.

    1. Add the following code inside the headerTitle element:

      Code snippet
      <ux:ObjectPageDynamicHeaderTitle id="objectPageDynamicHeader">
      
      </ux:ObjectPageDynamicHeaderTitle>
      Expand
  5. Add an sap.uxap.expandedHeading aggregation to the sap.uxap.ObjectPageDynamicHeaderTitle control.

    1. Add the following code inside the ObjectPageDynamicHeaderTitle element:

      Code snippet
      <ux:expandedHeading>
      
      </ux:expandedHeading>
      Expand
  6. Add an sap.m.Title control to the sap.uxap.expandedHeading aggregation with the listed attributes and values.

    Attribute-value Pairs for the sap.m.ObjectAttribute Elements

    AttributeValue
    idtitle1
    text{Carrname}
    levelH2
    1. Add the following code inside the expandedHeading element:

      Code snippet
      <Title
      id="title1"
      text="{Carrname}"
      level="H2"/>
      Expand
    2. The headerTitlesection should now look like the following:

      Code snippet
      
      <ux:headerTitle>
       <ux:ObjectPageDynamicHeaderTitle id="objectPageDynamicHeader">
       <ux:expandedHeading>
       <Title id="title1"
       text="{Carrname}"
       level="H2"/>
       </ux:expandedHeading>
       </ux:ObjectPageDynamicHeaderTitle>
      </ux:headerTitle>
      
      
      Expand
  7. Add an sap.m.FlexBox control to the sap.uxap.headerContent with the listed attributes and values.

    AttributeValue
    idflexbox
    warpWrap
    1. Add the following code inside the headerContent element: 

      Code snippet
      <FlexBox wrap="Wrap" id="flexBox">
      
      </FlexBox>
      Expand
  8. Add an sap.f.Avatar and a sap.ui.layout.VerticalLayout control to the sap.m.Flex control with the listed attributes and values.

    ElementAttributeValue
    1idavatar
    srcsap-icon://flight
    2idverticallayout1
    classsapUiSmallMarginBeginEnd
    1. Add the following code inside the FlexBox element:

      Code snippet
      <Avatar id="avatar" src="sap-icon://flight"/>
      <layout:VerticalLayout id="verticalLayout1" class="sapUiSmallMarginBeginEnd">
      
      </layout:VerticalLayout>
      Expand
  9. Add two sap.m.Label controls to the sap.ui.layout.VerticalLayout control with the listed attributes and values:

    ElementAttributeValue
    1idlabel1
    text{Carrid}
    2idlabel2
    text{Url}
    1. Add the following code into the VerticalLayout element:

      Code snippet
      <Label id="label1" text="{Carrid}"/>
      <Label id="label2" text="{Url}"/>
      Expand
    2. Your code for the headerContent should now look like the following:

      Code snippet
      
      <ux:headerContent>
       <FlexBox wrap="Wrap" id="flexBox">
       <Avatar id="avatar" src="sap-icon://flight"/>
       <layout:VerticalLayout id="verticalLayout1" class="sapUiSmallMarginBeginEnd">
       <Label id="label1" text="{Carrid}"/>
       <Label id="label2" text="{Url}"/> 
       </layout:VerticalLayout> 
       </FlexBox>
      </ux:headerContent>
      
      
      Expand
  10. Add an sap.uxap.ObjectPageSection control to thesap.uxap.sections aggregation. Add the value objectPageSection to the attribute id.

    1. Add the following code inside the sections element:

      Code snippet
      <ux:ObjectPageSection id="objectPageSection">
      
      </ux:ObjectPageSection>
      Expand
  11. Add an sap.uxap.ObjectPageSubSection control to the sap.uxap.ObjectPageSection control. Add the value objectPageSubSection to the attribute id.

    1. Add the following code inside the ObjectPageSection element:

      Code snippet
      <ux:ObjectPageSubSection id="objectPageSubSection1">
      
      </ux:ObjectPageSubSection>
      Expand
  12. Add ansap.m.Table control to the sap.uxap.ObjectPageSubSection aggregation and add the following attributes to the Table control:

    Attribute-value Pairs for the sap.m.Table Element 

    AttributeValue
    idtable
    headerText{i18n>tableHeaderText}
    items{to_Connection}
    noDataText{i18n>talbeNoDataText
    1. Add the following code inside the ObjectPageSubSection element:

      Code snippet
      <Table id="table" headerText="{i18n>tableHeaderText}" items="{to_Connection}" 
       noDataText="{i18n>tableNoDataText}">
      
      </Table>
      Expand
  13. Add the columns aggregation to the Table control and add three sap.m.Column controls to the columns aggregation. For each control, assign the following attributes:

    ElementAttributeValues
    1idcolumn1
    2idcolumn2
    3idcolumn3
    1. Add the following code inside the Table element:

      Code snippet
      <columns>
       <Column id="column1">
       </Column>
       <Column id="column2">
       </Column>
       <Column id="column3">
       </Column>
       </columns>
      Expand
  14. To each of the sap.m.Columns controls, add one sap.m.Text control and assign the following attributes:

    Attribute-value Pairs for the sap.m.Text Elements

    ElementAttributeValue
    1idtext1
    text{i18n>idColumnText}
    2idtext2
    text{i18n>cityFromColumnText}
    3idtext3
    text{i18n>cityToColumnText}
    1. Add the following code into the first column definition:

      Code snippet
      <Text id="text1" text="{i18n>idColumnText}"/>
      Expand
    2. Add the following code into the second column definition:

      Code snippet
      <Text id="text2" text="{i18n>cityFromColumnText}"/>
      Expand
    3. Add the following code into the third column definition:

      Code snippet
      <Text id="text3" text="{i18n>cityToColumnText}"/>
      Expand
  15. Add the items aggregation to the Table element. 

    1. Add the following code after the columns aggregation:

      Code snippet
      <items>
      
      </items>
      Expand
  16. Add an sap.m.ColumnListItem control to the items aggregation and add the value columnListItem to the attribute id.

    1. Add the following code inside the items aggregation:

      Code snippet
      <ColumnListItem id="columnListItem">
      
      </ColumnListItem>
      Expand
  17. Add the cells aggregation to the ColumnListItem control. 

    1. Add the following code into the ColumnListItem element:

      Code snippet
      <cells>
      
      </cells>
      Expand
  18. Add three sap.m.Text controls to the cells aggregation and add the following attributes and values to the Text elements.

    ElementAttributeValue
    1idtext4
    text{Carrid}
    2idtext5
    text{Cityfrom}
    3idtext6
    text{Cityto}
    1. Add the following code into the cells aggregation:

      Code snippet
      <Text id="text4" text="{Carrid}"/>
      <Text id="text5" text="{Cityfrom}"/>
      <Text id="text6" text="{Cityto}"/>
      
      Expand
    2. Your code for the sections aggregation should now look like the following:

      Code snippet
      
      <ux:sections>
       <ux:ObjectPageSection id="objectPageSection">
       <ux:ObjectPageSubSection id="objectPageSubSection1">
       <Table id="table" headerText="{i18n>tableHeaderText}" items="{to_Connection}" 
       noDataText="{i18n>tableNoDataText}">
       <columns>
       <Column id="column1">
       <Text id="text1" text="{i18n>idColumnText}"/>
       </Column>
       <Column id="column2">
       <Text id="text2" text="{i18n>cityFromColumnText}"/>
       </Column>
       <Column id="column3">
       <Text id="text3" text="{i18n>cityToColumnText}"/>
       </Column>
       </columns>
       <items>
       <ColumnListItem id="columnListItem">
       <cells>
       <Text id="text4" text="{Carrid}"/>
       <Text id="text5" text="{Cityfrom}"/>
       <Text id="text6" text="{Cityto}"/>
       </cells>
       </ColumnListItem>
       </items>
       </Table> 
       </ux:ObjectPageSubSection>
       </ux:ObjectPageSection> 
      </ux:sections>
      
      
      Expand

Task 6: Define the NotFound View content

The NotFound view is displayed when the requested target was not found. Define the NotFound view content as a MessagePage.

Steps

  1. Open the NotFound.view.xml file and remove the pageelement.

    1. Perform this step as shown in a previous exercise.

  2. Add an sap.m.MessagePage element to the view and add the following attributes to the element:

    Attribute-value Pairs for the sap.m.MessagePage Element

    AttributeValue
    idnotFoundMessagePage
    title{i18n>notFoundTitle}
    text{i18n>notFoundText}
    iconsap-icon://document
    1. Update the code as follows:

      Code snippet
      
      <mvc:View controllerName="student##.com.sap.training.ux402.listdetail.ux402listdetail.controller.NotFound"
       xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
       xmlns="sap.m">
       <MessagePage
       id="notFoundMessagePage"
       title="{i18n>notFoundTitle}"
       text="{i18n>notFoundText}"
       icon="sap-icon://document"
       >
       </MessagePage>
      </mvc:View>
      Expand

Task 7: Define the DetailObjectNotFound View content

The DetailObjectNotFound view is displayed when the detail was not found. Define the DetailObjectNotFound view content as a MessagePage.

Steps

  1. Open the DetailObjectNotFound.view.xml file and remove the page element.

    1. Perform this step as shown in a previous exercise.

  2. Add an sap.m.MessagePage element to the view and add the following attributes to the element:

    Attribute-value Pairs for the sap.m.MessagePage Element

    AttributeValue
    iddetailObjectNotFoundPage
    title{i18n>detailTitle}
    text{i18n>noObjectFoundText}
    iconsap-icon://document
    1. Update the code as follows:

      Code snippet
      
      <mvc:View controllerName="student##.com.sap.training.ux402.listdetail.ux402listdetail.controller.DetailObjectNotFound"
      xmlns:mvc="sap.ui.core.mvc" displayBlock="true"
      xmlns="sap.m">
       <MessagePage
       id="detailObjectNotFoundPage"
       title="{i18n>detailTitle}"
       text="{i18n>noObjectFoundText}"
       icon="sap-icon://document"
       >
       </MessagePage>
      </mvc:View>
      Expand

Task 8: Provide View Synchronization Functionality

In this task, you implement view synchronization by creating a class with the name ListSelector. The ListSelector implementation should be placed in the controller folder of your project.

Steps

  1. In the controller folder, create a file with name ListSelector.js.

    1. Open the context menu of the controller folder and choose New File.

    2. Enter ListSelector.js and choose OK.

  2. Derive the ListSelector from sap.ui.base.Object.

    1. Update the code as follows:

      Code snippet
      
      sap.ui.define([
       "sap/ui/base/Object"
      ], function(BaseObject) {
      "use strict";
      
      return BaseObject.extend("student##.com.sap.training.ux402.listdetail.ux402listdetail.controller.ListSelector", 
       {
      
       });
      });
      
      Expand
  3. Add a constructor function.

    1. Add a constructor function as follows:

      Code snippet
      constructor : function () {
      }
      Expand
  4. Inside the constructor function, create a Promise instance and assign the instance to a member variable called _oWhenListHasBeenSet. Pass an anonymous function to the Promise-constructor. The function should take an argument with the name fnResolveListHasBeenSet. Assign this argument to a public variable _fnResolveListHasBeenSet of function object. Make sure that the reference of the anonymous function points to the right object.

    1. Add the_oWhenListHasBeenSet variable as follows:

      Code snippet
      
      this._oWhenListHasBeenSet = new Promise(function (fnResolveListHasBeenSet) {
       this._fnResolveListHasBeenSet = fnResolveListHasBeenSet;
      }.bind(this));
      Expand
  5. Create another Promise instance and assign the object to a member variable with the name oWhenListLoadingIsDone. The constructor function of the Promise should take an anonymous function with two arguments. Name the first argument fnResolve and the second fnReject. Make sure that the reference inside the promise points to the correct object.

    1. Add the oWhenListLoadingIsDonevariable as follows:

      Code snippet
      this.oWhenListLoadingIsDone = new Promise(function (fnResolve, fnReject) {
      
      }.bind(this));
      Expand
  6. Implement the anonymous function created in previous step in a way that when the _oWhenListHasBeenSet promise is resolved, an anonymous function with an oList parameter is called.

    1. Add the following code inside the Promise function of oWhenListLoadingIsDone:

      Code snippet
      
      this._oWhenListHasBeenSet
       .then(function (oList) {
      
       });
      Expand
  7. The oList object will contain a reference at runtime to a list of the List view. Register an event handler for the dataReceived event for the items binding. The event handler registered for that event should check whether data are received from the backend during binding or not. When no data received from a backend call the fnReject function point should be invoked. Pass a literal javascript object with a property list and a property error. Assign the oList list object to the list property and a true value to the error property.

    1. Add the following code after .then(function (oList) {:

      Code snippet
      
      oList.getBinding("items").attachEventOnce("dataReceived",
       function (oData) {
       if (!oData.getParameter("data")) {
       fnReject({
       list : oList,
       error: true
       });
       }
       }
      Expand
  8. Continue with the implementation and enhance the event handler for the dataReceived event. Check whether the list referenced by the oList object contains at least one item. If this is the case, call the function point, fnResolve. Pass a literal javascript object with a property list and a property firstListitem. Assign the oList list object to the list property and the first item of the list to the firstListitemproperty. If there is not at least one item in the list, call fnReject. Pass a literal javascript object with a property list and a property error. Assign the oList list object to the list property and a false value to the error property.

    1. Add the following code after the if statement closing bracket :

      Code snippet
      
      var oFirstListItem = oList.getItems()[0];
       if(oFirstListItem) {
       fnResolve({
       list: oList,
       oFirstListItem: oFirstListItem
       })
       }
       else {
       // No items in the list
       fnReject({
       list : oList,
       error: false
       });
       }
      Expand
    2. The complete implementation of the constructor function should look like the code in the following figure:

      Code snippet
      
      constructor : function () {
      
       this._oWhenListHasBeenSet = new Promise(function (fnResolveListHasBeenSet) {
       this._fnResolveListHasBeenSet = fnResolveListHasBeenSet;
       }.bind(this));
      
       this.oWhenListLoadingIsDone = new Promise(function (fnResolve, fnReject) {
       this._oWhenListHasBeenSet
       .then(function (oList) {
       oList.getBinding("items").attachEventOnce("dataReceived",
       function (oData) {
       if (!oData.getParameter("data")) {
       fnReject({
       list : oList,
       error: true
       });
       }
       var oFirstListItem = oList.getItems()[0];
       if(oFirstListItem) {
       fnResolve({
       list: oList,
       oFirstListItem: oFirstListItem
      
       })
       }
       else {
       // No items in the list
       fnReject({
       list : oList,
       error: false
       });
       }
       }
       );
       });
       }.bind(this));
      }
      Expand
  9. Implement a setBoundMasterList function. The function should take one argument named oList. Assign the oList parameter to a member variable named _oList and call the function pointer stored in _fnResolveListHasBeenSet. Pass the oList object to that function pointer.

    1. Add the following code after the constructor function:

      Code snippet
      
      setBoundMasterList: function(oList) {
       this._oList = oList;
       this._fnResolveListHasBeenSet(oList);
      },
      Expand
  10. Implement a function named selectAListItem. The function should take one argument named sBindingPath. The sBindingPath argument will contain the path to an item of the master list. The function should react when the oWhenListLoadingIsDone promise is resolved and should check whether the sBindingPath variable points to the same entity of the list that was already selected. If this, or the mode of the list, equals None, the processing should return to the caller. Otherwise, the item of the list should be selected.

    1. Implement the code for selectAListItem after the setBoundMasterListfunction, as follows:

      Code snippet
      
      selectAListItem : function (sBindingPath) {
       this.oWhenListLoadingIsDone.then(
       function () {
       var oList = this._oList,
       oSelectedItem;
      
       if (oList.getMode() === "None") {
       return;
       }
      
       oSelectedItem = oList.getSelectedItem();
      
       // skip update if the current selection is already matching the object path
       if (oSelectedItem && oSelectedItem.getBindingContext().getPath() === sBindingPath) {
       return;
       }
      
       oList.getItems().some(function (oItem) {
       if (oItem.getBindingContext() && oItem.getBindingContext().getPath() === sBindingPath) {
       oList.setSelectedItem(oItem);
       return true;
       }
       });
       }.bind(this));
      },
      Expand
  11. Implement a clearListSelection function. This function should reset the statuses selected at all items of the list. The deselection should be done when the promise stored in _oWhenListHasBeenSet is resolved. Then save your changes.

    1. Add the clearListSelectionfunction as follows:

      Code snippet
      
      clearMasterListSelection: function() {
       this._oWhenListHasBeenSet.then(function() {
       this._oList.removeSelections(true);
       }.bind(this));
      }
      Expand
    2. Save your changes.

  12. To make sure that the ListSelector can be referenced from each part of the application, we must create an instance of the ListSelector at a more global level. Therefore, create an instance of the ListSelector inside the Component.js file and make sure that the instance is a member variable of the component. Then save your changes.

    1. Open the Component.js file located in the controller folder of your project.

    2. Add a reference to the ListSelector in the Component.js file as follows:

      Code snippet
      
      sap.ui.define([
       "sap/ui/core/UIComponent",
       "sap/ui/Device",
       "student##/com/sap/training/ux402/listdetail/ux402listdetail/model/models",
       "student##/com/sap/training/ux402/listdetail/ux402listdetail/controller/ListSelector"
      ],
      function (UIComponent, Device, models, ListSelector) {
      Expand
    3. In the init function of the Component, declare a member variable named oListSelector and assign an ListSelector instance to the variable. Then save your changes.

      Code snippet
      
      // instantiation of the listselector
      this.oListSelector = new ListSelector();
      Expand
    4. You init function should now look like this:

      Code snippet
      
      init: function () {
       // call the base component's init function
       UIComponent.prototype.init.apply(this, arguments);
      
       // instantiation of the listselector
       this.oListSelector = new ListSelector();
      
       // enable routing
       this.getRouter().initialize();
      
       // set the device model
       this.setModel(models.createDeviceModel(), "device");
      }
      
      
      Expand

Task 9: Implement the Base Controller

In this task, you implement a base controller class .This class provides to all controllers some functionalities for reuse (for routing, view synchronization, and backward navigation).

Steps

  1. Add a file named BaseController.js to the controller folder of your project.

    1. Open the context menu of the controller folder and choose New File.

    2. Enter BaseController.js and choose OK.

  2. Derive a new class from sap.ui.core.mvc.Controller.

    1. Add the following code :

      Code snippet
      
      sap.ui.define([
       "sap/ui/core/mvc/Controller"
      ], function (Controller) {
       "use strict";
       return Controller.extend("student##.com.sap.training.ux402.listdetail.ux402listdetail.controller.BaseController", {
      
       });
      });
      Expand
  3. Add a reference to sap.ui.core.routing.History and pass the reference as History to the callback.

    1. Update the code as follows:

      Code snippet
      
      sap.ui.define([
       "sap/ui/core/mvc/Controller",
       "sap/ui/core/routing/History"
      ], function (Controller, History) {
      
      Expand
  4. Implement a function named getRouter and return the router of the component.

    1. Add the following code inside the extend function :

      Code snippet
      
      getRouter: function () {
       return this.getOwnerComponent().getRouter();
      },
      Expand
  5. Implement a getListSelector function and return the reference of the oListSelector object from the component.

    1. Add the following code after the getRouterimplementation:

      Code snippet
      
      getListSelector: function() {
       return this.getOwnerComponent().oListSelector;
      },
      Expand
  6. Implement a getResourceBundle function and return a reference to the i18n model of the application.

    1. Add the following code after the getListSelector implementation :

      Code snippet
      
      getResourceBundle: function () {
       return this.getOwnerComponent().getModel("i18n").getResourceBundle();
      },
      Expand
  7. Implement a function named onNavBack. The function should navigate back to the master list using the masterlist route. Then save your changes.

    1. Add the following code after the getResourceBundle implementation:

      Code snippet
      
      onNavBack: function() {
       var sPreviousHash = History.getInstance().getPreviousHash();
       if (sPreviousHash !== undefined) {
       // The history contains a previous entry
       history.go(-1);
       } else {
       // Otherwise we go backwards with a forward history
       var bReplace = true;
       this.getRouter().navTo("masterlist", {}, bReplace);
       }
      }
      Expand
    2. Save your changes. Your BaseController implementation should now look like the following:

      Code snippet
      
      sap.ui.define([
       "sap/ui/core/mvc/Controller",
       "sap/ui/core/routing/History"
      ], function (Controller, History) {
      "use strict";
      return Controller.extend("student00.com.sap.training.ux402.listdetail.ux402listdetail.controller.BaseController", {
       getRouter: function () {
       return this.getOwnerComponent().getRouter();
       },
      
       getListSelector: function() {
       return this.getOwnerComponent().oListSelector;
       },
      
       getResourceBundle: function () {
       return this.getOwnerComponent().getModel("i18n").getResourceBundle();
       },
      
       onNavBack: function() {
       var sPreviousHash = History.getInstance().getPreviousHash();
       if (sPreviousHash !== undefined) {
       // The history contains a previous entry
       history.go(-1);
       } else {
       // Otherwise we go backwards with a forward history
       var bReplace = true;
       this.getRouter().navTo("masterlist", {}, bReplace);
       }
       }
      });
      });
      Expand

Task 10: Implement the Application Controller 

In this task, you implement the controller of the App view to link the JSON model for layout to the Application.

Steps

  1. Open the App.controller.js file. Define that the controller should be derived from the BaseController. Moreover, add sap/ui/model/JSONModel to the namespace.

    1. Open the App.controller.js file located in the controller folder of your project.

    2. Update the code as follows :

    Code snippet
    
    sap.ui.define(
     [
     "./BaseController",
     "sap/ui/model/json/JSONModel"
     ],
     function(BaseController, JSONModel) {
     "use strict";
    
     return BaseController.extend("student00.com.sap.training.ux402.listdetail.ux402listdetail.controller.App", {
     onInit() {
     
     }
     });
     }
    );
    Expand
  2. Implement the onInit function. Create an JSON-Model with an attribute layout and assign the created Model to the App view.

    1. Add the following code into the onInit function:

      Code snippet
      
       var oViewModel = new JSONModel({
       layout : "OneColumn"
       });
      
       this.getView().setModel(oViewModel, "mainView");
      Expand

Task 11: Implement the List Controller

In this task, you implement the controller of the List view for navigation.

Steps

  1. Open the List.controller.js file. Define that the controller should be derived from the BaseController. Add a reference to the Device API of SAPUI5.

    1. Open the List.controller.js file located in the controller folder of your project.

    2. Update the code as follows:

      Code snippet
      
      sap.ui.define([
       "student##/com/sap/training/ux402/listdetail/ux402listdetail/controller/BaseController",
       "sap/ui/Device"
      ],
      /**
      * @param {typeof sap.ui.core.mvc.Controller} Controller
      */
      function (Controller, Device) {
       "use strict";
      Expand
  2. Implement a _navigateToCarrierDetails function. The function takes two arguments. Name the first argument sCarrierId and the second bReplace. The function should navigate to the carrierdetails route and pass a sCarrierId value as a parameter.

    1. Add the following code after the onInit function:

      Code snippet
      
      _navigateToCarrierDetails : function(sCarrierId,bReplace) {
       this.getRouter().navTo("carrierdetails", {
       objectId: sCarrierId
       }, bReplace);
      },
      Expand
  3. Add a _showDetail function. The function takes one argument. Name the argument oItem. This variable will contain during runtime the item selected by the user from the master list. Read the property Carrid from the oItemand store it in a variable sCarrierId. Check also if the app is running on a mobile device or not. Store the result in a local variable bReplace. Invoke the _navigateToCarrierDetails function and pass both variables as an argument.

    1. After the _navigateToCarrierDetails implementation, add the _showDetail function with an oItem argument as follows:

      Code snippet
      _showDetail: function(oItem) {
      
      },
      Expand
    2. Check whether the app is running on a mobile device or on a desktop and store the result in a local variable. Then read the Carrid property from the bindingContext of the oItem object and store it in a variable. Pass both variables to the _navigateToCarrierDetails function. Add the following code into the _showDetail function:

      Code snippet
      
      var bReplace = !Device.system.phone;
      var sCarrierId = oItem.getBindingContext().getProperty("Carrid");
      this._navigateToCarrierDetails(sCarrierId,bReplace);
      Expand
  4. Implement the event handler for the select event of the list. The assign function to handle the event was called onSelect. Invoke the _showDetail method and pass the selected item to the function. The selection behavior of the table depends on whether the app is running on a mobile device or not.

    1. Add the following code after the _showDetail implementation:

      Code snippet
      
      onSelect: function(oEvent) {
       this._showDetail(oEvent.getParameter("listItem") || oEvent.getSource());
      },
      Expand
  5. Implement an onBypassed function. This function should handle the case that when the router bypasses the target. Inside the function, invoke the removeSelections function on the list object.

    1. Add the following code after the onSelect implementation:

      Code snippet
      
      onBypassed: function() {
       this._oList.removeSelections(true);
      },
      Expand
  6. Add an _onListMatched function to the implementation. The function takes no argument. The function will be called when the list route is invoked by the router. React on the case when the promise oWhenListLoadingIsDone is resolved. If the promise is resolved, the details of the first item from the master list should be displayed inside the details view. This should only be processed when the mode of list is not None.

    1. Add the following code after the onBypassed implementation :

      Code snippet
      
      _onListMatched: function() {
       this.getListSelector().oWhenListLoadingIsDone.then(
       function(mParams) {
       if (mParams.list.getMode() === "None") {
       return;
       }
       var sObjectId = mParams.firstListitem.getBindingContext().getProperty("Carrid");
       this._navigateToCarrierDetails(sObjectId,true);
       }.bind(this)
       );
      }
      Expand
  7. Implement the onInit function. Get a reference to the master list of your view and store the reference in a variable named oList. Assign the reference to oList to a member variable named _oList. Add a eventhandler for onBeforeFirstShow of the view using addEventDelegate-function and invoke the setBoundMasterList of the ListSelector and pass the reference to the oList object to the function. Implement a behavior when the masterlist route is invoked by the router. If this is the case, the _onListMatched function should be invoked. Implement a behavior when the router bypasses the list route, register the onBypassed function when this event occurs. Then save your changes.

    1. Add the following code to the onInit function:

      Code snippet
      
      var oList = this.byId("list");
      this._oList = oList;
      this.getView().addEventDelegate({
       onBeforeFirstShow: function () {
       this.getOwnerComponent().oListSelector.setBoundMasterList(this._oList);
       }.bind(this)
      }); 
      this.getRouter().getRoute("masterlist").attachPatternMatched(this._onListMatched, this);
      this.getRouter().attachBypassed(this.onBypassed, this);
      Expand
    2. Save your changes.

Task 12: Implement the Detail Controller

In this task, you implement the controller of the Detail view for navigation.

Steps

  1. Open the Detail.controller.js file. Define that the controller should be derived from the BaseController and add a reference to the Device API of SAPUI5.

    1. Open the Detail.controller.js file located in the controller folder of your project.

    2. Update the code as follows:

      Code snippet
      
      sap.ui.define([
       "student##/com/sap/training/ux402/listdetail/ux402listdetail/controller/BaseController",
       "sap/ui/Device"
      ],
      /**
      * @param {typeof sap.ui.core.mvc.Controller} Controller
      */
      function (Controller, Device) {
       "use strict";
      Expand
  2. Define a function named _onBindingChange. The function should check whether the view is bound to an entity or not. If not, a target with the name detailObjectNotFound should be displayed and the selection on the master list should be cleared. To clear the selection, call the function clearListSelection on the ListSelector. If the view is bound to an entity, get the binding path of the entity and invoke the selectAListItem function from the ListSelector. Pass the binding path as an argument.

    1. Add the following code after the onInit function:

      Code snippet
      
      _onBindingChange: function() {
       var oView = this.getView();
       var oElementBinding = oView.getElementBinding();
       if (!oElementBinding.getBoundContext()) {
       this.getRouter().getTargets().display("detailObjectNotFound");
       this.getOwnerComponent().oListSelector.clearMasterListSelection();
       return;
       }
       var sPath = oElementBinding.getPath();
       this.getOwnerComponent().oListSelector.selectAListItem(sPath);
      },
      Expand
  3. Implement a function named _bindView. The function takes one argument named sObjectPath. The variable contains the path to the selected Object from the master list. Update the binding of the view using the bindElement function. Pass a literal object to the bindElement function. Add a property named path to the literal object and assign the sObjectPath variable to the property. Then add an events property to the literal object. Assign another literal object to the events property. Define three attributes to the events object. The first property is called change. Assign the reference to the function _onBindingChangeto the property. Add a property dataRequested and dataReceived. Assign a function to each property. Implement the function for dataRequested and show that the view is busy. Implement the function for dataReceived and hide the busy indicator for the view.

    1. Add the following code after the _onBindingChange implementation:

      Code snippet
      
      _bindView: function(sObjectPath) {
       var oView = this.getView();
      
       this.getView().bindElement({
       path: sObjectPath,
       events: {
       change: this._onBindingChange.bind(this),
       dataRequested: function() {
       oView.setBusy(true);
       },
       dataReceived: function() {
       oView.setBusy(false);
       }
       }
       });
      },
      Expand
  4. Implement a function named _onObjectMatched. This function is an event handler function for the Pattern-Matched event during navigation. Read the navigation property objectId from the events arguments and call the _bindView function of the controller. Pass the objectId to the function. In addition, change the layout attribute of the mainView Model to TwoColumnsMidExpanded to adjust the layout behavior of your sap.f.flexibleColumnLayout.

    1. Add the following code after the _bindView implementation:

      Code snippet
      
      _onObjectMatched: function(oEvent) {
       this.getView().getModel("mainView").setProperty("/layout", "TwoColumnsMidExpanded");
       var sObjectPath =
       "/UX_C_Carrier_TP('" + oEvent.getParameter("arguments").objectId + "')";
       this._bindView(sObjectPath);
      }
      Expand
  5. Implement the onInit function. Register the _onObjectMatched event handler for the pattern matching event of the carrierdetails route. Then save your changes.

    1. Update the code of the onInit function as follows:

      Code snippet
      
      onInit: function () {
       this.getRouter().getRoute("carrierdetails").attachPatternMatched(this._onObjectMatched, this);
      },
      Expand
    2. Save your changes.

Task 13: Implement the NotFound Controller

In this task, you implement the controller of the NotFound view.

Steps

  1. Open the NotFound.controller.js file. Define that the controller should be derived from the BaseController. In addition, delete the onInit function. Then save your changes.

    1. Open the NotFound.controller.js file located in the controller folder of your project.

    2. Update the code as follows:

      Code snippet
      
      sap.ui.define([
       "student##/com/sap/training/ux402/listdetail/ux402listdetail/controller/BaseController"
      
      ],
      /**
      * @param {typeof sap.ui.core.mvc.Controller} Controller
      */
      function (Controller) {
       "use strict";
      
       return Controller.extend("student00.com.sap.training.ux402.listdetail.ux402listdetail.controller.NotFound", {
      
       });
      });
      Expand
    3. Save your changes.

Task 14: Implement the DetailObjectNotFound Controller

In this task, you implement the controller of the DetailObjectNotFound view.

Steps

  1. Open the DetailObjectNotFound.controller.js file. Define that the controller should be derived from the BaseController. In addition, delete the onInit function. Then save your changes.

    1. Open the DetailObjectNotFound.controller.js file located in the controller folder of your project.

    2. Update the code as follows:

      Code snippet
      
      sap.ui.define([
       "student##/com/sap/training/ux402/listdetail/ux402listdetail/controller/BaseController"
      ],
      /**
      * @param {typeof sap.ui.core.mvc.Controller} Controller
      */
      function (Controller) {
      "use strict";
      
       return Controller.extend("student00.com.sap.training.ux402.listdetail.ux402listdetail.controller.DetailObjectNotFound", {
      
       });
      });
      Expand
    3. Save your changes.

Task 15: Test your Application

Steps

  1. Test your application. Choose a carrier to see the corresponding connections.

    1. Perform this step as shown in a previous exercise.

  2. Try to navigate to a hash for which no route exists. The NotFound view should be displayed.

    1. Change the URL in your browser to something not corresponding to a route, for example Carrier/AA instead of Carriers/AA.

  3. Try to navigate to a hash for which a route exists, but also to the object key for which no suitable detail object can be loaded. The DetailObjectNotFound view should be displayed.

    1. Change the URL in your browser so that the Carrier id is not known, for example enter Carriers/XX.

Log in to track your progress & complete quizzes