Just wondering how do I pass a this or other variables in dataRequested and dataReceived in XML events? Something like the below:
<Select items="{
path: '/Countries',
events: {
dataRequested: '.onCountriesRequested($source)',
dataReceived: '.onCountriesReceived($source)',
change: '.onCountriesChange'
}
}">
'this' is the scope of your controller on which your view.xml is attached.
You can directly control everything related to your Select UI element in the related file Detail.controller.js or App.controller.js.
Just add an event listener in your UI element such as:
change="onCountriesChange"
Then define the function "onCountriesChange" in the corresponding controller.js, for example:
onCountriesChange : function(oEvent) {console.log(this);}
Or you can simply bind 'this' to your event handler by attaching it at the end of your function, for example
this.oVariable1 = "Test";
oSelect.attachEvent('dataReceived', function (oEvent) {
//write your logic here using this.oVariable1 for example
}.bind(this));
Related
I have two components, one is parent component and another one is child component which is a modal popup.
Parent component
<parent-component>
<!-- this is a modal popup -->
<child-component v-bind:message="childMessage"></child-component>
Open model
</parent-component>
<script>
export default {
data: function() {
return {
childMessage:{}
}
},
methods:{
openModal: function(id){
axios.get('api/message/'+id)
.then(response => {
this.childMessage = response.data;
})
.catch(error => {
console.log(error);
})
this.showModal = true
}
}
}
</script>
Child component
<!-- this is a popup modal -->
<child-component>
<h1>{{ message.title }}</h1>
</child-component>
<script>
export default {
props:{
message: {},
}
</script>
In parent component, I trigger the modal and request ajax at the same time.
And I can pass the ajax data to child component correctly.
But if I open the console, there is an error
Cannot read property 'title' of undefined
(Although I can see the data is working fine and it's already in html page)
It seems the appending data {{ childMessage.title }} run first (before the ajax request).
1 - How can I append the data correctly, probably after the ajax request.
2 - Do I need to check the condition for undefined value?
I don't see where you use showModal but I suppose it's a sort of switch to display or not the child-component. If it's the case the error can come from the fact that you set showModal to true just after the call to the API. But this call is asynchronous you should probably move this.showModal = true in the success callback under this.childMessage = response.data;.
If you do that the message prop will be initialize at the moment the child component is rendered.
Also pay attention to your prop type, as #ittus mention message seems to be a String according to the default value in the child-component but you use it like an object in the template.
In this case, you have to check the condition for undefined value because child component is being rendered before the message API call ends. You can do it in this way,
<child-component>
<h1 v-if = "message">{{ message.title }}</h1>
</child-component>
I would like to check for data changes when the user clicks on the back button in the Fiori Launchpad. I have the following code
onAfterRendering: function() {
sap.ui.getCore().byId("backBtn").attachPress(this, function(oEvent) {
oEvent.preventDefault();
});
}
Within the function I would like to access the oData and other variables of the main controller. However when I press the back button the "this" object is the header control's view.
How can get the view of the page content and also access the oData and other parameters of the controller associated to the content view.
In order to access the current context you have to call the event handler function within that specific context, so therefore a binding is needed on that function.
onAfterRendering: function() {
sap.ui.getCore().byId("backBtn").attachPress(this, function(oEvent) {
oEvent.preventDefault();
}.bind(this));
}
If usage of sap.m.Page is an option than navButtonPress builtin event can be used:
View.xml:
<mvc:View xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc">
<Page navButtonPress="onNavBack">
...
Controller.js:
onNavBack: function(oControlEvent) {
var oController = this;
var oView = this.getView();
}
the event listener will be triggered every time the button is pressed in the Fiori Launchpad header.
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.
}
When using the WorkList (and even Master-detail) templates you have the following event in the onInit function:
oTable.attachEventOnce("updateFinished", function() {
// Restore original busy indicator delay for worklist's table
oViewModel.setProperty("/tableBusyDelay", iOriginalBusyDelay);
});
In the view.xml you also have the eventHandler for updateFinished which you can set, so that you are able to do stuff when the data is received in your list.
In the PlanningCalendar you don't have such an eventhandler, how do we handle these kind of things for such a component?
The logic I'm trying to implement is the following:
<PlanningCalendar
id="PC1"
rows="{
path: '/DeveloperSet'
}"
viewKey="Day"
busyIndicatorDelay="{planningView>/calendarBusyDelay}"
noDataText="{planningView>/calendarNoDataText}"
appointmentSelect="onAppointmentSelect"
rowSelectionChange="onDeveloperRowChange"
startDateChange="onStartDateChange">
<toolbarContent>
<Title
text="Title"
titleStyle="H4" />
<ToolbarSpacer />
<Button
id="bLegend"
icon="sap-icon://legend"
type="Transparant"
press="onShowlegend" />
</toolbarContent>
<rows>
<PlanningCalendarRow
icon="{Pic}"
title="{Name}"
text="{Role}" />
</rows>
</PlanningCalendar>
I want to load and add the "appointments" only for the visible part (filter on start and endDate) of the calendar, so I want to perform the oDataModel.read-calls myself. But the rows (the DeveloperSet) should always remain the same. So I should be able to "wait" until the calendar has the data/rows filled in the calendar and then do my manual calls to retrieve the appointments.
So I need to be able to do something when the data is retrieved, but there is no updateFinished event for a calendar?
Does anybody have an idea on how to solve this?
the event "updateFinished" when used in the Table or List is triggered from method updateList, this method handles the update of aggregation "list"
PlanningCalendar does not have an updateRows method, therefore no event "updateFinished"
You could listen to the dataReceived event on the Row binding, if you have one
OnInit: function(){
...
this.oPlanningCalendar = this.byId("PC1")
var oBinding = oPlanningCalendar.getBinding("rows");
oBinding.attachDataReceived(this.fnDataReceived, this);
else you can extend the control and add your own updateRows method and fire "updateFinished", the hack test below shows it would work
OnInit: function(){
...
this.oPlanningCalendar = this.byId("PC1");
this.oPlanningCalendar.updateRows = function(sReason) {
this.oPlanningCalendar.updateAggregation("rows");
var oBinding = this.oPlanningCalendar.getBinding("rows");
if (oBinding) {
jQuery.sap.log.info("max rows = " + oBinding.getLength() || 0);
}
}.bind(this);
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).