Display a 'one to many' association with SAP Fiori annotations in CAP - sap-fiori

I try do do a DB project with SAP cap. I want to display all entities associated with the Parent entity. If the Association is one to one it works fine. But if it is one to many it doesn't display anything.
--I have following Schema definition:
entity Company{
key ID : Integer #title: 'Company ID';
cName : String #title: 'Company name';
pers: Association to many Person on pers.comp = $self; //association to one works
}
entity Person{
key ID : Integer #title: 'ID';
pName: String #titel: 'Name';
comp: Association to Company;
}
--And following fiori Annotations:
annotate Admin.Company with #(
UI.LineItem:[
{$Type: 'UI.DataField', Value: cName},
],
UI.HeaderInfo:{
TypeName: 'Company',
TypeNamePlural: 'Companys'
},
);
//Details view:
annotate Admin.Company with #(
UI: {
HeaderInfo: {
Description: {Value: ID}
},
HeaderFacets: [
{$Type: 'UI.ReferenceFacet', Target: '#UI.FieldGroup#Comp'},
],
Facets: [
{$Type: 'UI.ReferenceFacet', Target: '#UI.FieldGroup#Pers'}
],
FieldGroup#Comp: {
Data: [
{Value: cName},
]
},
FieldGroup#Pers: {
Data: [
{Value: pers.pName},
]
},
});
The Admin Service is just a projection on the given entities.
Does anyone know what I have to change to get pers.Name displayed in a list or something like that?
As already mentioned if I change in Schema definition 'association to many' into 'association to one' it works. But I want it to have more than one entity associated.

If you are using the Fiori Elements List Report Object Page template I think you can only use a 1:1 association. This is the intended design of the floorplan (List → Detail). How should Fiori Elements know which of the "many" person items should be bound to the one detail view.
However, what you can and probably want to do is binding the detail view to the selected company item and create a field group. There you can display a table which refers to a different entity. So, you keep your schema.cds as it currently is but change your annotation.cds:
annotate Admin.Company with #(
UI: {
// columns of the list view
LineItem:[
{$Type: 'UI.DataField', Value: cName},
],
// title (singular/plural) of the list view and description of the detail view
HeaderInfo:{
TypeName: 'Company',
TypeNamePlural: 'Companies',
Description: {Value: ID}
},
// facet for the person table
HeaderFacets: [
{$Type: 'UI.ReferenceFacet', Target: 'Person/#UI.LineItem'}
]
);
annotate Admin.Person with #(
// person table (only displayed in detail view) with columns
UI.LineItem: [
{$Type: 'UI.DataField', Value: 'comp/cName'},
{$Type: 'UI.DataField', Value: 'pName'}
]);
It might be that the code above doesn't work right away because I didn't test it. But I created a working example with the Northwind OData V2 Service using the customer entity for the list view and the order entity in a table in the detail view. You can recreate the example by running the Fiori List Report Object Page wizard with the OData service URL https://services.odata.org/Northwind/Northwind.svc/ and the main entity Customer. When the generation of the project is finished you can replace the content of the annotation.xml with the following:
<edmx:Edmx xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx" Version="4.0">
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/Common.xml">
<edmx:Include Namespace="com.sap.vocabularies.Common.v1" Alias="Common" />
</edmx:Reference>
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/UI.xml">
<edmx:Include Namespace="com.sap.vocabularies.UI.v1" Alias="UI" />
</edmx:Reference>
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/Communication.xml">
<edmx:Include Namespace="com.sap.vocabularies.Communication.v1" Alias="Communication" />
</edmx:Reference>
<edmx:Reference Uri="/Northwind/Northwind.svc/$metadata">
<edmx:Include Namespace="NorthwindModel" />
<edmx:Include Namespace="ODataWebV3.Northwind.Model" />
</edmx:Reference>
<edmx:DataServices>
<Schema xmlns="http://docs.oasis-open.org/odata/ns/edm" Namespace="local">
<Annotations Target="NorthwindModel.Customer">
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="CompanyName" />
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.Facets">
<Collection>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Target" AnnotationPath="Orders/#UI.LineItem" />
</Record>
</Collection>
</Annotation>
</Annotations>
<Annotations Target="NorthwindModel.Order">
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="OrderID" />
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="Customer/CompanyName" />
</Record>
</Collection>
</Annotation>
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>
I hope this solves your problem. If you have any questions feel free to ask.

Related

How to filter by OData nested dynamic property?

I have an open type entity that is accessed by foo.net/items.
The entity is defined as:
<EntityType Name="myEntity" OpenType="true">
<Key>
<PropertyRef Name="name" />
</Key>
<Property Name="name" Type="string" />
</EntityType>
An entity instance may look something like this:
{
"name": "foo",
"location": {
"country": "USA"
}
}
How can I reference the country property in my filter odata option? Is this the correct approach?
foo.net/items?$filter=location/country ne null and location/country eq 'USA'.
I believe the above is correct because it is a valid way to reference a property as per the ABNF in OData V4.
Yes, you can reference nested property by parentProperty/childProperty.
In your case is not necessary to check if nested property in not null, so you can simplify the query like this:
foo.net/items?$filter=location/country eq 'USA'

How to build an edit form for a list of strings in Grails?

I have a Grails domain class with a list of strings in it, and I want to edit these strings. For the sake of simplicity let's assume that the list is fixed size. Here's what I have:
MyEntity.groovy:
class MyEntity {
String name
List variables = ['one', 'two', 'three']
static hasMany = [
variables: String,
]
}
_fields/myEntity/variables/_widget.gsp:
<g:textField name="variables[0]" value="${value[0]}" />
<g:textField name="variables[1]" value="${value[1]}" />
<g:textField name="variables[2]" value="${value[2]}" />
This renders text fields for each element in the list that are prefilled with the correct values. However, when I edit the values and sumbit the form my edits get discarded. What am I missing?
Ok, I found it myself. The input fields all need to have the name of the domain property, without any index:
<g:textField id="variables[0]" name="variables" value="${value[0]}" />
<g:textField id="variables[1]" name="variables" value="${value[1]}" />
<g:textField id="variables[2]" name="variables" value="${value[2]}" />

JIRA Gadget Multiselect field select all

I've created a JIRA gadget that has a multiselect field which by default (isConfigured == "false") has no field selected. Is it possible to have the default, when the gadget is first added to the dashboard that multiselect items are selected?
You can do this. Apply these changes to your gadget.xml file:
...
<UserPref name="projectId" display_name="Project" datatype="select" default_value="My def value"/>
...
fields: [
{
userpref: "projectId",
label: "Project",
type: "select",
selected: this.getPref("projectId"),
options: projects
},
...
More code you can find here: JIRA - list projects in configuration and remember selection

Use Struts2 property tag in DisplayTags

I'm trying to render a table using DisplayTag. Everything works normally when I render a column in the usual manner:
<display:column property="id_material" title="ID" />
Now, i want one column to show 2 properties concatenated. So I tried using the < s:property > tag:
<display:column title="UNIDAD"><s:property value="property1"/> <s:property value="property2"/></display:column>
But no value is showed in the column. Is there an alternative way to do this?
You can use a decorator, get the row object properties and return the concatenation of both.
A quick example:
public class ExampleDecorator extends TableDecorator{
public String getConcatenatedProperties(){
Object object = getCurrentRowObject();
return object.getProperty1() + " " + getProperty2();
}}
Then you add a column in the display table:
<display:column property="concatenatedProperties" title="Properties" />
To use the decorator in the displaytag:
<display:table [..] decorator="org.example.ExampleDecorator" [...] >
Try to add uid attribute in table tag and then you can access current row in Struts2 via request attribute. See following code:
<display:table name="list" uid="row">
<display:column title="UNIDAD"><s:property value="#attr.row.property1"/> <s:property value="#attr.row.property2"/>
</display:column>
</display:table>

LINQ to SQL Table Dependency

If I have two tables... Category and Pet.
Is there a way in LINQ to SQL to make the result of the joined query map to a another strongly typed class (such as: PetWithCategoryName) so that I can strongly pass it to a MVC View?
I currently have Category and Pet classes... should I make another one?
Maybe I missing something here. Can any of you enlighten me?
from p in petTable
join c in categoryTable on p.CategoryId equals c.Id
where (c.Id == categoryId.Value)
select new
{
p.Id,
p.Name,
p.Description,
p.Price,
CategoryName = c.Name
}
<?xml version="1.0" encoding="utf-8" ?>
<Database Name="PetShop" xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007">
<Table Name="Category" Member="PetShop.Models.Category">
<Type Name="PetShop.Models.Category">
<Column Name="Id" Member="Id" IsDbGenerated="true" IsPrimaryKey="true" />
<Column Name="Name" Member="Name" />
<Column Name="Description" Member="Description" />
</Type>
</Table>
<Table Name="Pet" Member="PetShop.Models.Pet">
<Type Name="PetShop.Models.Pet">
<Column Name="Id" Member="Id" IsDbGenerated="true" IsPrimaryKey="true" />
<Column Name="Name" Member="Name" />
<Column Name="Description" Member="Description" />
<Column Name="ImageUrl" Member="ImageUrl" />
<Column Name="Price" Member="Price" />
<Column Name="CategoryId" Member="CategoryId" />
<Association Name="FK_Pet_Category" Member="Category" ThisKey="CategoryId" OtherKey="Id" IsForeignKey="true" />
</Type>
</Table>
</Database>
How would I go about using LoadWith? I'm not finding much help online. Any good resources?
I found this online: http://blogs.msdn.com/wriju/archive/2007/10/04/linq-to-sql-change-in-datacontext-from-beta-1-to-beta-2.aspx
You would do something like:
var loadOption = new DataLoadOptions();
loadOption.LoadWith<Pets>(p => p.Category);
db.LoadOptions = loadOption;
var pets = from p in PetStoreContext.Pets
select p;
And then your pets query will already include category, so no trip to the database happens when you try to access category.
If you use the LoadWith LoadOption then your Pet query will do an eager load on categories, so that you will be able to do
MyPet.Category.Name without incurring an extra query, so you'll have the data joined and strongly typed without the risk of running multiple queries for Categories as you loop or bind on the Pet collection.
Or you can use stored procedures in Linq To SQL,the result is strongly typed.

Resources