How to pass a key field as variable instead of hard-coded key value to OData operation? - sapui5

I am calling the GetEntity OData read method from the SAP UI5 view controller and passing a key value in the request URL. I am getting the proper response from the back-end when I hardcode the key value.
However, when I try to pass the key value dynamically in a variable by appending it to the URL, it doesn't work. I get the following error
HTTP request failed 404
In below code, sGrant is the variable and it doesn't work. But if I replace the variable name with its value hard-coded in below code, for example, in the read method like this: "/GrantMasterSet('TY560003')", then it works:
var sGrant = this.byId("grantNbr").getValue();
var oMod = this.getOwnerComponent().getModel();
oMod.read("/GrantMasterSet('sGrant')", {
success: function(oData) {
var oJsonModel = new JSONModel();
oJsonModel.setData(oData);
this.getView().setModel(oJsonModel);
}.bind(this),
error: function(oError) {
MessageToast.show("Read Failed");
}
});

UI5 has a method to generate the right URI for you, no matter what is the data type of the key of your entity type.
The method is createKey of the sap.ui.model.odata.v2.ODataModel class. See its documentation
Inside your controller, use the following source code.
onInit: function () {
var oRouter = this.getOwnerComponent().getRouter();
oRouter.getRoute("routeName").attachPatternMatched( this.onPatternMatched , this );
},
onPatternMatched: function(oEvent){
var oParameters = oEvent.getParameters();
var oArguments = oParameters.arguments; // is not a function - without ()
var sKey = oArguments.id; // route parameter passed when using navTo
var oDataModel = this.getView().getModel(); // v2.ODataModel
oDataModel.metadataLoaded().then(function() {
var sPath = oDataModel.createKey("EntitySet", { Key: sKey });
this.getView().bindElement("/" + sPath);
}.bind(this)
);
}
Usually this is necessary in details pages, in order to apply element binding to a page. As the createKey method relies on the $metadata of your service, you must make sure that it is already loaded in your app. This can be achieved by using method metadataLoaded, provided in the snippet as well.

You should concatenate the variable to the rest of the string, like this:
oMod.read("/GrantMasterSet('" + sGrant + "')", {
Or, you can use a template literal, which comes down to the same thing (notice the backtics):
oMod.read(`/GrantMasterSet('${sGrant}')`, {

You should escape 'sGrant' so it can be evaluated.
It should be something like that :
var sGrant = this.byId("grantNbr").getValue();
var oMod = this.getOwnerComponent().getModel();
oMod.read("/GrantMasterSet("+sGrant+")", {
success: function(oData) {
var oJsonModel = new sap.ui.model.json.JSONModel();
oJsonModel.setData(oData);
this.getView().setModel(oJsonModel);
}.bind(this),
error: function(oError) {
MessageToast.show("Read Failed");
}
});

Related

SAPUI5: getModel returns undefined if called within the same function of setModel

I'm trying to set a model and retrieving it from OData after pressing a certain button.
The problem is when I call getModel right after setting the model, it returns undefined.
However, if I call getModel from another function (after model being stetted from other functions), it returns the desired output.
Code for reference:
onPressButton1: function(){
var vEntityURL = "/CustomerSet(ID='000')";
var sServiceUrl = "/Customers_SRV/";
var oServiceModel = new sap.ui.model.odata.ODataModel(sServiceUrl, true);
var oJsonModel = new sap.ui.model.json.JSONModel();
oServiceModel.read(vEntityURL, {
success: function(oData) {
oJsonModel.setData(oData);
}
});
this.getView().setModel(oJsonModel, "Customers");
var oCustomer = this.getView().getModel("Customers");
console.log(oCustomer.getProperty("/Name"));
}
The above returns undefined in the console.
However, it works if I press another button with the following function.
onPressButton2: function(){
var oCustomer = this.getView().getModel("Customers");
console.log(oCustomer.getProperty("/Name"));
}
This is not a sapui5 problem, it is the common behaviour of asynchronous code: you can be sure to have your data only in the success callback of the read method.
Move the last three lines of code inside the success function and you're done :-)

Stubbing byId() in SAPUI5-sinon

I'm filddling with sinonjs in SAPUI5. But there are some things I can't get my head around.
QUnit.module("Validation of Betaalwijze", {
beforeEach : function () {
this.oMainViewController = new MainViewController();
this.oViewStub = new ManagedObject();
var data = {
IBANPrimair: "123",
IBANSecundair: "456",
Betaalwijze: ""
};
var oModel = new JSONModel(data);
var fakeBetaalwijzeField = new Input();
sinon.stub(this.oViewStub, "getModel").returns(oModel);
sinon.stub(this.oViewStub, "byId").returns(fakeBetaalwijzeField);
sinon.stub(this.oMainViewController, "getView").returns(this.oViewStub);
},
afterEach : function() {
this.oMainViewController.destroy();
this.oViewStub.destroy();
this.fakeBetaalwijzeField.destroy();
}
});
QUnit.test("Should set an ValueState Error", function (assert) {
// Arrange
//All preparation here above.
// Act
this.oMainViewController._validateInput();
// Assert
//TODO
});
The getModel-stub works nicely when I use a "sap/ui/base/ManagedObject" for the oViewStub. But the byId-stub causes the message "Attempted to wrap undefined property byId as function" in that case.
When I use a "sap/ui/core/mvc/View" for the oViewStub, the getModel-stub is not found. (But this gives an error in the beforeEach also: Cannot read property 'viewData' of undefined.)
What is the right way to stub the View and it's methods getModel() and byId()?
The answer is quiete simple: sap.ui.base.ManagedObject does not have a method byId. This is a method of sap.ui.core.mvc.View. Just create a View instead of a ManagedObject in beforeEach and you should be fine.
BR
Chris

TypeError: Cannot read property 'then' of undefined in protractor-cucumber

I am using cucumber 3 with protractor 5.2.2. and i have given the url in my config file as baseUrl: 'https://www.linkedin.com' and i have to check whether navigated to the home page after clicking login.Instead of passing URL "https://www.linkedin.com/feed" ,i have pass "feed" and given split method in my step.js file.
my feature file is given below
When I enter "qwerty" in ".username"
And I enter "Passw0rd" in ".password"
And I click on ".login"
Then I should be at the "feed"
and in my step.js
Then(/^I should be at the "(.*)"$/, function (url) {
var geturl=browser.getCurrentUrl() + '';
var res=geturl.split("/");
var result=res[1];
return expect(result).to.eventually.equal(url);
});
My fourth step failed and getting error "TypeError: Cannot read property 'then' of undefined".where i am going wrong in my step.js file.Or how can i check the remaining part of base url when i am on inner page which having a url format of "base.com/feed/profile/profile-edit".Thanks in advance.
Explain your code issue inline at below.
Then(/^I should be at the "(.*)"$/, function (url) {
var geturl=browser.getCurrentUrl() + '';
// browser.getCurrentUrl() return a pomise which it's also a javascript object
// in javascript claculate `an object + ''`, will convert the object
// to string firstly by toString() function,
// if the object not overwrite the supper toString() function,
// it will return an string "[object Object]"
// So geturl = "[object Object]"
var res=geturl.split("/");
// there is no "/" in geturl, so the array length of res is 1
var result=res[1];
// res[1] exceed array length, so result = undefined
return expect(result).to.eventually.equal(url);
// because you used eventually at here, so chai will regard result is a promise,
// chai will check the argument of expect(xxx) is a promise or not
// by detect xxx has property: then via calling xxx.then in chai's inside code,
// but result is undefined, of course has no property: 'then'.
});
You have to consume promise eventual value in then()
Then(/^I should be at the "(.*)"$/, function (url) {
return browser.getCurrentUrl().then(function(cururl){
var parts = cururl.split("/");
return expect(parts[parts.length-1]).to.equal(url);
// dont't use eventually at here, because parts[parts.length-1] is not promise
});
});

Sending POST requests to a nested API endpoint URL using Ember Data

I see several questions on SO attempting to solve this problem of sending POST requests to nested API resource routes.
See:
- [Sending REST requests to a nested API endpoint URL using Ember Data(Sending REST requests to a nested API endpoint URL using Ember Data)
- Custom request URLs in Ember model
I've started overloading the createRecord, updateRecord, and deleteRecord methods on the RESTAdapter to attempt some sort of hackery solution to building the correct URL. Now, using a method similar to this is the route I've taken so far.
Here is the updateRecord method in their solution:
App.UserAdapter = DS.RESTAdapter.extend({
updateRecord: function(store, type, record) {
if(!record.get('parent') || null === record.get('parent')){
return this._super(store, type, record);
}
var data = {};
var serializer = store.serializerFor(type.typeKey);
var parent_type = record.get('parent');
var parent_id = record.get(parent_type).get('id');
var child_parts = Ember.String.decamelize(type.typeKey).split('_');
var path = Ember.String.pluralize(parent_type) + '/' + parent_id + '/' + Ember.String.pluralize(child_parts.pop());
serializer.serializeIntoHash(data, type, record);
var id = record.get('id');
return this.ajax(this.buildURL(path, id), "PUT", { data: data });
}
....
});
This method should work great in tandem with adding the parent type to the model and ensuring the related parent model id is also represented on the model. For PUT and DELETE requests, this shouldn't be a problem, as we already have the parent ID relation on the object in store.
Project model:
App.ProjectModel = DS.Model.extend({
name: DS.attr('string'),
createdAt: DS.attr('date'),
updatedAt: DS.attr('date'),
workspace : DS.belongsTo('workspace'),
parent: 'workspace',
....
});
Where this method appears to go awry for me is in creating new resources with a post. I've attempted it, but since the payload hasn't been returned from the API server with the related parent ID, I actually don't have access to it.
Here's my crappy first attempt, that doesn't work. The workspace id always returns null.
createRecord: function(store, type, record) {
if (!record.get('parent') || null === record.get('parent')){
return this._super(store, type, record);
}
var data = {};
var serializer = store.serializerFor(type.typeKey);
var parent_type = record.get('parent');
var parent_id = record.get(parent_type).get('id');
var path = Ember.String.pluralize(parent_type) + '/' + parent_id + '/' + type.typeKey);
serializer.serializeIntoHash(data, type, record, { includeId: true });
return this.ajax(this._buildURL(path, null), "POST", { data: data });
},
Got any thoughts on how I can get the parent ID, before I have a saved record?
I am the author of the solution you cited in your question.
What does your model hook look like in the route where you are creating the new ProjectModel?
Assuming your Workspace route looks something like:
App.WorkspaceRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('workspace', params.id);
}
});
Then your Workspace Project add/create route's model hook would need to be something like:
App.WorkspaceProjectAddRoute = Ember.Route.extend({
model: function () {
var workspace = this.modelFor('workspace');
return this.store.createRecord('project', {
workspace: workspace
});
}
}
I hope this makes some sense...

custom webapi function binding to store extjs grid

I need help to bind data returned by a custom webapi function. Say my webapi function calling syntax is like below:
var filter = {<some conditions here>};
Myapp.systemcontroller.Getdata(filter).then(function(result){
--- this result contain my data and total record
});
How can I bind this function to the store proxy and then bind it to a grid?
Any help would be greatly appreciated.
You can use different way to add the data to store. Its depend on your data structure(array or object).
grid.getStore.add(model) -ref:
http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.data.Store
grid.getStore.loadData(data,[append]) - ref:
http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.data.Store-method-loadData
grid.getStore.loadRawData(data,[append]) - ref:
http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.data.Store-method-loadRawData
grid.getStore.loadRecords(records,options) - ref:
http://docs.sencha.com/extjs/4.2.1/#!/api/Ext.data.Store-method-loadRecords
In your case, you can use loadRecords() or add()
The problem has been fixed. I m not using any proxy in my store.. I m using the above api to retrieve the data and bind it to store using loadData method. Then I will set the totalProperty of my toolbar as well
-- on load
Myapp.systemcontroller.Getdata(f).then(function (data) {
gridstore.loadData(data.Items);
gridstore.totalCount = data.TotalNumber;
var pgTb = Ext.getCmp('DataListPgTb');
pgTb.onLoad();
me.getLogList().setLoading(false);
});
Then in the toolbarchange event
toolBarChange: function (tbar, pageData, eOpts) {
var pageSize = PrIns.getApplication().Configuration.PageSize;
var me = this;
me.getLogList().setLoading(true);
var f = Ext.create(MyApp.webapi.filter.LogFilter', { pageIndex: pageData, pageSize: pageSize, orderBy: 'Ascending' });
var gridstore = this.getLogList().getStore();
Myapp.systemcontroller.Getdata(f).then(function (data) {
gridstore.loadData(data.Items);
gridstore.totalCount = data.TotalNumber;
gridstore.currentPage = pageData;
var pgTb = Ext.getCmp('DataListPgTb');
pgTb.onLoad();
me.getLogList().setLoading(false);
});
return false;
},
return false will prevent us from calling the proxy