0
votes

I creating a SAPUI5 application using the "SAP Fiori Master-Detail Application" Template. I have the Master and Detail pages working and properly connecting to my OData service. What I am trying to do now is route the Detail page to a second (different) Detail page.

The first Detail page (I'll call Detail1) contains a list of the Detail2 objects. Clicking one of those objects in a list should take you to the Detail2 page (where it displays even more information).

I created a new Detail2 View and Controller using WebIDE, so it came with some filler code. I also created a new route in the manifest.json file:

{
    "name": "detail2Object",
    "pattern": "Detail2(Master={masterId},Id={detail1Id})",
    "titleTarget": "",
    "greedy": false,
    "target": ["master", "detail2"]
}

"detail2Object": {
    "viewType": "XML",
    "transition": "slide",
    "clearAggregation": true,
    "viewName": "Detail2",
    "title": "",
    "viewId": "detail2page",
    "viewLevel": 3
}

As you can see from the pattern, the service takes in two parameters: Master and ID (which are the Master ID and the first Defects Id).

Testing the service independent from my application returns data just fine: http://{root}.com:{port}/sap/opu/odata/sap/TEST_SRV/Detail2(‌​Master='552364',Id='‌​0004')

I created a NavTo function in my Detail1 controller to pass in the Master and Id parameters:

_onNav : function (oEvent) {
            // get the list item, either from the listItem parameter or from the event's source itself (will depend on the device-dependent mode).
            this._showDetail2(oEvent.getParameter("listItem") || oEvent.getSource());
},

_showDetail2 : function (oItem) {
            this.getRouter().navTo("detail2Object", {
                masterId: oItem.getBindingContext().getProperty("Master"),
                detail1Id: oItem.getBindingContext().getProperty("Id")
            });
}

When I run the application through WebIDE testing (both with the live OData and with Mockdata I created) clicking on the Detail2 object in the Detail1 page navigates me to a new page that displays this error:

This <"ObjectName"> is not available

Going into the F12 tools, I see the following error:

MockServer: Malformed URI literal syntax in key 'Id' -

HTTP request failed400,Bad Request,{"error":{"code":400,"message":{"lang":"en","value":"Malformed URI literal syntax in key 'Id'"}}} -

This is occurring even though the url correctly pulled the parameters:

http://localhost:54634/webapp/test/mockServer.html?hc_reset&origional-url=mockServer.html&sap-ui-appCacheBuster=..%2F..%2F..%2F&sap-ui-xx-componentPreload=off#/Detail2(Master='552364',Id='0004')

Through a lot of research, the only thing I am finding is that this error may have something to do with the metadata file. However, both Id's are of type Edm.String and they are both spelled the exact same way, so I don't think that's the issue. Also, as stated earlier, the service runs just fine with the same parameters running independently from the application.

Any help would be greatly appreciated.

Update

Detail1 View which contains a list of the Detail2 objects:

        <List noDataText="Drop list items here" id="__list0" items="{DetailToDetail2}" headerText="Defects">
            <items>
                <ObjectListItem type="Navigation" title="{Detail2Title}" number="{Qty}" numberUnit="Detail Qty" press="_onNav">
                <attributes>
                    <ObjectAttribute text="{Detail2_Attr}" id="__attribute11" title="Detail2 Attr"/>
                    <ObjectAttribute id="__attribute12" title="Detail2 Attr2" text="{Detail2_Attr2}"/>
                </attributes>
                </ObjectListItem>
            </items>
        </List>

The item="{DetailToDetail2}" appears as a NavigationProperty and Association in the metadata of service.

This is how it looks in the Detail1 Entity:

<NavigationProperty Name="DetailToDetail2" Relationship="TEST_SRV.DetailToDetail2" FromRole="FromRole_DetailToDetail2" ToRole="ToRole_DetailToDetail2"/>

This is the entire Detail2 Entity:

        <EntityType Name="Detail2" sap:content-version="1">
            <Key>
                <PropertyRef Name="Id"/>
                <PropertyRef Name="Master"/>
            </Key>
            <Property Name="Id" Type="Edm.String" Nullable="false" MaxLength="4" sap:unicode="false" sap:label="Id" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
            <Property Name="Master" Type="Edm.String" Nullable="false" MaxLength="12" sap:unicode="false" sap:label="Master" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
            <Property Name="Attr1" Type="Edm.String" MaxLength="40" sap:unicode="false" sap:label="Attribute 1" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
            <Property Name="Qty" Type="Edm.Int32" sap:unicode="false" sap:label="Detail Qty" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
            <Property Name="Attr2" Type="Edm.String" MaxLength="40" sap:unicode="false" sap:label="Attribute 2" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
            <Property Name="Attr3" Type="Edm.String" MaxLength="4" sap:unicode="false" sap:label="Attribute 3" sap:creatable="false" sap:updatable="false" sap:sortable="false" sap:filterable="false"/>
        </EntityType>

And here is the DetailToDetail2 Association:

        <Association Name="DetailToDetail2" sap:content-version="1">
            <End Type="TEST_SRV.Detail1" Multiplicity="1" Role="FromRole_DetailToDetail2"/>
            <End Type="TEST_SRV.Detail2" Multiplicity="*" Role="ToRole_DetailToDetail2"/>
            <ReferentialConstraint>
                <Principal Role="FromRole_DetailToDetail2">
                    <PropertyRef Name="Id"/>
                </Principal>
                <Dependent Role="ToRole_DetailToDetail2">
                    <PropertyRef Name="Master"/>
                </Dependent>
            </ReferentialConstraint>
        </Association>
1
The error you are experiencing is coming form OData and has little to do with the routing itself. Please show the nav handler of view 2. Most probably the problem lies in binding path / odata url construction. Pls. also post the erroneous service url that resulted in the Malformed URI error.cschuff
It's good to know that the problem is coming from the OData and not the routing. That narrows down where the problem lies. I added information about my service metadata. Maybe the problem is within the Association that wouldn't otherwise we called when I test the service independently of the application?K. Peters
The erroneous service url that resulted in the Malformed URI error is the localhost url that I posted above: localhost:54634/webapp/test/…K. Peters
When using a navigation property in the binding, it is necessary to use the parameter “expand” and select. See hereuser6177399
Do the "expand" and "select" parameters still apply if both Id and Master are primary keys for the Detail2 Entity?K. Peters

1 Answers

0
votes

I found the "Malformed URI" error was occurring not at the routing or navigation, but with the binding after getting to the page.

My Detail2 controllers _onObjectMatched function looked like this:

_onObjectMatched : function (oEvent) { 
        var sObjectMasterId =  oEvent.getParameter("arguments").masterId; 
        var sObjectDetailId =  oEvent.getParameter("arguments").detail1Id; 
        this.getModel().metadataLoaded().then( function() { 
          var sObjectPath = this.getModel().createKey("Detail2", { 
            masterId:  sObjectMasterId, 
            detail1Id : sObjectDetailId 
          }); 
          this._bindView("/" + sObjectPath); 
        }.bind(this)); 

Through debugging I found that the sObjectPath variable was being set to Detail2(Id=null, Master=null). That is when I realized masterId and detail1Id are not the correct parameter names to use because we are not binding to the route, we are binding to the path. So I replaced masterId with Master and detail1Id with Id:

_onObjectMatched : function (oEvent) { 
    var sObjectMasterId =  oEvent.getParameter("arguments").masterId; 
    var sObjectDetailId =  oEvent.getParameter("arguments").detail1Id; 
    this.getModel().metadataLoaded().then( function() { 
      var sObjectPath = this.getModel().createKey("Detail2", { 
        Master:  sObjectMasterId, 
        Id: sObjectDetailId 
      }); 
      this._bindView("/" + sObjectPath); 
    }.bind(this)); 

Voila, it worked!

Thank you to everyone who responded and pointed me away from the routing and towards the binding. I would suggest to anyone getting the "Malformed URI" error to review all bindings and navigation to verify that all parameters are spelt correctly.

Reviewing the Routing with Parameters tutorial was also a significant help.