Thymeleaf: Form submit on click of table row - ajax

I have a table with orders and order ids. I would like to click on a row to view the order details in another page. My code:
<form id="orderDetalsForm" th:action="#{/restaurant/orderdetails}"
method="POST" th:object="${order}">
<table class="table table-bordered table-hover">
<thead>
<tr>
<th scope="col">Order Id</th>
<th scope="col">Order Details</th>
<th scope="col">Date</th>
<th scope="col">Amount</th>
</tr>
</thead>
<tbody>
<tr id="orderRow" th:each="order : ${orders}"
style="cursor: pointer"
th:onclick="'getOrderItems(\''+${order.orderId}+ '\');'">
<td scope="row" th:text="${order.orderId}"></td>
<td th:text="${order.orderDetais}"></td>
<td th:text="${order.orderDate}"></td>
<td th:text="${order.amount}"></td>
</tr>
<tr>
<td colspan="3">Total</td>
<td th:text="${grandTotal}"></td>
</tr>
</tbody>
</table>
</form>
I tried an ajax form submit:
<script>
function getOrderItems(orderId) {
var url = "/restaurant/orderdetails";
$.ajax({
url : url,
type : "post",
data : {
"orderId" : orderId
},
success : function(data) {
console.log(data);
},
error : function() {
console.log("There was an error");
}
});
}
</script>
In my controller I have this:
#PostMapping(value="/restaurant/orderdetails")
public ModelAndView orderDetails(#RequestParam String orderId){
List<Product> orderDetails = userService.getOrderDetails(orderId);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("orderDetails", orderDetails);
modelAndView.setViewName("restaurant/orderdetails");
return modelAndView;
}
While the ajax works, the page is not getting redirected to my orderdetails page.

You can't use AJAX to redirect to a page. AJAX is for getting data from the server that is then processed/displayed using JavaScript. You however basically want clicking on the row to behave like clicking a link or (since you are using a POST mapping) submitting a form.
First off, using POST makes this a bit more complicated. You should consider using a GET mapping, not only because it makes this problem easier, but also because a POST mapping isn't really appropriate here. POST is used to send data to the server, which you are not doing.
Another thing you should consider it that using a (pure) JavaScript solution to link the table row hinders accessibility. For example, users that can't/don't use a mouse (such as disabled people or search engines) won't be able to see or even use such a link. To solve this it is a good idea to add a proper link to the row. Then that link can used by "clicking" on it with the JavaScript of the click handler.
<tr th:each="order : ${orders}" onclick="orderRowClick(this)">
<td scope="row"><a th:href="#{/restaurant/orderdetails(orderId=${order.orderId})}" th:text="${order.orderId}"></a></td>
<td th:text="${order.orderDetais}"></td>
<td th:text="${order.orderDate}"></td>
<td th:text="${order.amount}"></td>
</tr>
<script>
// Look for a link in the row and click on it
function orderRowClick(row) {
row.querySelector("a").click();
}
</script>
Several more points:
IDs must be unique in HTML. By putting id="orderRow" on such a repeated row will result in invalid HTML.
You shouldn't be using on... attributes to assign event handlers. I'm just using it here or otherwise this answer will go too far.
Remove the <form> from around the table. It doesn't do anything.
If you do want to/have to use a POST mapping, then replace the link in the table row with a form with a hidden field containing the order ID and a submit button and in the JavaScript look for the form instead of the link and submit it: row.querySelector("form").submit();.
BTW there are several (possibly better) ways to do what you are trying. For example:
Forget the JavaScript and just put a link into every cell. With the right CSS the row/cells can be changed so that it looks like you are clicking on the row.
It seems like you are using Bootstrap, which has the "stretched link" feature. Unfortunately it's a bit tricky to get to work with table rows, but it's worth looking at.

What I've understand so far is that you want to redirect user to a new page when the user clicks on the button on the table, for that there're different approaches -
Issue with your approach -
Since you're using ajax it wont be redirecting user to a new page ( because thats exactly how a AJAX works, for more info on AJAX us this link - https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX ), unless you explicitly tells your ajax-code to redirect the user on a new page.
For that you can simply put your page redirection code to a on your ajax success, something like this -
<script>
//your code
$.ajax({
// your code
success : function(data) {
//page redirection code here
window.location.href = "your-base-URL/restaurant/orderdetails/orderId="+orderId;
},
error : function() {
console.log("There was an error");
}
});
}
</script>
PS - This is not efficient programming practice since you're technically making an extra call to your server un-necessarily.
Approach 2nd
Simply make your html table buttin a-href link , like this -
<html>
// your code
<a th:href="#{'/restaurant/orderdetails/orderId=' + ${order.orderId}}">Order details button </a>
//your rest of the code
</html>
Approach-3rd , You can alternatively use java-script function for page redirection as well, simply modify you ajax function , something like this -
function getOrderItems(orderId) {
//page redirection code here
window.location.href = "your-base-URL/restaurant/orderdetails/orderId="+orderId;
}

Related

MVC consideration in Laravel

Is it right to use php code or blade code in view file using laravel?
for MVC consideration is it better to sepearate front end code like HTML with any server side code and keep them in corresponding controller file ?
for example using like this:
in view:
<table>
<Loop> <!-- instate of using #foreach -->
<tr>
<td>
<-UserId->
</td>
<td>
<-UserName->
</td>
</tr>
</Loop>
</table>
in controller:
$html = view('page');
$loop_section=my_own_func_to_get_loop_tag_content($html);
//edit loop_section var with php foreach and return resault to $modified_loop_section var
$html=str_replace($loop_section,$modified_loop_section,$html);
return $html;
Updated:
above code is just an example way that isn't seemed a good way.
but i look for better way to separate any php code (including if foreach etc) with html code in view file without using a custom tag and code?
Yes you should definitely use Blade code in your templates. So function my_own_func_to_get_loop_tag_content should be processed in template file, but it should only show data. All business logic (such as DB querying, sorting, calculations, ...) should be done in your controllers. View is only for displaying data, so there shouldn't be any logic.

ngtable filters not working

I am trying to implement ngtable in order to have out of the box filters and pagination but it seems not to be as easy to implement as they say.
My data is loading correctly in the table, however the actual filters are not responding at all. Below is my table code inside the MyCtrl:
<table ng-table="tableParams" class="table table-condensed table-bordered table-striped">
<tr ng-repeat="movie in items.movies">
<td data-title="'Title'" filter="{ title: 'text'}" sortable="'title'">{{movie.title}}</td>
<td data-title="'Duration'" filter="{duration: 'number'}" sortable="'duration'">{{movie.duration | durationFilter | date:'HH:mm:ss'}} hrs</td>
</tr>
</table>
In the app.js I serve data factory to the controller then trigger the ng table:
app.controller('MyCtrl', function($scope, itemsFactory){
itemsFactory.getItems().success(function(data){
$scope.items = data;
tableParams = new NgTableParams({}, { data: data});
});
});
The result is that all the data is displayed and the input filters above the content, however, neither work. Is there something I am missing?
Supposing your HTML contains something like <table ng-table="tableParams", you want to assign your new NgTableParams to $scope.tableParams so the table can see them. Currently you are assigning a local variable.
Also, enable Javascript strict mode by "use strict". Avoids errors like that.
try to change your code
<tr ng-repeat="movie in items.movies">
to
<tr ng-repeat="movie in $data">

Meteor - Return HTML Template via ajax

I would like to display a template generated in my Meteor app on another website.
I had hoped to use an ajax call in the website to fetch the relevant meteor template and data, but I am struggling.
the website makes the call as follows:
<body>
<div id="result">
</div>
</body>
<script>
$( "#result" ).load( "http://myMeteorApp/get_template" );
</script>
In the Meteor app I have tried to return the template using iron-router:
Router.map(function () {
this.route('get_template', {
where: 'server',
path: 'get_template',
action: function () {
return 'get_template'
}
})
The template 'get_template.html' is in the 'private' folder.
<template name="get_template">
<table class="table table-hover table-condensed">
<tr>
<th>Name</th>
<th>Email</th>
<th>Mobile</th>
</tr>
{{#each members}}
<tr>
<td>{{first_name}} {{last_name}}</td>
<td>{{email}}</td>
<td>{{mobile}}</td>
</tr>
{{/each}}
</table>
This does not work and I have a feeling I am barking up the wrong tree.
Maybe I ought to be using an iframe? But I prefer for the Meteor app to behave like an API and simply return the template.
Help appreciated.
https://github.com/EventedMind/iron-router#server-side-routing:
Server action functions (RouteControllers) have different properties
and methods available. Namely, there is no rendering on the server
yet.
Also, even if that was working there is a lot missing in your javascript:
You don't seem to specify anywhere the data to use in the template
The action function returns a string. What is this supposed to do?
I think your best bet is to use a server-side template engine such as meteor-handlebars-server and just manually render the template:
Router.map(function () {
this.route('serverFile', {
where: 'server',
path: '/get_template/:templatename',
action: function () {
var templatename = this.params.templatename;
this.response.writeHead(200, {'Content-Type': 'text/html'});
this.response.end(Handlebars.templates[templatename]({members: []}));
}
});
});
Inserting the data in the members field of the supplied object.

get previous td element to manipulate in Ajax call

How would I go about grabbing the previous td element in a table in order to change it's text value inside an Ajax call?
This is what i have so far. I think I am grabbing the td element properly and placing it into the variable, however I cannot get an alert to pop up with the text on the td element I'm trying to alter and I can't actually change the text like I'm trying to do.
function ChangeStatus(id) {
var td = $(this).parent('td').prev();
$.ajax({
type: 'GET',
url: '#Url.Action("changeStatus","AgentTransmission")',
data: { id: id },
dataType: 'json',
success: function (data) {
td.empty();
td.html("Hold");
}
});
}
Edit
Here are the two td elements I'm working with. When the user clicks the Hold link w/the ChangeStatus() onclick event, I want to be able to change the text of the previous element from Waiting to Hold.
<td>
Waiting
</td>
<td>
Details
<span> | </span>
<a href="javascript:void(0)" onclick="ChangeStatus(17)" >Hold</a>
</td>
<td align=center id=17>
</td>
First, parent() only get the first parent, if the caller is deeper in the dom, you won't get it. You should use parents(), but i'd recommend using classes instead of tag names, something like this:
$(this).parents('td.myClass').prev();
You can also use:
console.log(myString);
instead of alerts, you can see the result in the console, using chrome or firebug in firefox
Hope it helps
Try getting the td like this:
var td = $(this).closest('td');

html() does not retrieve the whole content

I am getting the following response
<div id="weblogs">
<tr>
<td nowrap class="bl">1</td>
</tr>
<tr>
<td nowrap class="bl">2</td>
</tr>
</div>
Now I am trying to attach the rows like the following:
function _ajax(postData)
{
loadUrl = "getweblogs.asp";
$.ajax( {
url : loadUrl, // your ajax file
type : 'post',
data : postData,
success : function( resp ) {
alert($("#weblogs" , resp).html());
$('#weblogs > tbody:last').append($("#weblogs" , resp).html());
}
});
return false;
}
The replace is working fine. My problem is, that the htmls elements from the response are removed. I'm getting only 1 and 2. instead of
<tr>
<td nowrap class="bl">1</td>
</tr>
<tr>
<td nowrap class="bl">2</td>
</tr>
I don't know what am I doing wrong. Could someone give me any clue?
Thank you!
Greetings
Magda
What you're trying to do is not so clear. Is there already an element with id="weblogs" in the page? If so, why does your response have an element with the same id (not a good idea), and if not, why are you trying to append an element's contents to itself like that (also, not a good idea)?
Why not just change the server-side to send the html required, without a wrapping div tag (which makes it invalid html anyway, another bad idea), and then use it as-is?
Another problem is that you're trying to select .html() of something that will always be an empty jquery object: $(selector, string) will never match anything. You'll need to make the string a jquery object if you want to search its substructure: string = $(string).
And I think you're misunderstanding the use of $(selector, $obj). The selector must be in the $obj's sub-structure:
$('#foo', $('<div id="foo"><span/></div>')); // returns empty jquery object
So looking for #weblogs in the substructure of an element with id weblogs will also never find anything.
Your question isn't entirely clear, but I'll try and answer: are you working with Internet Explorer? If so, consider this paragraph from the docs:
For example, Internet Explorer sometimes leaves off the quotes around attribute values if they contain only alphanumeric characters.
API docs: html()

Resources