When will bindElement call the real service? - sapui5

Main.view.xml:
<ObjectHeader
id="objectHeader"
title="{name}"
intro="{id}">
<headerContainer>
<IconTabBar
id="tabBar"
items="{
path: 'assigned',
parameters: {expand: 'b, c'}
}"
select="onTabBarSelect">
</IconTabBar>
</headerContainer>
</ObjectHeader>
Main.controller.js
onMasterSelectionChange : function (sPath) {
var oSource = oEvent.getParameter("listItem") || oEvent.getSource(),
// sPath = "/ASet('d')"
sPath = oSource.getBindingContext().getPath(),
oObjectHeader = this.byId("objectHeader");
oObjectHeader.bindElement(sPath);
this._oModel.attachEventOnce("requestCompleted", function(oEvent) {
var oTabBar = this.byId("TabBar");
oFirstListItem = oTabBar.getItems()[0],
sObjectId = oFirstListItem.getBindingContext().getProperty("id"),
sDPath = "/DSet('" + sObjectId + "')";
this._getControls(sDPath);
}, this);
}
I found a strange behavior: when I change the master list selection, sometimes Network will show a "/ASet/assigned" call, but sometimes there will be no call, which cause requestCompleted not fired.
According to: SAPUI5 - bindElement doesn't work the second time,
If you call bindElement with the same path twice, the second time won't actually trigger a new call to get new data, since the path didn't change.
But I am sure it is different path. Is there any way that can trigger this call?
BTW
oObjectHeader.bindElement({
path : sPath,
events: {
change : function (oData, test) {
},
dataRequested : function (oData) {
},
dataReceived: function (oData) {
}
}
});
change can be fired every time I click master list, but dataRequested and dataReceived is never called.

I know this is an old post but you can actually trigger a network call for the bindElement by refreshing the binding within its change event.
this.getView().bindElement({
path: "model>/" + sContextPath,
events: {
change: function (oEvent) {
// Get the context binding object
var oContextBinding = oEvent.getSource();
// Refresh the binding.
// This triggers a network call.
oContextBinding.refresh(false);
}.bind(this)
}
});
Refreshing the binding, triggers the network call. Also, set the bForceUpdate flag of the refresh to false so that it would only update the UI if there is a change in the data as mentioned in the APIs.

If your ODataModel has already loaded the data under sPath (check in myODataModel.oData) it will not load that data again. Therefore dataRequested/dataReceived will not fire.
But change should fire whenever the path changes no matter if data existed or not.

I find a work around by replace bindElement by change&oModel.read ...
onMasterSelectionChange : function (sPath) {
var oSource = oEvent.getParameter("listItem") || oEvent.getSource(),
// sPath = "/ASet('d')"
sPath = oSource.getBindingContext().getPath(),
oObjectHeader = this.byId("objectHeader");
oObjectHeader.bindElement({
path : sPath,
events: {
change : this._onBindingChange.bind(this, sPath)
}
})
},
_onBindingChange : function(sPath) {
this._oModel.read(sPath + "/assigned", {
urlParameters: {
"$expand": "b, c"
},
success: function(oData) {
var aAssigmentSet = oData.results,
oViewModel = this.getModel("viewModel"),
oTabBar = this.byId("hazardTabBar");
oViewModel.setProperty("/aAssigmentSet", aAssigmentSet);
oTabBar.bindAggregation("items",
"viewModel>/aAssigmentSet",
new sap.m.IconTabFilter({
key: "{viewModel>id}",
tooltip : "{viewModel>id}",
text : {
parts: [{path: "viewModel>b/name"},
{path: "viewModel>c/type"}],
formatter : function(name, type) {
var sType = type.split(".");
return name + " (" + sType[sType.length - 1] + ")";
}
}
})
);
if (oTabBar.getItems().length > 0) {
var sSelectedTabKey;
sSelectedTabKey = oTabBar.getItems()[0].getKey();
oTabBar.fireSelect({selectedKey: sSelectedTabKey});
}
}.bind(this),
error: this.errorCallback.bind(this)
});
},
onTabBarSelect : function(oEvent) {
var sRiskAssignmentPath = "/AssignmentSet('" + oEvent.getParameter("selectedKey") + "')";
this._oModel.read(AssignmentSet + "/assignedAssignedControls", {
...
});
}

I think the propper way to achieve this would be to use the invalidate-Method form Odata-Model.
This invalidates all cached entries and forces a new read for the Bindings:
https://sapui5.hana.ondemand.com/#/api/sap.ui.model.odata.v2.ODataModel%23methods/invalidate

Related

Context passed but ignored

I'm creating an offline app.
When I create an item in a view, I'd like it to be printed in another view so I'm passing a context from a controller to another one thanks to the appView model :
controller where it comes from :
onCreateContainer: function () {
var oView = this.getView(),
containerNumber = oView.byId("addContainerNumberInput").getValue(),
containerWeight = oView.byId("addContainerWeightInput").getValue(),
C = this.getView().getBindingContext();
if (containerNumber !== "" && containerWeight !== "") {
var d = this.getModel("appView").getData();
if (!d["/LotSet('" + this.idParent + "')"]) {
d["/LotSet('" + this.idParent + "')"] = {};
}
d["/LotSet('" + this.idParent + "')"][C.getPath()] = C;
// this.getView().unbindObject();
this._navBack();
} else {
var emptyInputFields = this.getView().getModel("i18n").getResourceBundle().getText("emptyInputFields");
MessageToast.show(emptyInputFields);
return;
}
},
Then I get it back in the second controller :
var oTable = this.getView().byId("contenantsTable");
var appView = this.getModel("appView");
var oLot = appView.getData()["/LotSet('" + sObjectId + "')"];
for (var key in oLot) {
var I = new sap.m.ColumnListItem({
cells: [
sap.ui.xmlfragment(this.getView().getId(), "com.xxx.view.AjoutContenant", this)
]
});
I.setBindingContext(oLot[key]);
oTable.addItem(I);
}
The value of "oLot[key]" is good, it's the context I want to pass, unfortunately, it's completely ignored and the fragment prints elements from the initial context.
Any idea?
I think that the problem is that your are modifying the data of the model directly. The model doesn't know that you are doing it so it is not propagating the changes to the bond components. So you have to either modify the model using setProperty method, or you have to call updateBindings method after you are done modifying the data.

SAPUI5 oData POST 500 error

I'm trying to do a oData create on checkbox pressed and getting the following errors. Not sure if this is front end or a back end ABAP issue as have got this same function working in another project.
It is failing on the create part but strangely is still passing through the details for SiteId, ArticleNumber, VarianceDate & Confirmed.
// Set CheckBox status, X for true, blank for false
onVarianceChecked: function (oEvent) {
var oEntry = {};
var bindingContext = oEvent.getSource().getBindingContext(this.MODEL_VIEW);
var path = bindingContext.getPath();
var object = bindingContext.getModel("SI").getProperty(path);
// Pass in the Header fields
oEntry.SiteId = this.SiteId;
oEntry.ArticleNumber = object.ArticleNumber;
oEntry.VarianceDate = moment(new Date(object.VarianceDate)).format('YYYY-MM-DDTHH:mm:ss');
// Set X or blank
if (oEvent.getParameter("selected") === true) {
oEntry.Confirmed = "X";
} else {
oEntry.Confirmed = "";
}
// Do the create
var oModel = this.getView().getModel("SI");
oModel.create("/VarianceHeaderSet", oEntry, {
success: function () {
console.log("Variance confirmed");
MessageToast.show("Variance confirmed", {
duration: 1000
});
},
error: function (oError) {
console.log("Error, variance could not be confirmed");
MessageToast.show("Error, variance could not be confirmed", {
duration: 1000
});
}
});
}
'000000000' is the initial value for Edm.DateTime, hence it will fail when you have modelled a DateTime property to not be nullable.
Go to SEGW and change the property to "nullable" or make sure that you always provide a correct Date in the POST.

how to put verification in pageobject model in protractor

I have a code (credit to #kishanpatel) Traverse-through-each-row-for-a-column-text which will verify whether the value is added in grid or not. i want to put this in my page object. i was thinking to add the elements into page object and the if condition in a different helper file similar to selenium but i am not sure is that the right appraoch. see the details below.
if I call the mo.helper in spec.ts, it says gridcheck.ispresent() is not a function. How to handle this scenario?
code:
it('verify the grid that master obligation is added', function () {
var testvar = "'test_protractor'";
var row_check = element(by.xpath("//div[contains(text()," + testvar + ")]"));
if (row_check.isPresent()) {
row_check.getText().then(function (msg) {
if (row_check.isPresent()) {
console.log("Grid contains========== " + msg);
}
});
}
});
i have the below method in mo.ts(page object page):
this.grid = function (value) {
// var testvar = "'test_protractor'";
var row_check = element(by.xpath("//div[contains(text()," + value + ")]"));
return require('./mohelper.ts')
}
}
mohelper.ts:
require('../page/mo.ts')
var mohelper = function () {
this.gridvaluepresent = function () {
require('../page/mo.ts')
var gridcheck = mo.grid();
if(gridcheck.isPresent()) {
gridcheck.getText().then(function (msg) {
if (gridcheck.isPresent()) {
console.log("Grid contains========== " + msg);
}
})
}
}
}
module.exports = new mohelper();
spec.ts:
it('go to corresponding module and verify whether the master obligation is added ', function () {
browser.sleep(10000);
taxhome.selectmodule;
taxhome.selectmoduledropdown(1);
mo.grid("test_protractor");
mohelper.gridvaluepresent();
});
Couple of things here to be considered -
1) Most of the protractor's api methods are asynchronous i.e. they return promises you have to resolve/reject them to perform actions.
isPresent() also returns a promise, you need to resolve it-
var row_check = element(by.xpath("//div[contains(text()," + value + ")]"));
row_check.isPresent().then(function(present) {
if(present) { // it returns a boolean value
row_check.getText().then(function (msg) {
console.log("Grid contains========== " + msg);
});
}
});
2) Since you are using TypeScript , use its syntax rather than conventional js-
let row_check = element(by.xpath("//div[contains(text()," + value + ")]")); // Block scoped variable using 'let'
row_check.isPresent().then((present) => { // notice the thick arrow
if(present) {
row_check.getText().then((msg) => {
console.log("Grid contains========== " + msg);
});
}
});
3) Maintain Page Objects efficiently and readable-
All the helper methods, elements etc. for a single page should go in a single page object. Write them in separate classes, typescript uses the concept of classes and transpiles them to global functions.
moHelper.ts
import {ElementFinder, element} from 'protractor';
export class MoHelper {
public row_check: ElementFinder; // its of element finder type
gridValueCheck(value : string) {
row_check = element(by.xpath("//div[contains(text()," + value + ")]")); // please use Css selectors instead of Xpath!
row_check.isPresent().then((present) => {
if(present) {
row_check.getText().then((msg) => {
return msg; // here you are returning the msg of the row from your page!
});
}
});
}
}
Your spec.ts should validate that row msg!
import {MoHelper} from './moHelper.ts'
let mo: MoHelper = new MoHelper();
it('go to corresponding module and verify whether the master obligation is added ', () => {
browser.sleep(10000); // please refrain from using sleeps instead use Expected Conditions
taxhome.selectmodule;
taxhome.selectmoduledropdown(1);
expect(mo.gridValueCheck("test_protractor")).toEqual("Your Expected Message");
});
Please find the links for your reference to understand the above in more detail-
isPresent
Getting started with typescript
Using page objects in protractor/style guide
Expected Conditions

Perform auto-search in ionic with MongoDB as backend

The search should be based on the first name and last name. During entering the letters the middle letters from the word should not come. It should come only only from the first name and last name. I got the auto-complete code from code pen and Github but as per requirement I need auto search;
Like:
Auto Search
You should look at creating your own custom directive or using an existing one like this https://github.com/guylabs/ion-autocomplete
This is my code snippet for auto-complete which I used but I need First name and last name Search (It should not search random letters) -
var airlines = [{"fs":"LCI","iata":"LF","icao":"LCI","name":"Lao Central Airlines ","active":true},{"fs":"TGU","iata":"5U","icao":"TGU","name":"TAG","active":true},{"fs":"BT","iata":"BT","icao":"BTI","name":"Air Baltic","active":true},{"fs":"9J","iata":"9J","icao":"DAN","name":"Dana Airlines","active":true},{"fs":"2O","iata":"2O","icao":"RNE","name":"Island Air Service","active":true},{"fs":"NPT","icao":"NPT","name":"Atlantic Airlines","active":true},{"fs":"C8","iata":"C8","icao":"ICV","name":"Cargolux Italia","active":true},{"fs":"FK","iata":"FK","icao":"WTA","name":"Africa West","active":true},{"fs":"8K","iata":"8K","icao":"EVS","name":"EVAS Air Charters","active":true},{"fs":"W8","iata":"W8","icao":"CJT","name":"Cargojet","active":true},{"fs":"JBW","iata":"3J","icao":"JBW","name":"Jubba Airways (Kenya)","active":true},{"fs":"TNU","iata":"M8","icao":"TNU","name":"TransNusa","active":true},{"fs":"HCC","iata":"HC","icao":"HCC","name":"Holidays Czech Airlines","active":true},{"fs":"APJ","iata":"MM","icao":"APJ","name":"Peach Aviation","active":true},{"fs":"TUY","iata":"L4","icao":"TUY","name":"LTA","active":true},{"fs":"LAE","iata":"L7","icao":"LAE","name":"LANCO","active":true},{"fs":"L5*","iata":"L5","icao":"LTR","name":"Lufttransport","active":true},{"fs":"QA","iata":"QA","icao":"CIM","name":"Cimber","active":true},{"fs":"KBZ","iata":"K7","icao":"KBZ","name":"Air KBZ","active":true},{"fs":"L2","iata":"L2","icao":"LYC","name":"Lynden Air Cargo","active":true},{"fs":"MPK","iata":"I6","icao":"MPK","name":"Air Indus","active":true},{"fs":"CAO","icao":"CAO","name":"Air China Cargo ","active":true},{"fs":"BEK","iata":"Z9","icao":"BEK","name":"Bek Air","active":true},{"fs":"IAE","iata":"IO","icao":"IAE","name":"IrAero","active":true},{"fs":"GL*","iata":"GL","name":"Airglow Aviation Services","active":true},{"fs":"ATN","iata":"8C","icao":"ATN","name":"ATI","active":true},{"fs":"GU","iata":"GU","icao":"GUG","name":"Aviateca Guatemala","active":true},{"fs":"GHY","icao":"GHY","name":"German Sky Airlines ","active":true},{"fs":"SS","iata":"SS","icao":"CRL","name":"Corsair","active":true},{"fs":"XK","iata":"XK","icao":"CCM","name":"Air Corsica","active":true},{"fs":"W9*","iata":"W9","icao":"JAB","name":"Air Bagan","active":true},{"fs":"Z8*","iata":"Z8","icao":"AZN","name":"Amaszonas","active":true},{"fs":"D2","iata":"D2","icao":"SSF","name":"Severstal Aircompany","active":true},{"fs":"SNC","iata":"2Q","icao":"SNC","name":"Air Cargo Carriers","active":true},{"fs":"PST","iata":"7P","icao":"PST","name":"Air Panama","active":true},{"fs":"VV","iata":"VV","icao":"AEW","name":"Aerosvit Airlines","active":true},{"fs":"UJ","iata":"UJ","icao":"LMU","name":"AlMasria","active":true},{"fs":"9U","iata":"9U","icao":"MLD","name":"Air Moldova","active":true},{"fs":"NF","iata":"NF","icao":"AVN","name":"Air Vanuatu","phoneNumber":"678 238 48","active":true},{"fs":"NJS","iata":"NC","icao":"NJS","name":"Cobham Aviation","active":true}];
airlines = airlines.sort(function(a, b) {
var airlineA = a.name.to();
var airlineB = b.name.toLowerCase();
if(airlineA > airlineB) return 1;
if(airlineA < airlineB) return -1;
return 0;
});
console.log(airlines);
angular.module('ionicApp', ['ionic'])
.factory('FlightDataService', function($q, $timeout) {
var searchAirlines = function(searchFilter) {
console.log('Searching airlines for ' + searchFilter);
var deferred = $q.defer();
var matches = airlines.filter( function(airline) {
if(airline.name.toLowerCase().indexOf(searchFilter.toLowerCase()) !== -1 ) return true;
})
$timeout( function(){
deferred.resolve( matches );
}, 100);
return deferred.promise;
};
return {
searchAirlines : searchAirlines
}
})
.controller('MyCtrl', ['$scope', 'FlightDataService', function($scope, FlightDataService) {
$scope.myTitle = 'Auto Complete Example';
$scope.data = { "airlines" : [], "search" : '' };
$scope.search = function() {
FlightDataService.searchAirlines($scope.data.search).then(
function(matches) {
$scope.data.airlines = matches;
}
)
}
}]);

Is there a way to resolve multiple promises with Protractor?

I have this:
element(by.id('x')).sendKeys('xxx').then(function(text) {
element(by.id('y')).sendKeys('yyy').then(function(text) {
element(by.id('z')).sendKeys('zzz').then(function(text) {
expect(element(by.id('myButton')).isEnabled()).toBe(true);
})
});
});
The button 'myButton' is enabled when the elements 'x', 'y' and 'z' all have values. It's my understanding that sendKeys returns a promise.
So is this the only way that I can check if 'myButton' which depends on data in all the three fields is enabled?
You don't need to chain any promises because protractor will wait until all the statements are done: https://github.com/angular/protractor/blob/master/docs/control-flow.md
element(by.id('x')).sendKeys('xxx');
element(by.id('y')).sendKeys('yyy');
element(by.id('z')).sendKeys('zzz');
expect(element(by.id('myButton'));
If you want to resolve multiple promises use:
var webdriver = require('selenium-webdriver');
webdriver.promise.fullyResolved(promises);
For example: https://github.com/angular/protractor/blob/d15d35a82a5a2/lib/protractor.js#L327
this is a bit after the fact, but:
var x = element(by.id('x')).sendKeys('xxx');
var y = element(by.id('y')).sendKeys('yyy');
var z = element(by.id('z')).sendKeys('zzz');
myFun(x,y,z).then(function(){
expect(element(by.id('myButton')).isEnabled()).toBe(true);
});
// in a common function library
function myFun(Xel,Yel,Zel) {
return protractor.promise.all([Xel,Yel,Zel]).then(function(results){
var xText = results[0];
var yText = results[1];
var zText = results[2];
});
}
but an even better way:
var x = element(by.id('x')).sendKeys('xxx');
var y = element(by.id('y')).sendKeys('yyy');
var z = element(by.id('z')).sendKeys('zzz');
myFun(x,y,z);
//isEnabled() is contained in the expect() function, so it'll wait for
// myFun() promise to be fulfilled
expect(element(by.id('myButton')).isEnabled()).toBe(true);
// in a common function library
function myFun(Xel,Yel,Zel) {
return protractor.promise.all([Xel,Yel,Zel]).then(function(results){
var xText = results[0];
var yText = results[1];
var zText = results[2];
});
}
another way is to chain the .thens together:
element(by.id('x')).sendKeys('xxx').
then(function(xtext){
element(by.id('y')).sendKeys('yyy');
}).then(function(ytext){
element(by.id('z')).sendKeys('zzz');
}).then(function(ztext){
expect(element(by.id('myButton')).isEnabled()).toBe(true);
});
it seems protractor supports all - protractor.promise.all
read more at:
https://github.com/angular/protractor/issues/2062#issuecomment-94030055
describe('promise.all', function() {
it('should greet the named user', function() {
browser.get('http://juliemr.github.io/protractor-demo');
$('div').click().then(function () {
return protractor.promise.all([
$('h3').getText(),
$('h4').getText()
]);
}).then(function (params) {
console.log('A');
});
});
it('does something else', function() {
console.log('B');
});
If you want to return an object instead of a list, seems you can also do that - used it and it's awesome
element.all(by.css('.fc-event-inner')).map(function(el) {
return {
time: el.findElement(by.className('fc-event-time')).getText(),
title: el.findElement(by.className('fc-event-title')).getText()
}
});
See the properties are actually promises.. protractor will resolve them.