SAPUI5 binding OData v4 - sapui5

I'm currently using OData v4 requests from my CAP app, and the problem is how to bind my Entity requested data in my Simple Form.
The user must input a Workspace code, and Search for its values.
Please, how do I bind and display my Workspace name and description values to my Simple Form fields to be displayed on screen?
Workspace CAP entity data:
{
"#odata.context": "$metadata#Workspace/$entity",
"name": "Projeto Compra de Material Escritorio",
"description": "",
"projectState": "Active",
"testProject": "false",
"version": "Original",
"baseLanguage": "pt"
}
in the onInit app function
let oModel = new sap.ui.model.odata.v4ODataModel({
groupId : "$auto",
synchronizationMode : "None",
serviceUrl : "/myCAP_URL/"
in my press event button
let oModel = this.getView().getModel();
let oContextBinding = oModel.bindContext(`/Workspace/${workspaceId}`);
oContextBinding.requestObject("name").then(function (sName) {
if (!sName) {
oContextBinding.getBoundContext().setProperty("name", "No name");
}
});
Finally, that`s my Simple Form fields (XML)
<Button id="button0" press="onPress" text="Search"/>
<f:SimpleForm editable="true" layout="ResponsiveGridLayout" id="form0">
<f:content>
<sap.ui.core:Title text="{description}" id="title2"/>
<Label text="Name" id="label0"/>
<Input width="30%" id="input0" value="{name}"/>
<Label text="Language" id="label1"/>
<Input width="30%" id="input2" value="{baseLanguage}"/>
</f:content>
</f:SimpleForm>

I've managed to bind and display data, based on this piece of code. I`ve just made some adjustments.
oContextBinding.requestObject("name").then(function(sName) {
if (!sName) {
oContextBinding.getBoundContext().setProperty("name", "No name");
}
});

Related

SAP UI5 : Unable to bind a controller variable in xml

Button on click of which the variable is getting changed:
<f:content>
<Button icon="sap-icon://edit" press="editClick" type="Transparent"></Button>
</f:content>
xml code where i need 2-way binding:
<VBox class="sapUiSmallMargin">
<form:SimpleForm id="SimpleFormDisplayColumn_oneGroup">
<form:content>
<Label text="{i18n>contextofusage}"/>
<Text text="{contextofusagetext}" visible="{!isInfoEditable}"/>
<Input type="Text" visible="{isInfoEditable}" value="{contextofusagetext}"></Input>
</form:content>
</form:SimpleForm>
</VBox>
controller:
var isInfoEditable=false;
return Controller.extend("abc.controller.Detail", {
editInfoClick: function(event){
if(isInfoEditable){
isInfoEditable=false;
}
else{
isInfoEditable=true;
}
}
});
It seems like you have tried nothing, but I will try to guide you:
You cannot simply declare a variable in your controller and then use it in your view. That's not how UI5 works. Instead create a model and bind it to your view. I also give my models a name, in this case "view":
onInit: function() {
var oViewModel = new sap.ui.model.json.JSONModel({
isInfoEditable: false
});
this.getView().setModel(oViewModel, "view");
}
Then use it in your view. Make sure that you use the name of your model ("view") in your bindings. If you want to do more than simply use the raw value (e.g. negate it) you have to use expression binding. Also you have to use an absolute path starting with /:
<form:SimpleForm id="SimpleFormDisplayColumn_oneGroup">
<form:content>
<Label text="{i18n>contextofusage}"/>
<Text text="{contextofusagetext}" visible="{= !${view>/isInfoEditable} }"/>
<Input type="Text" visible="{view>/isInfoEditable}" value="{contextofusagetext}"></Input>
</form:content>
</form:SimpleForm>
When you click on the button you have to modify/toggle the value in your model:
editClick: function (oEvent) {
var oViewModel = this.getView().getModel("view");
var bIsEditable = oViewModel.getProperty("/isInfoEditable");
// negate the current value and set it as the new value
oViewModel.setProperty("/isInfoEditable", !bIsEditable);
}
I suggest strongly reading on the basics of UI5. Your approach looks more like Vue.js, but UI5 has its own paradigms.

Vue reactive forms components communication

I have multiple form components, each form as a component. Now, I want to use same component for adding data and editing data. So what I am thinking to do is something like when the Post component receives a prop containing data that means it is in a "editing mode" and populate the fields with its data, if not it is in "create mode".
So how should I use v-model in my form fields?
Should I v-model each form field to a computed property (which has a getter and a setter) and the computed property would check if the data prop is empty and if not use its data to populate fields ? And in the computed property set method to update the prop ?
parent component
<post :data.sync="dataObject"></post>
child (Post) component
<template>
<div>
<form>
<input type="text" label="title" v-model="computedTitle" />
<input type="text" label="message" v-model="computedMessage" />
</form>
</div>
<input type="button" #click="submitted"
<template>
<script>
export default {
data(){
return{
post:{
title:null,
message:null
}
}
},
props:["data"],
computed:{
computedTitle:{
get(){
return data ? data.title : ''
},
set(computedTitle){
computedTitle = computedTitle // trying to update computed property value with the field value...
}
},
computedMessage:{...}
}
}
</script>
You can use watch to check data prop, if it's set then set to local post variable.
If created, then data is null, post.title and post.message are set to null
If updated, then data is not null, post.title is set to data.title and post.message to set to data.message
<template>
<div>
<form>
<input type="text" label="title" v-model="post.title" />
<input type="text" label="message" v-model="post.message" />
</form>
</div>
<input type="button" #click="submitted"
<template>
<script>
export default {
data() {
return{
post: {
title: null,
message: null
}
}
},
props:["data"],
watch: {
data: {
handler(newData) {
if (newData) {
this.post = {
title: newData.title,
message: newData.message
}
}
},
immediate: true // this makes watch is called when component created
}
}
}
</script>
Note that you should use immediate: true to make the watch's function called when component is created

How to add control to items statically

Sometimes in an aggregation binding, I need to add some controls to the generated list of items statically. Is there some elegant solution for this?
Let's say I have a Select with the following code:
<Select width="100%"
items="{project>/Milestones}"
selectedKey="0"
>
<core:Item
key="{project>Id}"
text="{project>Name}"
/>
</Select>
Bound to a model with these data:
{
Milestones: [
{
"Id": 1,
"Name": "Cost Estimation 1",
"Description": "Initial cost estimation"
},
{
"Id": 2,
"Name": "Pid Packages",
"Description": "Ready for RFQs"
},
...
]
}
I want to add an item to the Select with key="0" and value="< Blank >" and have that stay there even when the content of project>/Milestones is changed, but I don't want to add it to the actual aggregation.
The solutions that I have at the moment seem really hackish and create problems later on: creating a new model (property) leads to having the data duplicated in multiple lists and therefor it will probably get out of sync at some point. I've also tried adding the static items through binding events, but this is somewhat error prone and very verbose.
Ok here the promised snippet. First of all, if you just want to add a "blank" item, I recommend you to use ComboBox instead of Select, because you can always delete your selection. I added it to the snippet as well.
But if you want to add an item, you just need to use the addItem() function described here
I also added a button to modify your model, so you can see how the '< Blank >' item remains there even when you change it.
Snippet
<!DOCTYPE html>
<html>
<head>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta charset="utf-8">
<title>MVC with XmlView</title>
<!-- Load UI5, select "blue crystal" theme and the "sap.m" control library -->
<script id='sap-ui-bootstrap' src='https://sapui5.hana.ondemand.com/resources/sap-ui-core.js' data-sap-ui-theme='sap_belize_plus' data-sap-ui-libs='sap.m' data-sap-ui-xx-bindingSyntax='complex'></script>
<!-- DEFINE RE-USE COMPONENTS - NORMALLY DONE IN SEPARATE FILES -->
<script id="view1" type="sapui5/xmlview">
<mvc:View xmlns="sap.m" xmlns:core="sap.ui.core" xmlns:mvc="sap.ui.core.mvc" controllerName="my.own.controller">
<Title text="With Select" class="sapUiMediumMarginTop"></Title>
<Select id="mySelect" width="100%" items="{ path: 'project>/Milestones', events:{ change: '.onSelectBindingChange' }, templateShareable: false}" selectedKey="0">
<core:Item key="{project>Id}" text="{project>Name}" />
</Select>
<Title text="With ComboBox" class="sapUiMediumMarginTop"></Title>
<ComboBox width="100%" items="{ path: 'project>/Milestones', templateShareable: false}" selectedKey="0">
<core:Item key="{project>Id}" text="{project>Name}" />
</ComboBox>
<Button text="Modify Model" press="onButtonPressed" class="sapUiLargeMarginTop sapUiLargeMarginBottom"></Button>
</mvc:View>
</script>
<script>
// define a new (simple) Controller type
sap.ui.controller("my.own.controller", {
onSelectBindingChange: function(oEvent) {
var oNewItem = new sap.ui.core.Item({
key: 0,
text: "< Blank >"
});
this.getView().byId("mySelect").addItem(oNewItem);
},
onButtonPressed: function(oEvent) {
var aMilestones = this.getView().getModel("project").getProperty("/Milestones");
aMilestones.push({
Id: 4,
Name: "New Milestone",
Description: "Just a model modification"
});
this.getView().getModel("project").setProperty("/Milestones", aMilestones);
}
});
/*** THIS IS THE "APPLICATION" CODE ***/
// create some dummy JSON data
var data = {
Milestones: [{
"Id": 1,
"Name": "Cost Estimation 1",
"Description": "Initial cost estimation",
},
{
"Id": 2,
"Name": "Pid Packages",
"Description": "Ready for RFQs",
},
{
"Id": 3,
"Name": "Certificate Check",
"Description": null,
}
]
};
var oJSONModel = new sap.ui.model.json.JSONModel();
oJSONModel.setData(data);
// instantiate the View
var myView = sap.ui.xmlview({
viewContent: jQuery('#view1').html()
}); // accessing the HTML inside the script tag above
myView.setModel(oJSONModel, "project");
// put the View onto the screen
myView.placeAt('content');
</script>
</head>
<body id='content' class='sapUiBody'>
</body>
</html>

Data Binding from TableSelectDialog to Form

I'm using TableSelectDialog to view some Carrier details. All I need is: If I select any item (row), then all the values from that row should get automatically populated to the form input fields.
In the attached image, if Carrier Name is selected from TableSelectDialog, then the remaining fields should be populated based on that value.
You can achieve the required functionality using the Binding context that you receive from the TableSelcect Dialog.
But for binding context to work properly, both form and the table select dialog should refer to the same Model.
Below is the working code:
VIEW.XML:
Has a Button to trigger the table select dialog.
Has a form.
<l:VerticalLayout class="sapUiContentPadding" width="100%">
<l:content>
<Button class="sapUiSmallMarginBottom" text="Show Table Select Dialog"
press="handleTableSelectDialogPress">
</Button>
<VBox class="sapUiSmallMargin">
<f:SimpleForm id="SimpleFormDisplay354">
<f:content>
<Label text="Supplier Name" />
<Text id="nameText" text="{SupplierName}" />
<Label text="Description" />
<Text text="{Description}" />
<Label text="ProductId" />
<Text text="{ProductId}" />
<Label text="Quantity" />
<Text id="countryText" text="{Quantity}" />
</f:content>
</f:SimpleForm>
</VBox>
</l:content>
</l:VerticalLayout>
Controller:
onInit: function () {
// set explored app's demo model on this sample
var oModel = new JSONModel(jQuery.sap.getModulePath("sap.ui.demo.mock", "/products.json"));
this.getView().setModel(oModel);
},
handleTableSelectDialogPress: function (oEvent) {
if (!this._oDialog) {
this._oDialog = sap.ui.xmlfragment("sap.m.sample.TableSelectDialog.Dialog", this);
}
this.getView().addDependent(this._oDialog);
// toggle compact style
this._oDialog.open();
},
handleClose: function (oEvent) {
var aContexts = oEvent.getParameter("selectedContexts");
if (aContexts && aContexts.length) {
// MessageToast.show("You have chosen " + aContexts.map(function(oContext) { return oContext.getObject().Name; }).join(", "));
this.byId('SimpleFormDisplay354').setBindingContext(aContexts[0]);
}
oEvent.getSource().getBinding("items").filter([]);
}
Now, we also have a Select dialog and on click of any Item we call method : handleClose
In handleClose, we obtain the clicked Item binding context and then tell the form : hey! refer to this context ( which is present in the model). Binding context has a path which tells the form from where to do the relative binding.
Please feel free to contact for more information.

Angular2 Radio buttons and Checkbox binding

I am currently using beta.17 within this web application i am building, as it had issues when trying to upgrade. But anyway thats not the issue.
I have a Model form, and I am trying to use Radio Buttons, and I know there was always issues with it until the fix on rc2.
But here is my section of the form using them:
<div *ngFor="let ctrl of markingForm.controls['design'].controls; let i = index;" [ngFormModel]="ctrl">
<accordion-group [heading]="ctrl.value.name" >
<div *ngFor="let design of studentAssign.assID[0].template[0].design; let j = index">
<label class="form-check-inline" *ngFor="let comment of design.comments">
<input name="input" class="form-check-input" type="radio" value="test" ngControl="comment">{{comment.short}}
</label>
</div>
</accordion-group>
</div>
I am trying to bind the value to 'comment' within this part of the ControlArray:
"design": [
{
"name": "Rubric Section Name",
"comment": null,
"mark": null,
"maxMark": 50
},
{
"name": "ttt",
"comment": null,
"mark": null,
"maxMark": 20
}
]
(the 'design' part is populated before hand and could be X amount big).
So realistically it should work right?
But i get this error:
ORIGINAL EXCEPTION: TypeError: Cannot read property 'value' of null
ORIGINAL STACKTRACE:
TypeError: Cannot read property 'value' of null
at RadioControlValueAccessor.onChange
Is there any way around this? Or is there a fix or do I need to upgrade to rc2?
Or can the radio buttons be easily replicated with checkboxes?