How to pass a more dynamic structure to jsp Spring Forms? - spring

I have a structure:
public class Tabl {
String tableName;
List<String> tableColumns;
public Tabl() {
}
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public List<String> getTableColumns() {
return tableColumns;
}
public void setTableColumns(List<String> tableColumns) {
this.tableColumns = tableColumns;
}
}
This form is emulating a table dynamically so to say. I have lots of simple tables with 1-2 columns except the id. I don't have an explicit assignment of fields as I have only one dynamic entity to hold them. The point of my question here is that I want to pass this tabl object and prepare a Spring form in jsp like this (I pass tableName from another JSP and it's ok:
#RequestMapping(value = "/addnew/{tableName}")
public String insertForm(#PathVariable("tableName") String tableName, Model m) {
Tabl tabl = new Tabl();
tabl.setTableName(tableName);
List<String> cols = spravochnikService.selectCols(tableName); //prepares structure by getting column names of the needed table (except the id): for example tableName=Employee, cols are firstname and lastname
tabl.setTableColumns(cols);
m.addAttribute("command", tabl);
return "insert_form"
}
insert_form.jsp:
<f:form action="${url_csave}">
<table border="1">
<c:forEach var="col" items="${command.tableColumns}">
<tr>
<td>
<c:out value="col" />
<f:input path="col" />
</td>
</tr>
</c:forEach>
<tr>
<td colspan="2" align="right">
<button>Save</button>
</td>
</tr>
</table>
</f:form>
When I try to do it it says invalid property "firstname" and invalid getter method. I can't have an explicit getter for it of course as all my tables are all in one dynamic entity. How to make Spring Forms understand the structure?

Related

How can I get data from from table2 by sending corresponding ID from table1 using thymleaf

getFunction() in database class:
public Review getReviews(Long id) {
MapSqlParameterSource namedParameters = new MapSqlParameterSource();
String query = "SELECT * FROM reviews WHERE id = :id";
namedParameters.addValue("id", id);
BeanPropertyRowMapper<Review> reviewMapper = new BeanPropertyRowMapper<Review>(Review.class);
List<Review> reviews = jdbc.query(query, namedParameters, reviewMapper);
if (reviews.isEmpty()) {
return null;
} else {
return reviews.get(0);
}
}
Controller class:
#GetMapping("/viewReview")
public String viewReviews(Model model,Long id) {
List<Review> review = da.getReviews(id);
model.addAttribute("reviewList", review);
return "view-book";
}
HTML:
<tr th:each="book : ${bookList}">
<td th:text="${book.title}"></td>
<td th:text="${book.author}"></td>
<td> View</td>
</tr>
I have two tables 'books' and 'reviews'. When I click the first book it should display the corresponding review of the book on the next page. But instead shows 404 error. Im guessing Im missing something in my database class but not sure what.
I'm not very familiar with Thymeleaf, but looking at your Controller (and the error message), the problem could be with your Controller than Database class.
Your viewReviews method is mapped for /viewReview, but you are trying to navigate to /viewReview/BOOK_ID, which is why you are getting the 404 error message.
Try updating your Controller as:
#GetMapping("/viewReview/{id}")
public String viewReviews(Model model, #PathVariable("id") Long id) {
List<Review> review = da.getReviews(id);
model.addAttribute("reviewList", review);
return "view-book";
}

Getting Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[ProjectName.DTOs.ProjectDTO] instead of actual Data

I am working on my .NET MVC Core project. I am trying to get the project name by joining two tables using LINQ in my Controller.
My Controller code:
public IActionResult Projects()
{
var projectName = from p in _context.Tbl_Projects
join d in _context.Tbl_MyOfficeDocumets on p.Id equals d.Project_ID
select new ProjectDTO { ProjectName = p.ProjectName };
ProjectDTO proj = new ProjectDTO { ProjectName = projectName; }
return View(proj);
}
And Razor view page:
<table>
<tr>
<td>Your Projects</td>
<td>Description</td>
<td>Created On</td>
</tr>
#foreach(var item in Model.listOfProjects)
<tr>
<td>#Model.ProjectName</td> <-- Here I am getting that unusual response
<td>#item.Description</td>
<td>#item.CreatedOn</td>
</tr>
</table>
The #Model.ProjectName shows:
Getting Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1[ProjectName.DTOs.ProjectDTO]
Am I missing something?
You have created query and assigned it to DTO, which is wrong.
public IActionResult Projects()
{
var query = from p in _context.Tbl_Projects
join d in _context.Tbl_MyOfficeDocumets on p.Id equals d.Project_ID
select new ProjectDTO { ProjectName = p.ProjectName };
return View(query);
}

Spring & Thymeleaf: Refresh single row in table

I refresh content of a table with:
<tr id="rows" th:each="entity: ${entities}">
<!-- ... -->
</tr>
and on server side:
#RequestMapping(value = "/replace", method = RequestMethod.GET)
public String replace(Model model) {
model.addAttribute("entities", entities);
return "/ :: #rows";
}
calling the method with:
$.get('/replace', function(fragment) {
// fragment gives me all rows of the table
}
which works as expected. However, I do not want to update all rows every time, but only a single one, so my idea was to use a dynamic id as follows:
<tr th:id="'row' + ${entity.id}" th:each="entity, iterStat: ${entities}">
<!-- ... -->
</tr>
and on server side:
#RequestMapping(value = "/replace", method = RequestMethod.GET)
public String replace(#RequestParam("id") int id, Model model) {
model.addAttribute("entities", entities);
return "/ :: #row" + id;
}
calling the method with:
$.get('/replace', {id: id} function(fragment) {
// fragment is empty
}
but that doesn't work. Looking a the html code and server side, the id is correct. Is it not possible to use a th:id? What would be a workaround if I only want to update one row in a table and not all rows? Thanks.
I also tried the same with th:fragment - it works without ${entity.id} and does not with.
Using th:fragment should do the trick. I normally use them to accomplish what you are trying to do. This is an example of how to implement it.
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns="http://www.w3.org/1999/xhtml">
<head></head>
<body>
<th:block th:fragment="entity-row">
<tr th:id="'row'+${entity.id}">
<td th:text=${entity.id}></td>
<!-- Whatever other element you need. -->
</tr>
</th:block>
</body>
Add the code above in a whole a new html file. Remember, we are expecting one single entity, so we don't need the th:each. Now in order to fetch the fragment, you would need to the following in your Controller.
#RequestMapping(value = "/replace/fragment", method = RequestMethod.GET)
#ResponseBody
public String replaceSingleRow(#RequestParam("id") int id, Model model) {
Entity entity = entityService.findById(id);
model.addAttribute("entity", entity);
return "file :: entity-row";
}
Where file would be the name of the html file that contains the fragment. Of course, I am assuming you are passing only the desired id to our controller.
Very late response, but I think this is the ultimate solution.
Thymeleaf fragment
<tr th:fragment="entity-row" th:id="'row' + ${entity.id}" th:each="entity, iterStat: ${entities}">
<!-- ... -->
</tr>
Controller code
#RequestMapping(value = "/replace", method = RequestMethod.GET)
public String replace(#RequestParam("id") int id, Model model) {
Entity entity = entityService.findById(id);
model.addAttribute("entities", Arrays.asList(entity)); // sending the row as list, so that the loop can iterate and will produce a single row
return "file :: entity-row";
}
You have the desired single row html in your ajax response, now you can replace the existing row
$.get('/replace', {id: id} function(fragment) {
$("#row" + id).replaceWith(fragment);
}
I was able to send only one entity to the view with:
Entity entity = entityService.findById(id);
model.addAttribute("entities", entity);
It is for sure not the prettiest way, but it gives the desired result.

asp.net mvc linq I want to fetch two columns from two join table

I have two tables join with PrId column, I have a view which show two columns from both tables, first column from first table and second column from second table. my actionresult is :
public ActionResult extrapoints()
{
ViewBag.dList = (from m in _session.customer
join p in _session.Products on m.PrId equals p.PrId
where m.UserId== 'john'
select new { FName = m.FName, price=p.price});
return View();
}
and in view i want to show both FName and price, I have following view :
#foreach (var item in ViewBag.dList)
{
<tr>
<td>#item.FName </td>
<td> #item.price</td>
</tr>
}
but is show error object' does not contain a definition for FName but when i use without Fname,price like
#foreach (var item in ViewBag.dList)
{
<tr>
<td>#item</td>
<td> #item</td>
</tr>
}
is shows :
{ FName = Shailendra, price= 1000 }
how to solve, please help
You Can Convert the Anonymous class to typed class, first add this class
class Test
{
public string FName { get; set; }
public decimal price { get; set; }
}
and make you linq statment as
ViewBag.dList = (from m in _session.customer
join p in _session.Products on m.PrId equals p.PrId
where m.UserId== 'john'
select new Test{ FName = m.FName, price=p.price});
and finally Cast the viewpage to type "Test"
#foreach (var item in (List<Test>)ViewBag.dList)
{
<tr>
<td>#item.Fname</td>
<td> #item.price</td>
</tr>
}

How can I do model binding and data display at the same time using the same model.list?

I have a code similar to this:
My View
#model MyCode.Models.RemarksModel
foreach (var row in Model.List)
{
<tr>
<td align="center"><font class="font_table_content">#row.Id</font></td>
<td align="center"><font class="font_table_content">#row.Full_Name</font></td>
<td align="center"><font class="font_table_content">#Html.TextBox("row.Remarks")</font></td>
}
My Model
public class RemarksModel
{
public IList<UserRemarks> List { get; set; }
}
My UserRemarks Object
public class UserRemarks
{
public virtual string Id { get; set; }
public virtual string Full_Name { get; set; }
public virtual string Remarks { get; set; }
}
Next in my controller, I will have some code that will load the records into a IList from the DB via Nhibernate, and then return the view with the list inside the model, something like this:
[HttpGet]
public ActionResult RemarksTest()
{
RemarksModel model = new RemarksModel();
model.List = LoadTheList();
return View(model);
}
Now, what I want to ask is, how am I able to receive the list back, i.e. get the remarks value back?
[HttpPost]
public ActionResult RemarksTest(RemarksModel model)
{
var list = model.list;
foreach (var remarks in list)
{
//do something to save the input data into code.
}
return View(model);
}
The actual code is more complex, and I've read about those IDictionary methods to receive the values. However implementing them, will cause the values not to be displayed instead, since the code no longer refers to the model.list.
Any ideas how can I display, and yet also, receive the data using the same 'list' inside my model above?
I think you need change your view as follows:
#model IEnumerable<UserRemarks>
#using(Html.BeginForm())
{
for(int i=0; i<#Model.Count; i++)
{
<table>
<tr>
<td align="center"><font class="font_table_content">#Html.TextboxFor(m => m[i].Id)</font></td>
<td align="center"><font class="font_table_content">#Html.TextboxFor(m => m[i].Full_Name</font></td>
<td align="center"><font class="font_table_content">#Html.TextboxFor(m => m[i].Remarks")</font></td>
</tr>
</table>
}
<input type="submit" value="submit"/>
}
Then the get action should be changed to:
[HttpGet]
public ActionResult RemarksTest()
{
List<UserRermarks> model = LoadTheList();
return View(model);
}
And the post action changed to:
[HttpPost]
public ActionResult RemarksTest(IEnumerable<UserRemarks> model)
{
foreach (var remarks in model)
{
//do something to save the input data into code.
}
return View(model);
}
Unfortunately I don't have visual studio on the computer I am working on so I am unable to test the above but the logic should be correct so if a problem occurs it may just be that I have typed something slightly a miss.
Hope this works for you.
In your GET controller action you could store the list in TempData:
[HttpGet]
public ActionResult RemarksTest()
{
RemarksModel model = new RemarksModel();
model.List = LoadTheList();
TempData['List'] = model.List;
return View(model);
}
Then in your POST action retrieve it with:
var myList = TempData['List']
If your app is stateless (i.e. no sessions), then you can use a cookie-based TempData provider.
The problem is you are just displaying the values.
To post values to server back, you need to have some inputs (like, textbox,checkbox,radiobutton,hiddenfields). In your case you can define hidden fields ,so that model binder will bind them to List of UserRemarks.
Follow http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx to bind a list to model.

Resources