how to capture the data sent by alloy ui io request in serveresource method? - liferay-6

Getting blank values for title and description in serveResource method.Is this the right way to send the parameters from io request?
After inserting blank values in database I have to reload the page to see the inserted values?So io-request is not ajax request?
<aui:script use="aui-base">
A.one('#<portlet:namespace/>save').on('click', function(event) {
var A = AUI();
var title=A.one('#<portlet:namespace/>title').val();
alert(title);
var description=A.one('#<portlet:namespace/>description');
var url = '<%= newJob.toString() %>';
A.io.request(
url,
{
method:'POST',
data: {
<portlet:namespace />title: title,
<portlet:namespace />description: description,
},
}
['aui-io-deprecated']
);
Liferay.Util.getOpener().<portlet:namespace/>closePopup('<portlet:namespace/>dialog');
});

AUI's io request is ajax request only.
You can get parameters in serveResource method using code below:
ParamUtil.get(resourceRequest, "NAMEOFPARAMETER");
Modify your javascript function and provide data attribute as below:
data: {
'<portlet:namespace />title': title,
'<portlet:namespace />description': description,
}

I assume both title and description are textfields. If so, description is missing a .val() call, or more appropriately, .get('value'). I didn't use a dialog/modal in my source, but the overall approach should be the same.
<script>
AUI().use('aui-base', 'aui-io-request', function(A){
A.one('#<portlet:namespace />save').on('click', function(event) {
var title= A.one('#<portlet:namespace />title').get('value');
var description=A.one('#<portlet:namespace />description').get('value');
var url = '<%=myResourceURL.toString()%>';
A.io.request(url,
{
method:'POST',
data: {
title: title,
description: description,
},
});
});
});
</script>
I'm still relatively new to Liferay and have had trouble with this as well. I've noticed that the data parameters are not in the parametersMap of the default ResourceRequest, as you have stated. Out of curiosity, I decided to use
UploadPortletRequest req = PortalUtil.getUploadPortletRequest(resourceRequest);
in the serveResource method and check it's parametersMap. The title and description parameters are available therein. I'm still learning where and how to access data from Liferay objects, but it would seem that for the UploadPortletRequest to have the data, it would be plucked from somewhere within the default ResourceRequest ... where still remains elusive to me.
After inserting blank values in database I have to reload the page to see the inserted values?
You have to reload the page because a resource action does not trigger a page refresh. If you are manipulating data that you want reflected in some other "view" you'll need to configure the appropriate communication or use one of the other available url types that does trigger the doView method of your other "view".

Related

parse response received from rememberCustomViewAsync from tableau javascript api

I am trying to save custom views using rememberCustomViewAsync and displaying the saved view using showCustomViewAsync, I want to parse the response received from executing rememberCustomViewAsync, it returns various details including the url of the view.
here is the code i am trying
$(document).on('click', '.new_dashboard_preference > [type="button"]', function() {
tableauViz.getWorkbook().rememberCustomViewAsync($('#dashboard_preference_name').val()).then(function(customView) {
console.log(customView.url); //this is what i am trying to access
jQuery(this).parent('form')[0].submit();
}).otherwise(function (err) {
console.log(err.message);
});
});
Can any please guide as to how the response received from rememberCustomViewAsync be parsed in javascript. Thanks.
If it is a JSON object you can use:
responseVariable = JSON.parse(responseVariable)
You can then use accessors like responseVariable.ItemYouWantToSee or array index accessors such as responseVariable[0].
To take this out of the 'click' event listener and make it a global object you can push the result to an array.

Data return from axios cant be written to variable

I want to get an data attribute from my axios post and write it to an local variable to reuse it.
If i console.log it inside the axios .then, tha data is set, if i write it to my variable and want to use it after, it is empty.
export default {
data(){
return {
post:{},
projectId: '',
existingProjects: []
}
},
methods: {
addPost(){
//check if project exists else create
let uriProj = 'http://localhost:4000/projects/add';
this.axios.post(uriProj, {
projectName: this.post.project,
}).then(response => this.projectId = response.data.data);
console.log("project_id: "+this.projectId)
}
}
What am i doing wrong?
Another Question:
Is this the right way if i want to reuse the id in another method?
My Goal is to first create a project if it is not already in my db, then i want to reuse the id of the created or returned project model to create a new customer in my db, if the customer already has the project with the id of this project, it shouldnt be added, if it is a new one it should be added.
Has this to be done in multiple requests or is there a simple method for doing this?
I believe the issue you are seeing has to do with the asynchronous nature of network calls. When axios submits the post request it returns a Promise then the addPost function continues executing. So the projectId gets logged after it the initial value gets set, but before the network request completes. Everything inside the then() function executes once the network request has been completed so you can test by moving the console.log to be executed once the request is done. You could also output the value in the template so you can see it update {{ projectId }}
this.axios.post(uriProj, {
projectName: this.post.project,
}).then(response => {
this.projectId = response.data.data
console.log("project_id: "+this.projectId)
});
I would ideally recommend using the VueJS dev tools browser extension because it allows you to inspect the state of your Vue components without having to use console.log or add random echos to your template markup.
#jfadich is correct. I recommend using async/await instead of then, it's more intuitive to read.
async addPost(){
//check if project exists else create
let uriProj = 'http://localhost:4000/projects/add';
let resp = await this.axios.post(uriProj, {
projectName: this.post.project,
})
this.projectId = resp.data.data
console.log("project_id: "+this.projectId)
}

What is the difference between setJSON, setData and loadData?

This is regarding the mentioned methods of sap.ui.model.json.JSONModel in SAPUI5:
setJSON
setData
loadData
What is the difference between these 3 methods? When do we use these methods and can we use more than 1 of them for the same purpose?
Have a look at the well documented API Reference for JSONModel.
In summary (from SAP Documentation):
setData: Sets the data, passed as a JS object tree, to the model.
e.g
var data = {
"ProductCollection": [{
"titleId": 0,
"Name": "Olayinka O",
"ProductId": "001",
"chartValue": 75,
"ProductPicUrl": "sap-icon://competitor"
}]
};
var oModel = new sap.ui.model.json.JSONModel(data);
//OR
var oModel = new sap.ui.model.json.JSONModel();
oModel.setData(data);
/*setdata, could also be a odata url in json format*/
loadData:
Load JSON-encoded data from the server using a GET HTTP request and store the resulting JSON data in the model. Note: Due to browser security restrictions, most "Ajax" requests are subject to the same origin policy, the request can not successfully retrieve data from a different domain, subdomain, or protocol.
e.g. you can use this to load/GET changes to the data/model and automatically updates the view if that specific model has being binded by reloading the url. If you use load, you don't need the other two in my opinion and loadData with not work on local json data.
var sURL = "https://cors-anywhere.herokuapp.com/https://services.odata.org/V3/Northwind/Northwind.svc/Products?$format=json";
var oModel = new sap.ui.model.json.JSONModel();
//if called in setInterval, all changes in the backend will be updated in the view if binded in this case every second
setInterval(oModel.loadData(sURL, true), 1000);
setJSON :
Sets the data, passed as a string in JSON format, to the model.
i.e. Same as Set Data but strict JSON
Luckily, the source code of UI5 is quite readable and often the better documentation than most of the API descriptions. Here is what each one of the APIs does basically:
setJSON
"Parse the JSON text and call setData"
JSONModel.prototype.setJSON = function(sJSON, bMerge) {
var oJSONData;
try {
oJSONData = jQuery.parseJSON(sJSON);
this.setData(oJSONData, bMerge);
} catch (e) {
// ...
}
};
Source
setData
"Store the data and notify all dependent bindings (checkUpdate)"
JSONModel.prototype.setData = function(oData/*plain JS object*/, bMerge){
if (bMerge) {
this.oData = /* merge with existing data */;
} else {
this.oData = oData;
} // ...
this.checkUpdate(); // notifies dependent bindings
};
Source
loadData
"Load data from the given remote URL and call setData" --> Please check the source here.
In short, they all call setData at some point.
Which API to call in which situation depends on in which format you have the data available.
The data are in JSON text --> setJSON
The data are somewhere else --> loadData
I already have the data in JS object / array ---> setData
setData
You have a JavaScript object and want to use this data as your model
const oJSONData = {
data: {
id: 4,
first_name: "Eve",
last_name: "Holt",
avatar: "https://s3.amazonaws.com/uifaces/faces/twitter/marcoramires/128.jpg"
}
};
oJSONModel.setData(oData);
setJSON
You have a String that when parsed represents a JavaScript object and want to use this data as your model
const sJSONData = '{"data":{"id":4,"first_name":"Eve","last_name":"Holt","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/marcoramires/128.jpg"}}';
oJSONModel.setJSON(sJSONData);
loadData
You want to access a remote API which returns data as JSON and want to use this data as your model
const sURL = "https://reqres.in/api/users/4";
oJSONModel.loadData(sURL);

What's the best way to handle a REST API's 'create' response in Backbone.js

I'm using backbone.js to interact with a REST API that, when posting to it to create a new resource, responds with a status of 201, a 'Location' header pointing to the resource's URI, but an empty body.
When I create a new model at the moment, its successful, but the local representation of the model only contains the properties I explicitly set, not any of the properties that would be set on the server (created_date, etc.)
From what I understand, Backbone would update its representation of the model with data in the body, if there were any. But, since there isn't, it doesn't.
So, clearly, I need to use the location in the Location header to update the model, but what's the best way to do this.
My current mindset is that I would have to parse the url from the header, split out the id, set the id for the model, then tell the model to fetch().
This seems really messy. Is there a cleaner way to do it?
I have some influence over the API. Is the best solution to try to get the API author to return the new model as the body of the response (keeping the 201 and the location header as well)?
Thanks!
Sounds like you will have to do a little customization.
Perhaps override the parse method and url method of your model class inherited from
Backbone.Model.
The inherited functions are:
url : function() {
var base = getUrl(this.collection);
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + this.id;
},
parse : function(resp) {
return resp;
},
and you could try something like:
parse: function(resp, xhr) {
this._url = xhr.getResponseHeader('location')
return resp
}
url: function() {
return this._url
}
Yes, backbone.js really wants the result of a save (be it PUT or POST) to be a parseable body which can be used to update the model. If, as you say, you have influence over the API, you should see if you can arrange for the content body to contain the resource attributes.
As you point out, its makes little sense to make a second over-the-wire call to fully materialize the model.
It may be that a status code of 200 is more appropriate. Purists may believe that a 201 status code implies only a location is returned and not the entity. Clearly, that doesn't make sense in this case.
With Backbone 0.9.9, I couldn't get the accepted answer to work. The signature of the parse function seems to have changed in an older version, and the xhr object is no longer available in the function signature.
This is an example of what I did, to make it work with Backbone v0.9.9 and jQuery 1.8.3 (using a Deferred Object/Promise), relying on the jqXHR object returned by Backbone.Model.save() :
window.CompanyView = Backbone.View.extend({
// ... omitted other functions...
// Invoked on a form submit
createCompany: function(event) {
event.preventDefault();
// Store a reference to the model for use in the promise
var model = this.model;
// Backbone.Model.save returns a jqXHR object
var xhr = model.save();
xhr.done(function(resp, status, xhr) {
if (!model.get("id") && status == "success" && xhr.status == 201) {
var location = xhr.getResponseHeader("location");
if (location) {
// The REST API sends back a Location header of format http://foo/rest/companys/id
// Split and obtain the last fragment
var fragments = location.split("/");
var id = fragments[fragments.length - 1];
// Set the id attribute of the Backbone model. This also updates the id property
model.set("id", id);
app.navigate('companys/' + model.id, {trigger: true});
}
}
});
}
});
I did not use the success callback that could be specified in the options hash provided to the Backbone.Model.save function, since that callback is invoked before the XHR response is received. That is, it is pointless to store a reference to the jqXHR object and use it in the success callback, since the jqXHR would not contain any response headers (yet) when the callback is invoked.
Another other to solve this would be to write a custom Backbone.sync implementation, but I didn't prefer this approach.

How to construct a REST API that takes an array of id's for the resources

I am building a REST API for my project. The API for getting a given user's INFO is:
api.com/users/[USER-ID]
I would like to also allow the client to pass in a list of user IDs. How can I construct the API so that it is RESTful and takes in a list of user ID's?
If you are passing all your parameters on the URL, then probably comma separated values would be the best choice. Then you would have an URL template like the following:
api.com/users?id=id1,id2,id3,id4,id5
api.com/users?id=id1,id2,id3,id4,id5
api.com/users?ids[]=id1&ids[]=id2&ids[]=id3&ids[]=id4&ids[]=id5
IMO, above calls does not looks RESTful, however these are quick and efficient workaround (y). But length of the URL is limited by webserver, eg tomcat.
RESTful attempt:
POST http://example.com/api/batchtask
[
{
method : "GET",
headers : [..],
url : "/users/id1"
},
{
method : "GET",
headers : [..],
url : "/users/id2"
}
]
Server will reply URI of newly created batchtask resource.
201 Created
Location: "http://example.com/api/batchtask/1254"
Now client can fetch batch response or task progress by polling
GET http://example.com/api/batchtask/1254
This is how others attempted to solve this issue:
Google Drive
Facebook
Microsoft
Subbu Allamaraju
I find another way of doing the same thing by using #PathParam. Here is the code sample.
#GET
#Path("data/xml/{Ids}")
#Produces("application/xml")
public Object getData(#PathParam("zrssIds") String Ids)
{
System.out.println("zrssIds = " + Ids);
//Here you need to use String tokenizer to make the array from the string.
}
Call the service by using following url.
http://localhost:8080/MyServices/resources/cm/data/xml/12,13,56,76
where
http://localhost:8080/[War File Name]/[Servlet Mapping]/[Class Path]/data/xml/12,13,56,76
As much as I prefer this approach:-
api.com/users?id=id1,id2,id3,id4,id5
The correct way is
api.com/users?ids[]=id1&ids[]=id2&ids[]=id3&ids[]=id4&ids[]=id5
or
api.com/users?ids=id1&ids=id2&ids=id3&ids=id4&ids=id5
This is how rack does it. This is how php does it. This is how node does it as well...
There seems to be a few ways to achieve this. I'd like to offer how I solve it:
GET /users/<id>[,id,...]
It does have limitation on the amount of ids that can be specified because of URI-length limits - which I find a good thing as to avoid abuse of the endpoint.
I prefer to use path parameters for IDs and keep querystring params dedicated to filters. It maintains RESTful-ness by ensuring the document responding at the URI can still be considered a resource and could still be cached (although there are some hoops to jump to cache it effectively).
I'm interested in comments in my hunt for the ideal solution to this form :)
You can build a Rest API or a restful project using ASP.NET MVC and return data as a JSON.
An example controller function would be:
public JsonpResult GetUsers(string userIds)
{
var values = JsonConvert.DeserializeObject<List<int>>(userIds);
var users = _userRepository.GetAllUsersByIds(userIds);
var collection = users.Select(user => new { id = user.Id, fullname = user.FirstName +" "+ user.LastName });
var result = new { users = collection };
return this.Jsonp(result);
}
public IQueryable<User> GetAllUsersByIds(List<int> ids)
{
return _db.Users.Where(c=> ids.Contains(c.Id));
}
Then you just call the GetUsers function via a regular AJAX function supplying the array of Ids(in this case I am using jQuery stringify to send the array as string and dematerialize it back in the controller but you can just send the array of ints and receive it as an array of int's in the controller). I've build an entire Restful API using ASP.NET MVC that returns the data as cross domain json and that can be used from any app. That of course if you can use ASP.NET MVC.
function GetUsers()
{
var link = '<%= ResolveUrl("~")%>users?callback=?';
var userIds = [];
$('#multiselect :selected').each(function (i, selected) {
userIds[i] = $(selected).val();
});
$.ajax({
url: link,
traditional: true,
data: { 'userIds': JSON.stringify(userIds) },
dataType: "jsonp",
jsonpCallback: "refreshUsers"
});
}