Reputation: 6105
[please start at comment to see where this problem is currently at, thanks. Hopefully fast/easy to read thru]
I've got a situation where I'm rendering a template, and the template has a javascript function that needs to send correct values to a controller action via a remoteFunction, e.g. below:
<script type="text/javascript">
function updateOrderEntry() {
alert("updating OrderEntries");
var unused = ${remoteFunction(action:'userByName', id:userId, update:[success: 'userOrderList', failure: 'userOrderListError'])};
}
</script>
I get the alert and "call" into the controller fine when this function gets executed on the page, but it is not sending an id value (params doesn't contain an id). When I build the template, I provide the userId as a model parameter and I have verified that it makes it into the template fine (e.g. in a div).
I've tried a bunch of variants of the above but can't get it to work, e.g. using remoteFunction as a tag:
var unused = <g:remoteFunction action="userByName" id="${userId}" update="[success: 'userOrderList', failure: 'userOrderListError']" />;
I also tried various flavors using the params attribute (which would be better), but after not getting that to work I thought I'd try just to get the id parameter working.
What am I doing wrong as I attempt to variably populate the remoteFunction attribute(s)?
Thanks
[project is on Grails 1.3.7]
-------------------------------- update per 1st/2nd comment -----------------------------------
Had no URL mappings for this controller, but removed the one I had for another controller. Also commented out the beforeInterceptor debug I had in this controller, namely:
// def beforeInterceptor = [action: this.&debug]
// def debug() {
// println "ACTION: ${actionUri}, PARAMS: ${params}"
// }
I added in debug code to the top of my userByName action, namely:
def userByName = {
println "action: ${actionUri}, params: ${params}"
I also did 3 versions of the remoteFunction that is sent back from the controller in the template, with all 3 identical, except the second one uses a userId that is hard coded, instead of produced from a variable, namely:
function updateOrderEntry() {
alert("updating OrderEntries, 1st time");
var unused = ${remoteFunction(action:'userByName', id:userId, params:[userId:userId], update:[success: 'userOrderList', failure: 'userOrderListError'])};
alert("updating OrderEntries, 2nd time, THIS TIME WITH hardcoded id and param values");
var unused = ${remoteFunction(action:'userByName', id:'4', params:[userId:'4'], update:[success: 'userOrderList', failure: 'userOrderListError'])};
alert("updating OrderEntries, 3rd time, back to non-hard-coded values");
var unused = ${remoteFunction(action:'userByName', id:userId, params:[userId:userId], update:[success: 'userOrderList', failure: 'userOrderListError'])};
}
When this function is sent back to the browser (from the render template), the browser (Chrome, using its network debug) shows it receives:
function updateOrderEntry() {
alert("updating OrderEntries, 1st time");
var unused = jQuery.ajax({type:'POST',data:{'userId': '4'}, url:'/nameIsInAppPropsFile/money/userByName/4',success:function(data,textStatus){jQuery('#userOrderList').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){jQuery('#userOrderListError').html(XMLHttpRequest.responseText);}});;
alert("updating OrderEntries, 2nd time, THIS TIME WITH hardcoded id and param values");
var unused = jQuery.ajax({type:'POST',data:{'userId': '4'}, url:'/nameIsInAppPropsFile/money/userByName/4',success:function(data,textStatus){jQuery('#userOrderList').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){jQuery('#userOrderListError').html(XMLHttpRequest.responseText);}});;
alert("updating OrderEntries, 3rd time, back to non-hard-coded values");
var unused = jQuery.ajax({type:'POST',data:{'userId': '4'}, url:'/nameIsInAppPropsFile/money/userByName/4',success:function(data,textStatus){jQuery('#userOrderList').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){jQuery('#userOrderListError').html(XMLHttpRequest.responseText);}});;
}
All of these look identical to me. The updateOrderEntry() gets automatically invoked, and I see all 3 alerts as shown above. Correspondingly, at the server, I see the following debug output:
action: /money/userByName, params: [userId:null, action:userByName, controller:money]
action: /money/userByName, params: [userId:4, id:4, action:userByName, controller:money]
action: /money/userByName, params: [userId:null, action:userByName, controller:money]
The problem is, the userId or id is not getting set correctly (as seen at the server) in the 1st and 3rd remoteFunctions, not clear why that is. Something is different, just can't figure out what.
P.S. Results are the same using both Chrome and Firefox.
------ updated per comments 3 - 5 below --------------------------
Looking at Chrome in what it sends, the middle, good request looks like:
Request URL:http://localhost:8080/nameIsInAppPropsFile/money/userByName/4
Request Method:POST
Status Code:200 OK
Request Headersview source
Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:8
Content-Type:application/x-www-form-urlencoded
Cookie:JSESSIONID=F0FDCEE323A1834350E209A7C211E64D
Host:localhost:8080
Origin:http://localhost:8080
Referer:http://localhost:8080/nameIsInAppPropsFile/money/transaction
User-Agent:Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7
X-Requested-With:XMLHttpRequest
Form Dataview URL encoded
userId:4
The 2 bad ones (note 1st and last lines, as well as ?) are:
Request URL:http://localhost:8080/nameIsInAppPropsFile/money/userByName
Request Method:POST
Status Code:200 OK
Request Headersview source
Accept:*/*
Accept-Charset:ISO-8859-1,utf-8;q=0.7,*;q=0.3
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:11
Content-Type:application/x-www-form-urlencoded
Cookie:JSESSIONID=F0FDCEE323A1834350E209A7C211E64D
Host:localhost:8080
Origin:http://localhost:8080
Referer:http://localhost:8080/nameIsInAppPropsFile/money/transaction
User-Agent:Mozilla/5.0 (Windows NT 6.0) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7
X-Requested-With:XMLHttpRequest
Form Dataview URL encoded
userId:null
So, this shows what the browser is sending is different.
When, I hard coded the value I gave to the template, i.e. model: [...., userId: '4'], I got the same failed result. Before I was using: model: [...., userId: order.user.id], but I updated my debug output and confirmed even hard-coding the '4' here doesn't help.
UPDATE: Adding minimal Test Case that matches real code. Note: the real code is a page with 3 sections, the top is fixed, where a customer is selected; the middle section is updated with orders for that customer; and the bottom section shows the credit card transactions associated via the order, usually multiple ones (e.g. authorize, capture, void, etc). When the user does an action (e.g. void) in the bottom transaction section, not only does it need to update this section, but also the middle order section. I was trying to get the order section -- the "second div" -- to update by using a link/remoteFunction on the bottom transaction section, and in its onComplete handler, calling a Javascript function that would do a second remoteFunction, to cause the middle order section to update.
Below is a minimal test case where I am not able to reproduce the problem exactly, but interestingly found a different but related problem. It appears Grails (or somehow IntelliJ, my IDE), has problems with an id parameter specified in the remoteFunction, when it is inside of Javascript. When you run it, you'll see what I mean. Hopefully fast to copy in the code and run. Thanks.
---- some simple controller, no model needed ----
class UiTestController {
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
def index = {
println "rendering remoteTest"
render(view: 'remoteTest')
}
def section1 = {
println "action1 params: ${params}"
render(template: 'remoteSection1', model: [id: params.id])
}
def section2 = {
println "action2 params: ${params}"
render(template: 'remoteSection2', model: [id: params.id])
}
}
--- remoteTest.gsp --
<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
<title>Remote</title>
<g:javascript library="jquery" plugin="jquery"/>
</head>
<body>
<div style="text-decoration:underline; color:purple; cursor:pointer; margin: 20px"
onclick="${remoteFunction(action: 'section1', id:'1', update: 'section1')}">click to update section 1</div>
<br />
<div id="section1">1st Section to Update</div>
<br />
<div id="section2">2nd Section to Update</div>
</body>
</html>
---- _remoteSection1.gsp -----
<g:javascript library="jquery" plugin="jquery"/>
<div>
<p>Section 1 updated, (id=${id}). Now, similar to real code, we
<span style="text-decoration:underline; color:purple; cursor:pointer;"
onclick="${remoteFunction(action: 'section2', id: '2', update: 'section2')}">update section 2</span>
</p>
</div>
---- _remoteSection2.gsp -----
<g:javascript library="jquery" plugin="jquery"/>
<script type="text/javascript">
function updateSection1() {
alert("doing remoteFunction with fixed value 55");
var unused = ${remoteFunction(action: 'section1', id='55', update: 'section1')};
alert("doing remoteFunction with fixed value 65, using params");
var unused = ${remoteFunction(action: 'section1', params:[id:'65'], update: 'section1')};
}
</script>
<div>
<p>Section 2 updated, (id=${id}). Now, similar to real code, we
<span style="text-decoration:underline; color:purple; cursor:pointer;"
onclick="${remoteFunction(action: 'section2', id: '2', update: 'section2', onComplete: 'updateSection1()')}">update section 2 again, and also section 1 with javascript updateSection1() function</span>
</p>
</div>
FINAL UPDATE
Code is now working, reason for change is unknown. I had to go on to a few other tasks, and I have come back to this one with changes to my controller, and surprisingly things are working. Same Javascript calls as before. But I will quickly post it in case you see that there was some kind of mess up before:
-------------------------- Pertinent Javascript Code in _moneyEntries.gsp --------------------------
function updateOrderEntry() {
alert("Updating OrderEntries -- 1st time");
${remoteFunction(action:'userByName', id:userId, update:[success: 'userOrderList', failure: 'userOrderListError'])};
alert("Updating OrderEntries -- 2nd time, THIS TIME WITH hardcoded id and param values");
${remoteFunction(action:'userByName', params:[id: '4'], update:[success: 'userOrderList', failure: 'userOrderListError'])};
alert("Updating OrderEntries -- 3rd time, back to non-hard-coded values");
${remoteFunction(action:'userByName', params:[id: userId], update:[success: 'userOrderList', failure: 'userOrderListError'])};
}
---------------- What Google network view shows me -- entire response --------------
<script type="text/javascript" src="/nameIsInAppPropsFile/plugins/jquery- 1.6.1.1/js/jquery/jquery-1.6.1.js"></script>
<script type="text/javascript">
function confirmVoid(moneyTransId) {
return confirm("Void transaction ID " +moneyTransId + ":");
}
function confirmCredit(moneyTransId, amountIn) {
var valid = false;
var amount = amountIn;
while (!valid) {
var r = prompt("For ID " +moneyTransId+ ", please specify the amount:", amount);
if (r == null || r == "") {
valid = true;
amount = -1;
} else {
//alert("result is:" +r);
if (r > amount) {
alert("You cannot credit an amount greater than " + amount);
amount = amountIn;
}
else {
amount = r;
// invoke remote function, update result
valid = true;
}
}
}
return amount;
}
function confirmCapture(moneyTransId) {
return confirm("Capture funds for ID " +moneyTransId + ":");
}
function confirmSend(moneyTransId) {
return confirm("Are you sure you want to send an email receipt for ID " +moneyTransId + ":");
}
//var unused = jQuery.ajax({type:'POST', url:'/nameIsInAppPropsFile/money/userByName/3',success:function(data,textStatus){jQuery('#userOrderList').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){jQuery('#userOrderListError').html(XMLHttpRequest.responseText);}});;
function updateOrderEntry() {
alert("Updating OrderEntries -- 1st time");
jQuery.ajax({type:'POST', url:'/nameIsInAppPropsFile/money/userByName/8',success:function(data,textStatus){jQuery('#userOrderList').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){jQuery('#userOrderListError').html(XMLHttpRequest.responseText);}});;
alert("Updating OrderEntries -- 2nd time, THIS TIME WITH hardcoded id and param values");
jQuery.ajax({type:'POST',data:{'id': '4'}, url:'/nameIsInAppPropsFile/money/userByName',success:function(data,textStatus){jQuery('#userOrderList').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){jQuery('#userOrderListError').html(XMLHttpRequest.responseText);}});;
alert("Updating OrderEntries -- 3rd time, back to non-hard-coded values");
jQuery.ajax({type:'POST',data:{'id': '8'}, url:'/nameIsInAppPropsFile/money/userByName',success:function(data,textStatus){jQuery('#userOrderList').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){jQuery('#userOrderListError').html(XMLHttpRequest.responseText);}});;
}
</script>
<div>
<div style="margin-bottom:9px;">Order # 1005 Merchant Transactions:
<span class="jsAction" style="padding-left:30px"
onclick="jQuery.ajax({type:'POST',data:{'userId': '8'}, url:'/nameIsInAppPropsFile/money/userByName',success:function(data,textStatus){jQuery('#userOrderList').html(data);},error:function(XMLHttpRequest,textStatus,errorThrown){jQuery('#userOrderListError').html(XMLHttpRequest.responseText);}});; return false;">(refresh order status)</span></div>
<div class="list">
<table>
<thead>
<tr>
<th>What Action?</th>
<th class="sortable" ><a href="/nameIsInAppPropsFile/money/voidTrans/12?sort=id&order=asc">ID</a></th>
<th class="sortable" ><a href="/nameIsInAppPropsFile/money/voidTrans/12?sort=dateCreated&order=asc">Date</a></th>
<th class="sortable" ><a href="/nameIsInAppPropsFile/money/voidTrans/12?sort=type&order=asc">Transaction Type</a></th>
<th class="sortable" ><a href="/nameIsInAppPropsFile/money/voidTrans/12?sort=responseCode&order=asc">Result</a></th>
<th class="sortable" ><a href="/nameIsInAppPropsFile/money/voidTrans/12?sort=totalAmt&order=asc">Amount</a></th>
<th class="sortable" ><a href="/nameIsInAppPropsFile/money/voidTrans/12?sort=description&order=asc">Description</a></th>
<th class="sortable" ><a href="/nameIsInAppPropsFile/money/voidTrans/12?sort=txid&order=asc">Authorize.Net Transaction Id</a></th>
<th class="sortable" ><a href="/nameIsInAppPropsFile/money/voidTrans/12?sort=responseReasonText&order=asc">Authorize.Net Response Text</a></th>
<th class="sortable" ><a href="/nameIsInAppPropsFile/money/voidTrans/12?sort=ccNumber&order=asc">Credit Card #</a></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>
<!-- First Check for transaction validity, for it to be 'actionable' -->
none
</td>
<td>13</td>
<td>01-13-12 03:41 MST</td>
<td>VOID OF previous authorization, ID 12</td>
<td><span class="">
Approved</span></td>
<td>NA</td>
<td>Void: sfk .</td>
<td>2168161109</td>
<td><span class="">
This transaction has been approved.</span></td>
<td></td>
</tr>
<tr class="even">
<td>
<!-- First Check for transaction validity, for it to be 'actionable' -->
none
</td>
<td>12</td>
<td>01-13-12 03:41 MST</td>
<td>AUTHORIZATION ONLY - then Voided in ID 13</td>
<td><span class="">
Approved</span></td>
<td>$11</td>
<td>sfk .</td>
<td>2168161109</td>
<td><span class="">
This transaction has been approved.</span></td>
<td>x9113</td>
</tr>
</tbody>
</table>
</div>
<!--div class="paginateButtons">
</div-->
</div>
-------------------------- What the Debug output shows in the controller -----
ACTION: /money/userByName, PARAMS: [id:8, action:userByName, controller:money]
ACTION: /money/userByName, PARAMS: [id:4, action:userByName, controller:money]
ACTION: /money/userByName, PARAMS: [id:8, action:userByName, controller:money]
So, I don't know why it is working now, and not before. But the updateOrderEntry() is the same. Perhaps you see something I did wrong before, in which case please let me know. Thanks again for all your help Tomasz, I learned a lot being able to debug with Google Chrome (thanks to your help).
Upvotes: 1
Views: 4858
Reputation: 1
this is how params work in a remoteFunction:
....
params:'\'id=\'+document.getElementById(\'idField\').value',
.....
Upvotes: 0
Reputation: 3723
It rendered fine for me, Grails 2.0, I've set def userId = 7
and as you can see it appears in URL:
function updateOrderEntry() {
alert("updating OrderEntries");
var unused = jQuery.ajax({type:'POST',
url:'/controller/userByName/7',
success:function(data,textStatus)
{jQuery('#userOrderList').html(data);},
error:function(XMLHttpRequest,textStatus,errorThrown)
{jQuery('#userOrderListError').html(XMLHttpRequest.responseText);}
});;}
Upvotes: 1