Why can I give items as property? - sapui5

The Table is extended from the ListBase. On the Table, I can you use items aggregation from ListBase as a property like as following:
<Table inset="false" items="{ path: '/ProductCollection', sorter: { path: 'Name' } }">
Why can I use items as a property, although is defined as aggregation?

In UI5, there are basically two ways of filling an aggregation. One is by adding the elements directly, and the other is to bind them to a model.
Your example shows the latter case, where the items aggregation is bound to the collection /ProductCollection in your model.
An aggregation binding consists of two parts in XML views, one is the actual binding with the "property" as you did, and the other is to specify the "template" which is used for each element in the collection.
This is also explained further in the documentation:
<mvc:View
controllerName="sap.ui.sample.App"
xmlns="sap.m"
xmlns:mvc="sap.ui.core.mvc">
<List id="companyList" items="{/companies}">
<items>
<StandardListItem
title="{name}"
description="{city}"
/>
</items>
</List>
</mvc:View>
The List element has both an items attribute and a nested items element:
The attribute items="{/companies}" binds the children of our json model's companies array to the list. This by itself is not enough to display the companies, instead it sets the parent path for the binding of all contained list items and their descendants. In addition you need to declare a nested element.
The nested items element in our case contains a StandardListItem. This serves as a template for creating the individual list rows.

Writing "items=..." in the XML is the same as writing "bindItems(..." or "bindAggregation('items',..." in JS. The framework knows that it is an aggregation and that you are binding the path '/ProductCollection' from your unnamend odata model to it.
As Ronnie mentioned, in the binding there are properties like 'path' or 'sorters'. You can check it here https://sapui5.hana.ondemand.com/#/api/sap.ui.base.ManagedObject/methods/bindAggregation
The object oBindingInfo is the one.

Related

SAPUI5 - How do I aggregate with formElement?

I previously implemented aggregation with VBox. This get all the 'questions' and creates a Text box for each....
<VBox items="{path: 'view>questions', templateShareable: true}">
<items>
<VBox class="sapUiTinyMargin" templateShareable="true">
<Text text="Question {view>OrderSequence}"/>
</VBox>
</items>
</VBox>
I need to do the same, but for formElements. Can this be done?
<f:formElements>
<f:FormElement label="{i18n>radioLabel}">
<f:fields>
<Input value="{viewmodel>radioLabel}" id="__redioLabel"/>
</f:fields>
</f:FormElement>
</f:formElements>
It doesn't seem to work with 'items'
In UI5 elements have several characteristics:
Properties: generally scalar attributes, like "title" or "width".
Events: which are fired when something happens, like "press" or "close".
Aggregations: collections of child entities, like the "items" of a list.
Associations: related controls, like the "label" of a input field.
You can find how these relate to the concept of data binding in the official documentation here.
In your case, the "formElements" is an aggregation of the FormContainer element. Based on the documentation:
Aggregation binding can be used to automatically create child controls according to model data. This can be done either by cloning a template control, or by using a factory function. Aggregations can only be bound to lists defined in the model, that is, to arrays in a JSON model or a collection in the OData model.
This implies that ANY aggregation can be used, no matter how it is named. Now, to go back to you example, the reason why "items" does not work, is because the FormContainer parent element has no aggregation with that name. Instead, you must use the "formElements" aggregation.
<f:FormContainer formElements="{viewmodel>/my/path/to/list}">
<f:formElements>
<f:FormElement label="{i18n>radioLabel}">
<f:fields>
<Input value="{viewmodel>radioLabel}"/>
</f:fields>
</f:FormElement>
</f:formElements>
</f:FormContainer>
Also, note that usually, you do not need to give an ID to the template or any of its children (the Input in your example has an ID), that is because that element specifically will not be part of the resulting control tree. It is just used to be cloned to create the "real" elements based on the model list.
Lastly, you have a "templateShareable" property on the VBox in your first example. VBox has no such property, so it does nothing (you actually use it correctly inside the binding specification for the "items" of the parent VBox).
Solution was...
An aggregation on the form...
<f:Form id="formCustomRadio" editable="true" visible="true" formContainers="{viewmodel>answers}">
Thanks a lot
You can use the VBox and then put the f:formElements inside the items.
Or else use Standard List.
FormElements or containers don't have aggregation items.

No data on http://services.odata.org/Northwind/Northwind.svc

I am trying consume an OData service(http://services.odata.org/Northwind/Northwind.svc) in SAPUI5 but it does not return any data
My controller code is as below
Dialog.fragment.xml
<core:FragmentDefinition
xmlns="sap.m"
xmlns:core="sap.ui.core">
<SelectDialog
title="Products"
class="sapUiPopupWithPadding"
items="{/CategoryName}"
search="_handleValueHelpSearch"
confirm="_handleValueHelpClose"
cancel="_handleValueHelpClose">
<StandardListItem
title="{CategoryName}"
/>
</SelectDialog>
</core:FragmentDefinition>
You seem to have an incorrect URI in your model declaration. The image shows the model path assigned as
var sServiceUrl = "8080/http/services.odata.org/V2/Odata/Odata.svc";
This is the reason for the 404 error, you will have to change this to
var sServiceUrl = "http://services.odata.org/Northwind/Northwind.svc";
Also, you have an invalid binding for your SelectDialog. The Odata metadata does no have a EntitySet named "CategoryName". You will have to change this to "Categories".
<SelectDialog
title="Products"
class="sapUiPopupWithPadding"
items="{/Categories}"
search="_handleValueHelpSearch"
confirm="_handleValueHelpClose"
cancel="_handleValueHelpClose">
<StandardListItem
title="{CategoryName}"
/>
</SelectDialog>
Edit: As it seems that you are using SAP WebIDE, it would be a good idea to add services.odata.org as a Destination in HCP
You binded the items aggregation of the SelectDialog to the "/CategoryName" collection, but this collection does not exist in the OData service. I guess you meant to bind it to the Categories collection instead.

BindElement() on form in popup dialog / fragment

Following on from my question about Is it possible to use 2 models in one view I am now confused about how to apply bindElement on a form in an XML fragment.
The fragment (cut down for brevity):
<core:FragmentDefinition
namespaces here...>
<Dialog id="theDialogId" title="Edit Subdetail">
<content>
<f:SimpleForm id="EditFormId" editable="true" >
<f:content>
<Label id="lblName" text="Name" labelFor="inpName" />
<Input id="inpName" editable="true" value="{name}"/>
</f:content>
</f:SimpleForm>
</content>
</Dialog>
</core:FragmentDefinition>
The challenge: Assume I have a model containing path /master/0/detail/6/subdetail/2 - how to bind the simpleform to this path? Im the examples I have seen, elements in dialogs are given a value by
sap.ui.getCore().byId("inputId").setValue("some value)
and accessed via
var uid = sap.ui.getCore().byId("inputId").getValue()
However I want to employ binding to the path.
The issue I have is how to get a reference to the simpleform so that I can use bindElement().
If this were a view I would use
var theForm = this.getView().byId("EditFormId");
var oBindingContext = event.oSource.getBindingContext();
theForm.bindElement(oBindingContext.getPath())
However fragments are not tightly coupled to the view so this will not work. Instead I believe I have to use
var dlgForm = sap.ui.core.Fragment.byId("theFragmentId", "EditFormId")
var oBindingContext = event.oSource.getBindingContext();
dlgForm.bindElement(oBindingContext.getPath())
This may well be correct but I am at a loss as to how to apply the "theFragmentId" in the XML view declaration. Can anyone clear the fog for me?
EDIT: What I was missing, as per the accepted answer, is that the desired fragment ID can be given as the first (optional) parameter of the sap.ui.xmlfragment() function. The key to binding into a fragment-based control is to get a handle to it but the sap.ui.core.Fragment.byId(fragId, ctrlId) function requires a fragment id. My issue was that I was fixated on the XML fragment declaration and how to apply an id there. This feels inconsistent within SAPUI5 but I guess being able to apply an ID to the fragment has some amenity somewhere, maybe.
You can try something like this...
onOpenDialog: function(oEvent) {
if(!this._oEditSubDetailDialog){
this._oEditSubDetailDialog = sap.ui.xmlfragment("fragmentId", "namespace.and.path.to.your.fragment", this);
this.getView().addDependent(this._oEditSubDetailDialog);
}
var sPathToBind = oEvent.getSource().getBindingContext().getPath();
sap.ui.core.Fragment.byId("fragmentId","EditFormId").bindElement(sPathToBind);
this._oEditSubDetailDialog.open();
}
...just need to make sure that the source control of the event has the context bound to it that you want to edit.
You could also bindElement directly on the dialog, this way you would not need to retrieve the form control within the dialog...
this._oEditSubDetailDialog.bindElement(sPathToBind)
This API documentation and this walkthrough guide regarding XML fragments may be useful

how to reach components in macrocomponent by id?

I have a zul file (MainPage.zul) which contains a macrocomponent (configtabs). Macrocomponent's zul file inturn contains another macrocomponent(fieldListBox). How can I use the id of second macrocomponent(fieldListBox) in my MainPage's Controller class? I want to set model for second macrocomponent in doAfterCompose method of MainPage's cOntroller class.
Example code:
<?component name="configtabs" macro-uri="iam.configtab.zul" ?>
<zk>
<window>
<configtabs />
</window>
</zk>
configtab.zul
<hbox>
<fieldListBox id="fieldsbox" />
</hbox>
You can use zk selectors for that.
Click here for a little bit of explanation of what are those selectors.
Also every component has query methods. If you use those methods with the selectors you can query for components inside other components. It's been very useful to me.
For example on your doAfterCompose you can do:
configtabs.queryAll("fieldListBox")
Or
configtabs.queryAll("#fieldsbox")
And it returns the component or components that you want to set model.
I hope it's helpful. It depends on the context.

iPhone: How to handle Core Data relationships

I'm new to Core Data and databases in general. Now I need to parse a XML and store the contents in Core Data. The XML looks something like this:
<books>
<book id="123A" name="My Book">
<page id="95D" name="Introduction">
<text id="69F" type="header" value="author text"/>
<text id="67F" type="footer" value="author text"/>
</page>
<page id="76F" name="Chapter 1">
<text id="118" type="selection">
<value data="1">value1</value>
<value data="2">value2</value>
<value data="3">value3</value>
</text>
</page>
</book>
<book id="124A"...
From my understanding I would need four Entities, like "Books", "Book", "Pages" and "Text". I wonder how to set the relationships correctly and how to add for example a Page object to a Book object and how to retrieve a Text object attribute's value? The tutorials I have found mostly deal with one Entity so I didn't really get the idea.. Gtrateful for any help!
No, you'd need three entities. You can think of "Books" as the CoreData database you're using. The CoreData database then includes a number of entities called book.
I think the data model you have is a bit weird, but I guess it makes sense for your application. To map it to CoreData I would:
Add the entities Book, Page, Text
Add a bookId, pageId, textId to them, respectively.
Then add a relation from Page to Book, and from Text to Page.
By then you should be able to print out a whole book by asking for all Pages that have
Book = the book you're interested in
and then order all those Pages by their pageId
and in order, ask for all texts that have
Page = the current page
then order those Texts by their textId.
What might be a problem is that a Text can have multiple Values, as seen in your XML above. You could use this by adding another entity called Value, but I would probably solve it by adding the attributes "value" and "type" to the Text entity directly. (You could then use "value" as a second sort key when printing out a page.
Check out these links:
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/CoreData/
http://developer.apple.com/cocoa/coredatatutorial/index.html (for regular Cocoa, but the same principles hold so this should help)