Search code examples
javascriptodatasapui5web-ide

SAPUI5: Malformed URI literal syntax in key


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>

Solution

  • 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.