BindElement() on form in popup dialog / fragment - sapui5

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

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.

Forms in reactjs with flux

I have a form, this form needs to post some data to my backend. With flux, what is the best practice for doing this, use a store?
My issue with using a store is that I have a sub component inside of my form that allows me to select a number 1-5 with buttons. I wanted that component to be reusable, but if i use a store, I have to hard code the store into the child component which means I cant really use it elsewhere. Instead do I just set the parent state from the child?
If anyone can point out some good tutorials or examples of react/flux forms let me know.
In my opinion any back end interaction should be done by using actions, but...
if you want to use store anyway then you can create additional attribute (prop) in your sub-component which will be a function (f.e. onChange) which should be passed from parent component as prop (this function should set data in store). Then you can reuse this component, because only parent needs to have access to store.
So in subcomponent:
onButtonClick(e) {
this.state.value = e.target.value;
if (this.props.onChange) this.props.onChange(e.target.value);
}
<div>
<button onClick={this.onButtonClick.bind(this)} value="1">1</button>
<button onClick={this.onButtonClick.bind(this)} value="2">2</button>
<button onClick={this.onButtonClick.bind(this)} value="3">3</button>
<button onClick={this.onButtonClick.bind(this)} value="4">4</button>
<button onClick={this.onButtonClick.bind(this)} value="5">5</button>
</div>
and in parent:
setMyStoreState(value) {
store.setNumber(value);
}
<Subcomponent onChange={this.setStoreState.bind(this)} />
or something like this.
Code not tested, but you should get the idea.

how to pass data from view to controller in SAPUI5

I have a view in my sapui5 app, where on a button press I want to pass some data to the controller, to the function invoked on the press event.
Below is the code snippet :
<HBox justifyContent="SpaceAround" alignItems="Center" >
<Input type="Tel" pattern="[0-9]*" inputmode="numeric"
value="{path:'cart>Quantity/value',
type: 'sap.ui.model.type.Integer'}"
class="qtyInput" editable="{cart>Quantity/isEditable}"/>
<core:Icon src="sap-icon://delete" press="deleteItem" visible="{cart>isDeletable}"/>
</HBox>
Here, I need to pass "{cart>lineNumber}" and ”{cart>itemKey}" to the function “deleteItem” which is there in the controller.
Please suggest.
You can try using sapui5 CustomData to pass your custom data on a event.
For that, you need to add below namespace in your view:
xmlns:app="http://schemas.sap.com/sapui5/extension/sap.ui.core.CustomData/1"
and add app:propertyName=“value” inside the Icon element.
Please take a look at below example, I updated your code with the changes required:
<core:Icon src="sap-icon://delete" press="deleteItem" visible="{cart>isDeletable}" app:lineNumber="{cart>lineNumber}" app:itemKey="{cart>itemKey}"/>
Thanks.
Another way to resolve this problem is, if you are getting the data from the same model on which the list is being iterated, you may get the index number of the list item and then read the specific record from the model itself using the index number.
Let me know if you need a code example for this.

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.