I've been involved in a large web application where I have a lot of functions that calls web services through JSON. For instance:
/*...*/
refreshClientBoxes: function(customerNr) {
var request = {};
request.method = "getClientBoxes";
request.params = {};
request.params.customerNr = customerNr;
request.id = Math.floor(Math.random() * 101);
postObject(jsonURL, JSON.stringify(request), successClientBoxes);
},
/*...*/
Where “postObject” it’s a function that receive an URL, the data and a callback.
As you can see I have to construct this piece of code in every single method:
var request = {};
request.method = "getClientBoxes";
request.params = {};
request.params.customerNr = customerNr;
request.id = Math.floor(Math.random() * 101);
What's change is the name of the method that we will call and the name and values of parameter that we want to pass.
So I was wondering if there is a way that we can avoid this effort through a method that receive the name of the method that we will call and array of parameters, and using some kind of reflection construct the request parameters and return the request stringifyed.
For the WS I used php + zend 1.12, the MVC framework in JS its ember 0.95 and jQuery.
Edit 1: So thanks all for your answers. What I want it's a way that can give me the name of the parameters that I'm passing to the function or the name of a variable that I passed. Something like this:
var contructRequest = function (methodName, paramList) {
var request = {};
request.method = methodName;
request.params = {};
for(var i = 0; i < paramlist; i++){
/*some how get the paramName through reflection...so if i give a variable called customerNr this "for" add this new parameter to list of parameters like request.params.customerNr = customerNr whatever the variable name is or its value*/
}
request.params[paramName] = paramValue;
request.id = Math.floor(Math.random() * 101);
return request;
}
How about a method like this:
var contructRequest = function (methodName, paramList, paramName, paramValue) {
var request = {};
request.method = methodName;
request.params = paramList;
request.params[paramName] = paramValue;
request.id = Math.floor(Math.random() * 101);
return request;
}
This exploits the fact that object.property can also be referred to using object["property"].
You can call the method like so:
var customerRequest = constructRequest("getClientBoxes", {}, "customerNr", customerNr);
postObject(jsonURL, JSON.stringify(customerRequest), successClientBoxes);
You could DRY this by encapsulating the common parts in a separate function which takes the non-common parts as arguments, and returns the JSON. For example, if we assume that the only parts that change across the different functions are the method and customerNr:
buildRequest(method, customerNr) {
var request = {
method: method,
params: {
customerNr: customerNr
},
id: Math.floor(Math.random() * 101)
};
return JSON.stringify(request);
}
and you'd use it like so:
refreshClientBoxes: function(customerNr) {
var json = buildRequest('getClientBoxes', customerNr);
postObject(jsonURL, json, successClientBoxes);
},
Related
I hosted my Slim app on AWS Lambda. For my PHP app to work, I followed this tutorial
My app runs fine until I try to submit a form with a POST method. My PHP cannot get the values from the form. When I dumped $_POST and file_get_contents('php://input'), both returned a null.
In the tutorial, Chris (the author) stated that this code spawns the child process and sets a bunch of environment variables which PHP CGI populates into the $_SERVER super global.
var php = spawn('./php-cgi', ['function.php'], {
env: Object.assign({
REDIRECT_STATUS: 200,
REQUEST_METHOD: requestMethod,
SCRIPT_FILENAME: 'function.php',
SCRIPT_NAME: '/function.php',
PATH_INFO: '/',
SERVER_NAME: serverName,
SERVER_PROTOCOL: 'HTTP/1.1',
REQUEST_URI: requestUri
}, headers)
});
I am not familiar with child processes, so I would like to ask if there is a way that I can also populate the $_POST superglobal? Because I think the POST data lives in the event object/variable in my handler function, meaning (I think) my NodeJS wrapper could access the POST data, but it didn't pass it to the PHP CGI?
exports.handler = function(event, context)
here is the full code of my NodeJS wrapper:
var spawn = require('child_process').spawn;
var parseHeaders, parseResponse, parseStatusLine;
parseResponse = function(responseString) {
var headerLines, line, lines, parsedStatusLine, response;
response = {};
lines = responseString.split('\r\n');
parsedStatusLine = parseStatusLine(lines.shift());
response['protocolVersion'] = parsedStatusLine['protocol'];
response['statusCode'] = parsedStatusLine['statusCode'];
response['statusMessage'] = parsedStatusLine['statusMessage'];
headerLines = [];
while (lines.length > 0) {
line = lines.shift();
if (line === "") {
break;
}
headerLines.push(line);
}
response['headers'] = parseHeaders(headerLines);
response['body'] = lines.join('\r\n');
return response;
};
parseHeaders = function(headerLines) {
var headers, key, line, parts, _i, _len;
headers = {};
for (_i = 0, _len = headerLines.length; _i < _len; _i++) {
line = headerLines[_i];
parts = line.split(":");
key = parts.shift();
headers[key] = parts.join(":").trim();
}
return headers;
};
parseStatusLine = function(statusLine) {
var parsed, parts;
parts = statusLine.match(/^(.+) ([0-9]{3}) (.*)$/);
parsed = {};
if (parts !== null) {
parsed['protocol'] = parts[1];
parsed['statusCode'] = parts[2];
parsed['statusMessage'] = parts[3];
}
return parsed;
};
exports.index = function(event, context) {
// Sets some sane defaults here so that this function doesn't fail when it's not handling a HTTP request from
// API Gateway.
var requestMethod = event.httpMethod || 'GET';
var serverName = event.headers ? event.headers.Host : '';
var requestUri = event.path || '';
var headers = {};
// Convert all headers passed by API Gateway into the correct format for PHP CGI. This means converting a header
// such as "X-Test" into "HTTP_X-TEST".
if (event.headers) {
Object.keys(event.headers).map(function (key) {
headers['HTTP_' + key.toUpperCase()] = event.headers[key];
});
}
// Spawn the PHP CGI process with a bunch of environment variables that describe the request.
var php = spawn('./php-cgi', ['slim/public/index.php'], {
env: Object.assign({
REDIRECT_STATUS: 200,
REQUEST_METHOD: requestMethod,
SCRIPT_FILENAME: 'slim/public/index.php',
SCRIPT_NAME: '/index.php',
PATH_INFO: '/',
SERVER_NAME: serverName,
SERVER_PROTOCOL: 'HTTP/1.1',
REQUEST_URI: requestUri
}, headers)
});
// Listen for output on stdout, this is the HTTP response.
var response = '';
php.stdout.on('data', function(data) {
response += data.toString('utf-8');
});
// When the process exists, we should have a complete HTTP response to send back to API Gateway.
php.on('close', function(code) {
// Parses a raw HTTP response into an object that we can manipulate into the required format.
var parsedResponse = parseResponse(response);
// Signals the end of the Lambda function, and passes the provided object back to API Gateway.
context.succeed({
statusCode: parsedResponse.statusCode || 200,
headers: parsedResponse.headers,
body: parsedResponse.body
});
});
};
In some cases it's necessary to set CONTENT_LENGTH and/or CONTENT_TYPE in the environment in order for php-cgi to process $_POST properly. For example (where postBody is a string like "field1=value1&field2=value2"):
var env = {
'SCRIPT_FILENAME': script_path,
'REQUEST_METHOD': 'POST',
'REDIRECT_STATUS': 1,
'CONTENT_TYPE': 'application/x-www-form-urlencoded',
'CONTENT_LENGTH': postBody.length
}
//if the URL has anything after "?", it should appear in $_GET even when the method is "POST"
if(queryString) env['QUERY_STRING'] = queryString;
The post body needs to be input into the stdin of the child.
Here is an async example:
var spawn = require('child_process').spawn;
var phpProcess = spawn (php_cgi_path, [script_path], {'env': env})
phpProcess.stdin.write( postBody );
var outputBuffer = [];
phpProcess.stdout.on('data', function(data) {
outputBuffer.push (data.toString());
})
phpProcess.stdout.on('end', function( ) {
var phpOutput = outputBuffer.join('') ;
// process php output
});
It's also possible to give the input data in a synchronous way, for example:
var spawnSync = require('child_process').spawnSync;
var phpProcessSync = spawnSync (php_cgi_path, [script_path], {'env': env, 'input': postBody})
var phpOutput = phpProcessSync.stdout.toString()
// process php output
Again the post data is input separately from "env".
It's also possible to modify the script so that it populates $_FILES as well.
For example, one can use Uint8Array (instead of string) to store post body, then set 'CONTENT_TYPE' to request.headers['content-type'] (Then we would have for example "Content-Type:multipart/form-data; boundary=----WebKitFormBoundarynGa8p8HMIQ8kWQLA")then use phpProcess.stdin.write( Buffer.from(postBody) );
It will have "tmp_name" etc in the $_FILES variable.
I'm trying to parse a string from a noSQL database. With difficulty.
when you access the PHP file it gives a result like this:
[{"timestamp":"2016-11-07T09:48:30.335Z","Id":"d7ee735f16b5"},
{"timestamp":"2016-11-07T09:48:29.015Z","Id":"d7ee735f16b5"},
{"timestamp":"2016-11-07T09:48:27.688Z","Id":"d7ee735f16b5"},
{"timestamp":"2016-11-07T09:48:27.085Z","Id":"d7ee735f16b5"},
{"timestamp":"2016-11-07T09:48:26.577Z","Id":"d7ee735f16b5"}]
The same result is given in the network of the console.
When I then attempt to stringify the response it shows null.
Please can anyone help me access the timestamp values. Here is the current code:
var ajax = new XMLHttpRequest();
ajax.open("GET", url, true);
ajax.responseType = 'json';
ajax.send();
var jsonResponse = JSON.stringify(ajax.response);
document.getElementById('demo').innerHTML = jsonResponse;
Most likely the response hasn't returned from the server yet in this example. Also verify that an element with an id of 'demo' does in fact exist in your html document.
Add event listeners to the ajax object:
ajax.onload = loadCompleted;
ajax.onerror = onLoadError;
then create a function to handle the result:
var jsonResponse = JSON.stringify(ajax.response);
document.getElementById('demo').innerHTML = jsonResponse;
Full example (updated per Bozidar's comments):
var url = 'https://jsonplaceholder.typicode.com/posts/1';
function loadCompleted() {
console.log(ajax.response)
var jsonResponse = JSON.stringify(ajax.response);
document.getElementById('demo').innerHTML = jsonResponse;
}
function onLoadError() {
//handle error logic
}
var ajax = new XMLHttpRequest();
ajax.onload = loadCompleted;
ajax.onerror = onLoadError;
ajax.open("GET", url, true);
ajax.responseType = 'json';
ajax.send();
See https://jsfiddle.net/px3mxa4n/ for a working example.
Shouldn't you wait for response before continuing with your code? Something like:
ajax.onreadystatechange = function () {
if(ajax.readyState == 4 && ajax.status == 200) {
// do what you need like
var jsonResponse = JSON.stringify(ajax.response); // maybe JSON.parse and then iterate trough array of objects to create individual HTML elements?
// .......
}
}
I'm having an issue where one of the fields in a created object is undefined.
Here's the object:
var Collateral = Collateral || {};
Collateral.NoteModel = function(config) {
"use strict";
this.loanId = config.loanId;
this.docId = config.docId;
this.borrowerName = config.borrowerName;
this.funder = config.funder;
this.docsDrawn = config.docsDrawn;
this.firstPayment = config.firstPayment;
this.loanAmount = config.loanAmount;
this.interestRate = config.interestRate;
this.dateDocsIn = config.dateDocsIn;
this.status = config.status;
this.eventDate = config.eventDate;
this.submittedBy = config.submittedBy;
};
I am storing a list of objects and each field of the object is storing properly, except the status field. When i call the status field by itself it returns the correct value, but when I try to access note.status it returns undefined.
Here is the code for creating the list of objects:
var renderNotesList = function(url, data, elementId, liFunc) {
$.getJSON(url, data, function(response) {
var notesList = [];
renderNotesList.divider;
$(elementId).empty();
$(response).each(function() {
var entry = $(this)[0];
var note = new Collateral.NoteModel({ //initialize note object
loanId: entry["Loan_ID"],
docId: entry["Doc_ID"],
borrowerName: entry["Borrower_Name"],
funder: entry["Funder"],
docsDrawn: entry["Docs_Drawn"],
firstPayment: entry["First_Payment"],
loanAmount: entry["Loan_Amount"],
interestRate: entry["Interest_Rate"],
dateDocsIn: entry["Date_Docs_In"],
status: entry["Event_Type"],
eventDate: entry["Event_Date"],
submittedBy: entry["Submitted_By"]
});
console.log(entry["Event_Type"]); // here the value is correct
console.log(note.status); // here the value is "undefined"
var li = liFunc(note, renderNotesList.divider);
li.appendTo($(elementId));
try {
$(elementId).listview('refresh');
}
catch(e) {
return;
}
});
});
};
For all other note. calls, the variables are correct. It is only status that returns undefined. The objects were storing the correct values, but after a short while, without changing anything, the values became undefined. Any ideas? Thanks in advance!
Since I still can't comment I'll post as an answer. My guess here is that javascript has never been clear about the "passing as value/reference" issue. var entry contains all the values from your JSON and are copied as reference to your "note" object, but since it's on a limited scope, these references cease to exist.
Just for testing, try copying some of the values directly from the response variable.
How do I submit an array from dojo to php.
I'm submitting these values:
["a", "b", "c"]
Here's what I got so far:
btn_send.onclick(function(){
var name_array = name_looper();
console.log(name_array);
dojo.xhrPost({
url: "dojo_phpform.php",
content: {names: name_array},
load: function(result) {
var x = dojo.byId('results');
x.innerHTML = result;
}
});
});
function name_looper(){
var names = dojo.query('input[type=text]');
var name_array = [];
names.forEach(function(element, index, array){
name_array[index] = dojo.attr(element, 'value');
});
return name_array;
}
I tried to echo $_POST['names'] from the php file(dojo_phpform.php) and it didn't return any errors. It seems like the array isn't actually submitted. The only thing that's returned is the last item in the array. What do I do?Please help, Thanks in advance!
I just tested this with grails and php. In grails I have no problem getting an array submitted through a dojo xhrPost : I retrieve the array properly parsed with all its values as expected.
If I post :
dojo.xhrPost({
content : {
names : ['foo', 'bar']
},
url : "mygrailscontroller"
});
I get an array param on the other side. Which proves the problem hasn't to be solved on the dojo side, but on the php side.
In php, if a form input has a variable of type array, its name parameter has to be set with square brackets, like : "names[]" rather than "names".
So... in your case the solution is not to flatten the array into a string (sorry), but to name your array argument with brackets. So it would be :
dojo.xhrPost({
content : {
"names[]" : ['foo', 'bar']
},
url : "myphpcontroller"
});
As far as I've been able to see, dojo's xhr functions don't support it. I'm using a helper function to "flatten" parameters myself.
_flattenXhrParams: function(params)
{
var newParams = {};
for(var key in params)
{
if(dojo.isObject(params[key]))
{
for(var innerKey in params[key])
{
newParams[key + "[" + innerKey + "]"] =
params[key][innerKey];
}
}
else if(dojo.isArray(params[key]))
{
for(var i = 0, l = params[key].length; i < l; i++)
{
newParams[key + "[]"] = params[key][i];
}
}
else
{
newParams[key] = params[key];
}
}
return newParams;
}
It's butt ugly, I know, and obviously only works on one dimensional arrays/objects. In your case, you'd do:
dojo.xhrPost({
url: "dojo_phpform.php",
content: _flattenXhrParams({names: name_array}),
load: function(result) {
var x = dojo.byId('results');
x.innerHTML = result;
}
});
.. and you'd get POST parameters like names[]=a&names[]=b&names[]=c. For objects, you'd get names[somekey]=a&names[otherKey]=b etc. PHP handles both nicely.
I'm pretty sure the values of the content object only take strings. If you want to submit an array, you'd have to turn it into JSON and then json_decode it on the server.
Really like that function.
$matches = array('12', 'watt');
list($value, $unit) = $matches;
Is there a Javascript equivalent of that?
There is, in 'newer' versions of Javascript: Destructuring assignment - Javascript 1.7. It's probably only supported in Mozilla-based browsers, and maybe in Rhino.
var a = 1;
var b = 3;
[a, b] = [b, a];
EDIT: actually it wouldn't surprise me if the V8 Javascript library (and thus Chrome) supports this. But don't count on it either
Now supported in all modern browsers(except IE, of course).
try this:
matches = ['12', 'watt'];
[value, unit] = matches;
ES6 does support this directly now via array destructuring.
const matches = ['12', 'watt'];
const [value, unit] = matches;
This is my solution for using List/Explode on Javascript.
Fiddle Working Example
First the implementation :
var dateMonth = "04/15";
dateMonth.split("/").list("month","day", "year");
month == "04";
day == "15";
year == null;
It also allows for scoping the new generated variables :
var scoped = (function()
{
var dateMonth = "07/24/2013";
dateMonth.split("/").list("month","day", "year", this);
this.month == "07";
this.day == "24";
this.year == "2013";
})();
This was accomplished by modifying an the Array prototype.
Array.prototype.list = function()
{
var
limit = this.length,
orphans = arguments.length - limit,
scope = orphans > 0 && typeof(arguments[arguments.length-1]) != "string" ? arguments[arguments.length-1] : window
;
while(limit--) scope[arguments[limit]] = this[limit];
if(scope != window) orphans--;
if(orphans > 0)
{
orphans += this.length;
while(orphans-- > this.length) scope[arguments[orphans]] = null;
}
}
There is a experimental implementation of list() by PHPJS here:
https://github.com/kvz/phpjs/blob/master/_experimental/array/list.js
CoffeeScript offers destructuring assignment with the syntax:
[a, b] = someFunctionReturningAnArray()
This is pretty much identical to the feature offered in very new JavaScript versions. However, CoffeeScript produces compiled JS that is compatible even with IE6's JavaScript engine, and therefore it's a good option if compatibility is vital.
Since most JavaScript implementations don't yet support that feature, you could simply do it in a more JavaScript-like fashion:
function list(){
var args = arguments;
return function(array){
var obj = {};
for(i=0; i<args.length; i++){
obj[args[i]] = array[i];
}
return obj;
};
}
Example:
var array = ['GET', '/users', 'UserController'];
var obj = {};
obj = list('method', 'route', 'controller')(array);
console.log(obj.method); // "GET"
console.log(obj.route); // "/users"
console.log(obj.controller); // "UserController"
Check the fiddle
An alternative is to add a list-method to Array.prototype (even I wouldn't recommend it):
Array.prototype.list = function(){
var i, obj = {};
for(i=0; i<arguments.length; i++){
obj[arguments[i]] = this[i];
}
// if you do this, you pass to the dark side `,:,´
this.props = obj;
return obj;
};
Example:
/**
* Example 1: use Array.prototype.props
*/
var array = ['GET', '/users', 'UserController'];
array.list('method', 'route', 'controller');
console.log(array.props.method); // "GET"
console.log(array.props.route); // "/users"
console.log(array.props.controller); // "UserController"
/**
* Example 2: use the return value
*/
var array = ['GET', '/users', 'UserController'];
var props = array.list('method', 'route', 'controller');
console.log(props.method); // "GET"
console.log(props.route); // "/users"
console.log(props.controller); // "UserController"
Check the fiddle for that one
This is my hack at it; as short as I could get it without writing a function to do it. Gotta be careful of the scope of "this" though:
list = ["a","b","c"];
vals = [1,2,3];
for(var i in vals)this[list[i]]=vals[i];
console.log(a,b,c);
Good enough for a laugh. I still assign each variable one at a time:
a=vals[0];
b=vals[1];
c=vals[2];
It's much shorter this way. Besides, if you've got a bunch of variables they should probably be kept in the array, or even better they should be properties of a closure, instead of declaring them all separately.
function list(fn,array){
if(fn.length && array.length){
for(var i=0;i<array.length;i++){
var applyArray = [];
for(var j=0;j<array[i].length;j++){
fn[j] = array[i][j];
applyArray.push(fn[j]);
}
fn.apply(this,applyArray);
}
}
}
Example:
//array array mixture for composure
var arrayMixture = [ ["coffee","sugar","milk"], ["tea","sugar","honey"] ];
//call our function
list(function(treat,addin,addin2){
console.log("I like "+treat+" with " + addin + " and " + addin2);
},arrayMixture);
//output:
//I like coffee with sugar and milk
//I like tea with sugar and honey