Nicolas Apercé Nicolas Apercé - 5 months ago 183
JSON Question

SAPUI5 Metadata.xml : binding two entities

I am in internship in a firm where I have to code in JS using SAPUI5 framework. It's totally new for me, that's why I followed the tutorial at http://sapui5.hana.ondemand.com . But now I have a problem, I can't find any advise on the Internet, and I can't always ask my colleagues to help me, they have a job too...

I have to do a planning with many employees and their task. I work with a mockserver with a metadata.xml file :

<edmx:Edmx xmlns:edmx="http://schemas.microsoft.com/ado/2007/06/edmx"
Version="1.0">
<edmx:DataServices
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
m:DataServiceVersion="1.0">
<Schema xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns="http://schemas.microsoft.com/ado/2008/09/edm" Namespace="NorthwindModel">
<EntityType Name="Employee">
<Key>
<PropertyRef Name="EmployeeID" />
</Key>
<Property xmlns:p8="http://schemas.microsoft.com/ado/2009/02/edm/annotation"
Name="EmployeeID" Type="Edm.Int32" Nullable="false"
p8:StoreGeneratedPattern="Identity" />
<Property Name="LastName" Type="Edm.String" Nullable="false"
MaxLength="20" Unicode="true" FixedLength="false" />
<Property Name="City" Type="Edm.String" Nullable="false"/>
<Property Name="Team" Type="Edm.String" Nullable="false"/>
<NavigationProperty Name="Appointments" Relationship="NorthwindModel.FK_Employees_Appointment"
FromRole="Employees" ToRole="Appointments"/>
</EntityType>
<EntityType Name="Appointment">
<Key>
<PropertyRef Name="AppointmentID"/>
</Key>
<Property Name="AppointmentID" Type="Edm.Int32" Nullable="false"/>
<Property Name="EmployeeID" Type="Edm.Int32" Nullable="false"/>
<Property Name="start" Type="Edm.DateTime" Nullable="false"/>
<Property Name="end" Type="Edm.DateTime" Nullable="false"/>
<Property Name="title" Type="Edm.String" Nullable="false"/>
<NavigationProperty Name="Employees" Relationship="NorthwindModel.FK_Employees_Appointment"
ToRole="Employees" FromRole="Appointments"/>
</EntityType>
<Association Name="FK_Employees_Appointment">
<End Type="Employee" Role="Employees" Multiplicity="1" />
<End Type="Appointment" Role="Appointments" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Employees">
<PropertyRef Name="EmployeeID" />
</Principal>
<Principal Role="Appointments">
<PropertyRef Name="EmployeeID" />
</Principal>
</ReferentialConstraint>
</Association>
</Schema>
<Schema xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices"
xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
xmlns="http://schemas.microsoft.com/ado/2008/09/edm" Namespace="ODataWeb.Northwind.Model">
<EntityContainer
xmlns:p7="http://schemas.microsoft.com/ado/2009/02/edm/annotation"
Name="NorthwindEntities" p7:LazyLoadingEnabled="true"
m:IsDefaultEntityContainer="true">
<EntitySet Name="Employees" EntityType="NorthwindModel.Employee" />
<EntitySet Name="Appointments" EntityType="NorthwindModel.Appointment"/>
<AssociationSet Name="FK_Employees_Appointments" Association="NorthwindModel.FK_Employees_Appointment">
<End Role="Employees" EntitySet="Employees"/>
<End Role="Appointments" EntitySet="Appointments"/>
</AssociationSet>
</EntityContainer>
</Schema>
</edmx:DataServices>




(for tags AssociationSet and NavigationProperty, I did it because I've seen on the OData service I use, but I don't understand it; I guessed it will be fine...)

Now the Appointments.json file :

[
{
"AppointmentID" : 0,
"EmployeeID" : 0,
"start":"\/Date(1466416800000)\/",
"end":"\/Date(1466424000000)\/",
"title": "test"
},{
"AppointmentID" : 1,
"EmployeeID" : 1,
"start":"\/Date(1466409600000)\/",
"end":"\/Date(1466416800000)\/",
"title": "test2"
}]


The Employees.json file :

[
{
"EmployeeID":0,
"LastName":"APERCE",
"City":"Paris",
"Team":"Dev"
},
{
"EmployeeID":1,
"LastName":"HACHMI",
"City":"Lille",
"Team":"Dev"
},
{
"EmployeeID":2,
"LastName":"MILLET",
"City":"Paris",
"Team":"Admin"
},
{
"EmployeeID":3,
"LastName":"CORNET",
"City":"Poitiers",
"Team":"Admin"
},
{
"EmployeeID":4,
"LastName":"EVAIN",
"City":"Paris",
"Team":"R&D"
}]


(As you can see, the EmployeeID is present on both Appointments.json et Employees.json)

And finally the Overview.view.xml file :

<mvc:View controllerName="sap.ui.demo.wt.controller.Overview"
xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc" xmlns:semantic="sap.m.semantic"
xmlns:unified="sap.ui.unified"
xmlns:core="sap.ui.core" displayBlock="true">
<semantic:FullscreenPage title="{i18n>overviewPageTitle}">
<VBox>
<PlanningCalendar startDate="{path : '/Startdate'}" rows="{path : 'data>/Employees',parameters:{expand:'Appointments'}}"
appointmentSelect="handleAppointmentSelect">
<rows>
<PlanningCalendarRow title="{data>LastName}"
appointments="{path : 'data>Appointments',templateShareable:true}">
<appointments>
<unified:CalendarAppointment
startDate="{data>start}"
endDate="{data>end}"
title="{data>title}">

</unified:CalendarAppointment>
</appointments>
</PlanningCalendarRow>
</rows>
</PlanningCalendar>
</VBox>
</semantic:FullscreenPage>




(the "data" model binds already with the metadata.xml and gets both Appointments.json and Employees.json)

Actually, this code displays the two tasks from Appointments.json for every employee in my planning. I want to bind it in order to have the appointment "test" only for the employee "APERCE" and "test2" only for "HACHMI", but I can't.

I believe it is possible to do something equivalent to a JOIN for a SQL Database, but I don't find anything about it. How should I do it ? Thank you for your answers.

PS : I am french, if you don't understand what I wrote above, sorry ; and if you can answer in french, do so, it will be better for my understanding.

EDIT : I have corrected the code according to nistv4n's advises. Now I get the following errors :

MockServer: Resource not found for the segment 'Appointment' => I understand that, the segment 'Appointment' doesn't exist since it's 'Appointments', but I don't know where I forgot the 's'.

The following problem occurred: HTTP request failed404,Not Found,{"error":{"code":404,"message":{"lang":"en","value":"Resource not found for the segment 'Appointment'"}}} => I guess it's related to the previous error, because it requests for 'Appointment' instead of 'Appointments' ?

REEDIT : I put the 's' on the right places. Now I just have : Cannot read property 'getTime' of undefined => because there is a problem with the model used in Overview.view.xml, probably in the , but it's the same if I use : "{Appointments/start}", "{Appointments>start}", "{start}" or "{/Appointments>start}".

LASTEDIT : Thanks to nistv4n, I finally did it. I have updated my code above if you have the same problem. The correct code is in PlanningCalendarRow => appointments="{path : 'data>Appointments'}" and in unified:CalendarAppointment => startDate="{data>start}" etc...

Actually, to quote nistv4n : "Appointments has a relative reference, and inner properties (start, end, title) has also a relative reference, including the model name."

Answer

Where you define the binding for aggregation rows of PlanningCalendar, include the $expand keyword to make the referenced activities accessible in the container:

<PlanningCalendar startDate="{path : '/Startdate'}" rows="{path : 'data>/Employees', parameters:{expand:'Appointment'}}" 
appointmentSelect="handleAppointmentSelect">

It will fetch the assigned Appointment data for the given employee. You can refer to that using the Appointment/start binding path inside the aggregation.

Edit: It seems you missed the Association node from the first schema. Define it like this:

<Association Name="FK_Employees_Appointment">
    <End Type="Employee" Role="Employees" Multiplicity="1" />
    <End Type="Appointment" Role="Appointments" Multiplicity="*" />
    <ReferentialConstraint>
        <Principal Role="Employees">
            <PropertyRef Name="EmployeeID" />
        </Principal>
        <Principal Role="Appointments">
            <PropertyRef Name="EmployeeID" />
        </Principal>
    </ReferentialConstraint>
</Association>

I would recommend to define only one-way connection between the two entities.

Could you post your code to somewhere (even in a zip archive) to investigate the whole architecture and codeline?

Edit 2: Please change the PlanningCalendarRow segment of your View XML according to this:

<PlanningCalendarRow title="{data>LastName}"
    appointments="{path : 'data>Appointments',templateShareable:true}">
    <appointments>
        <unified:CalendarAppointment
            startDate="{data>start}"
            endDate="{data>end}"
            title="{data>title}">

        </unified:CalendarAppointment>
    </appointments>
</PlanningCalendarRow>

Appointments has a relative reference, and inner properties (start, end, title) has also a relative reference, including the model name.