How to handle calling a function without oEvent - sapui5

I have a CheckBox with a handler attached to the select event. In this function is the code to dynamically populate/ display few fields. If I come on the screen and the data brings in a value which makes the checkbox selected already, then those fields are not displayed (because they become visible only when I select the checkbox).
I want to ensure that if the CheckBox is auto selected, still I should be able to process the logic in the function, which has oEvent as an input parameter. But the issue is that if I call this function from another method, that function does not work as it has many statements like oEvent().getSource() which I do not pass.
Controller.js
onCheckBoxSelect: function(oEvent) {
var cells = sap.ui.getCore().byId("cell");
controlCell.destroyContent();
vc.abc();
var material= sap.ui.getCore().byId("abc");
var isSelected = oEvent.getParameters("selected").selected;
if (isSelected) {
// ...
}
},
someFunction : function(){
if(true){
// want to call onCheckBoxSelect here
}
// ...
},

If you assign an ID to your checkbox, you can get the checkbox in any function you want as long as it is known in the view. By doing that you won't need the oEvent which is only available when an event on the checkbox is executed.
Example:
var cb = this.byId('checkboxId');
if(cb.getProperty('selected')) {
// execute code
} else {
// do something else
}

Decouple the handler body into a separate function so that other functions can call the decoupled function with the right arguments. For example:
Controller
onCheckBoxSelect: function(oEvent) {
const bSelected = oEvent.getParameter("selected");
this.doIt(bSelected); // Instead of "doing it" all here
},
someFunction: function(){
if (/*Something truthy*/) {
const checkBox = this.byId("myCheckBox");
const bSelected = checkBox.getSelected();
doIt(bSelected); // passing the same argument as in onCheckBoxSelect
}
// ...
},
doIt: function(bSelected) { // decoupled from onCheckBoxSelect
// ...
if (bSelected) {
// ...
}
},
View
<CheckBox id="myCheckBox"
select=".onCheckBoxSelect"
/>
Or since 1.56:
<CheckBox id="myCheckBox"
select=".doIt(${$parameters>/selected})"
/>
Docu: Handling Events in XML Views
By that, you can have a pure, decoupled function that can be called from anywhere.

I would suggest a different approach. Use the same property that you have used in your checkbox binding, to determine the visibility of the other fields, i.e. bind the visible property of each relevant field to that property in your model.
If there is additional post-processing required in populating the fields, you can either use expression binding or custom formatter for field-specific processing, or model binding events if you need to do a bit more "staging" work (in which case you would probably store the resultant data in a client model and bind to that for populating your fields).

Related

How to check in protractor is mat-select disable, if there is no "disabled" in snippet?

I work with tables in page objects and want to avoid getAttribute(mat-select-disabled), just isEnabled(), as other elements such as inputs and checkboxes, which have "disabled"
<mat-select class="mat-select mat-select-disabled formcontrolname="xxx" role="listbox" aria-disabled="true"></mat-select>
There is not built on method for this and you can not avoid the getAttribute method because your element does not have the disabled attribute, just only has a class name.
But you can always create your own, something like this:
// async/await
async function isElementDisabled(elem) { #elem <ElementFinder>
const classNames = await elem.getAttribute('class');
return classNames.includes('mat-select-disabled');
}
// Promise based
function isElementDisabled(elem) { // #elem <ElementFinder>
return elem.getAttribute('class').then(classNames => classNames.includes('mat-select-disabled'));
}

How to access control from the popup fragment by ID

I want my text area to be empty after I press OK button.
I have try this line this.byId("id").setValue("")
onWorkInProgress: function (oEvent) {
if (!this._oOnWorkInProgressDialog) {
this._oOnWorkInProgressDialog = sap.ui.xmlfragment("WIPworklist", "com.sap.FinalAssestments.view.WorkInProgress", this);
//this.byId("WIP").value = "";
//this.byId("WIP").setValue();
this.getView().addDependent(this._oOnWorkInProgressDialog);
}
var bindingPath = oEvent.getSource().getBindingContext().getPath();
this._oOnWorkInProgressDialog.bindElement(bindingPath);
this._oOnWorkInProgressDialog.open();
},
//function when cancel button inside the fragments is triggered
onCancelApproval: function() {
this._oOnWorkInProgressDialog.close();
},
//function when approval button inside the fragments is triggered
onWIPApproval: function() {
this._oOnWorkInProgressDialog.close();
var message = this.getView().getModel("i18n").getResourceBundle().getText("wipSuccess");
MessageToast.show(message);
},
The text area will be in popup in the fragment. I am expecting the text area to be empty.
If you instantiate your fragment like this:
sap.ui.xmlfragment("WIPworklist", "com.sap.FinalAssestments.view.WorkInProgress", this);
You can access its controls like this:
Fragment.byId("WIPworklist", "WIP").setValue(""); // Fragment required from "sap/ui/core/Fragment"
Source: How to Access Elements from XML Fragment by ID
The better approach would be to use a view model. The model should have a property textAreaValue or something like that.
Then bind that property to your TextArea (<TextArea value="{view>/textAreaValue}" />). If you change the value using code (e.g. this.getView().getModel("view").setProperty("/textAreaValue", "")), it will automatically show the new value in your popup.
And it works both ways: if a user changes the text, it will be automatically updated in the view model, so you can access the new value using this.getView().getModel("view").getProperty("/textAreaValue");.
You almost have it, I think. Just put the
this.byId("WIP").setValue("") line after the if() block. Since you are adding the fragment as a dependent of your view, this.byId("WIP") will find the control with id "WIP" every time you open the WIP fragment and set its value to blank.
You are likely not achieving it now because A. it is not yet a dependent of your view and B. it is only getting fired on the first go-around.

Event Handler for JSONModel Change?

Say, there's an sap.m.table whose items are bound to a JSON model - "/rows". Outside sap.m.table layout, there's a toolbar that contains "Add" button to add rows to the table. "Add" button adds rows to the table using model's setProperty method. Now, the requirement is to disable "Add" button when JSON model "/rows" length has reached 10. How do we create a handler to observe the changes of JSON model's "/rows" property? https://sapui5.netweaver.ondemand.com/1.52.22/#/api/sap.ui.model.Model/events/propertyChange states that
Currently the event is only fired with reason sap.ui.model.ChangeReason.Binding which is fired when two way changes occur to a value of a property binding.
This means that the eventHandler of propertyChange doesn't get triggered when JSONModel's setProperty() is called. Is there a way out where we can observe the changes of JSONModel's property changes - in this case, "/rows" property of the JSONModel?
Well I can think of several ways to achieve this
1. Standard view binding + formatter:
View
...
<Button text="Add" press="onPressAdd" enabled="{path: '/rows', formatter: '.isAddEnabled'}" />
...
Controller:
Controller.prototype.isAddEnabled = function(rows) {
return rows && rows.length < 10;
}
2. Expression binding (pure xml)
...
<Button text="Add" press="onPressAdd" enabled="{= ${/rows/length} < 10 }" />
...
3. JSONPropertyBinding (pure javascript)
You can call bindProperty on JSONModel to create a property binding that can be observed for changes:
https://sapui5.hana.ondemand.com/#/api/sap.ui.model.Model/methods/bindProperty
https://sapui5.hana.ondemand.com/#/api/sap.ui.model.json.JSONPropertyBinding
Controller.prototype.onInit = function() {
var model = this.getMyJsonModel();
var button = this.getView().byId("myButtonId");
model.bindProperty("/rows").attachChange(function(event) {
button.setEnabled(event.getSource().getValue().length < 10);
})
}

Why local Unique IDs inconsistent with IDs in HCP?

I have inspect the element in app that run local in HCP, the id is application-MaintainMasterData-display-component---addRoute--form, but when I deploy to cloud, the id changed to application-MaintainFleet-Display-component---addRoute--form
The app name changed, and the display in the upper class way, which makes my sap.ui.getCore().byId() failed in cloud. I was confussing why this happens.
I've read the ref, I was in a Event handler, I need the oEvent scope, so this.getView().byId() and this.createId() won't works for me.
Ref:
sap.ui.getCore().byId() returns no element
https://sapui5.netweaver.ondemand.com/sdk/#docs/guide/91f28be26f4d1014b6dd926db0e91070.html
=========UPDATE=========
I also tried sap.ui.getCore().byId("application-MaintainMasterData-display-component---addRoute").byId("form") , but the same issue , view id is application-MaintainFleet-Display-component---addRoute in cloud.
The IDs are dynamically generated. So you cannot rely on them. That's why you should not use sap.ui.getCore().byId(). Even the separators -- and --- may change in the future.
You should always use the byId() method of the nearest view or component to resolve local ids. You can chain the calls: component.byId("myView").byId("myControl")
In your eventhandler this should refer to the controller. For XMLViews this should be the case without further doing.
So i guess you are using JSViews? If you attach an eventhandler in code you can always supply a second argument to the attachWhatever() functions: The Object that becomes this in the event handler.
Controller.extend("myView", {
onInit:function(){
var button = this.byId("button1");
button.attachPress(this.onButtonPress, this); //The second parameter will become 'this' in the onButtonPress function
},
onButtonPress: function(oEvent){
console.log(this); //this is the controller
var buttonPressed = oEvent.getSource(); //get the control that triggered the event.
var otherControl = this.byId("table"); //access other controls by id
var view = this.getView(); //access the view
}
});
If you are using the settings-object-syntax you can supply an array for the events. It should contain the handler function and the object that should become this:
createContent:function(oController){
return new Button({
text: "Hello World",
press: [
function(oEvent){ console.log(this); }, //the event handler
oController //oController will be 'this' in the function above
]
});
If you are attaching to a non-UI5-event you can always use a closure to supply the view or controller to the handler function:
onInit:function(){
var that = this; //save controller reference in local variable
something.on("event", function(){ console.log(that); });
//you can use that local variable inside the eventhandler functions code.
}

backgrid.js - how to prevent multi-row selection?

I am new to backgrid and using it in a form to allow the user to select a row (via a checkbox) and then click "Submit". I cannot figure out how to configure my grid to behave like "radio buttons" in that only one row can be selected. Is this something backgrid natively supports or do I need to write a handler to "unselect" previously selected rows?
Here is a quick-n-dirty method:
wellCollection.on('backgrid:selected', function(model, selected) {
if (wellGrid.getSelectedModels().length > 1) {
model.trigger("backgrid:select", model, false);
alert('Only one selection is allowed.');
}
});
The downside is this approach requires the use of "SelectAll" which is really counter-intuitive to the user. I would prefer to be able to not use "SelectAll" but it is required to get the getSelectedModels object populated.
You could create a custom cell that renders radio buttons. The implementation below may need some more work but something like this will get you started:
var RadioCell = Backgrid.Cell.extend({
events: {
"click .selectedRadio": "makeSelected"
},
render: function () {
this.template = Mustache.to_html($("#radioCell").html(), this.model.toJSON());
this.$el.html(this.template);
this.delegateEvents();
return this;
},
makeSelected: function () {
// set all to inactive
this.model.collection.invoke('set', { "SomeModelAttrib": false });
// check if radio is checked and set the value on the model
var radioValue = $("input[name='someSelection']").is(":checked");
this.model.set("SomeModelAttrib", radioValue);
}
});
and the mustache template:
<script type="text/template" id="radioCell">
<input type='radio' class='selectedRadio' name='someSelection' value="{{SomeModelAttrib}}" {{#SomeModelAttrib}}checked{{/SomeModelAttrib}}>
</script>