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

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";
}

Related

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);
}

How to check if a particular row has validation error for a multi row validation in Spring using BindingResult

The current logic will check if the BindingResult has errors and the display the data and errors in a jsp.
The logic needed is to check Errors for each row and display only those rows containing validation errors and update the rows which don't have validation errors.
#Autowired
private IncidentExtractStgService incidentExtractStgService;
#RequestMapping(value = "/validatingIncidentList", method = RequestMethod.POST)
public String ValidateIncidentList( #Valid #ModelAttribute("incidentsForm") IncidentsForm incidentsForm,
BindingResult bindingResult,RedirectAttributes redirectAttributes) {
if (bindingResult.hasErrors()) {
for(ObjectError error: bindingResult.getAllErrors()){
System.out.println(error);
}
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.incidentsForm", bindingResult);
redirectAttributes.addFlashAttribute("incidentsForm", incidentsForm);
return "redirect:/validateIncidentList";
}
else
{
for(IncidentExtractStg ie : incidentsForm.getIncidents()) {
ie.setValidated(1);
incidentExtractStgService.update(ie);
System.out.println(ie.getNumber()+" "+ie.getWaitTime());
}
return "redirect:/validateIncidentList";
}
The below code snippet will check if the model contains attribute "incidetsForm",if so will send the same to example.jsp which in-turn will display the data and validation errors.
#RequestMapping(value = "/validateIncidentList", method = RequestMethod.GET)
public String incidentList(Model model) {
if (!model.containsAttribute("incidentsForm")) {
List<IncidentExtractStg> incidents = incidentExtractStgDao.validateList();
incidentsForm.setIncidents(incidents);
model.addAttribute("incidentsForm", incidentsForm);
return "example";
}
model.addAttribute("errormessage","Please Check the Validation Errors column for Errors");
return "example";
}
Example.jsp code snippet
<c:forEach var="ie" items="${incidentsForm.incidents}" varStatus="status">
<tr>
<td><form:input path="incidents[${status.index}].id" value="${ie.id}" readonly ="true"/></td>
<td><form:errors path="incidents[${status.index}].id" cssClass="error" /></td>
<td><form:input path="incidents[${status.index}].number" value="${ie.number}"/></td>
<td><form:errors path="incidents[${status.index}].number" cssClass="error" /></td>
</tr>
IncidentsForm.java:
import java.util.List;
import javax.validation.Valid;
import com.infosys.sla.model.IncidentExtractStg;
public class IncidentsForm {
#Valid
private List<IncidentExtractStg> incidents;
public List<IncidentExtractStg> getIncidents() {
return incidents;
}
public void setIncidents(List<IncidentExtractStg> incidents) {
this.incidents = incidents;
}
}
IncidentExtractStg.java snippet
#Entity
#Table(name="incident_extract_stg")
public class IncidentExtractStg {
#Id
#Column(name="ies_id")
private int id;
#NotBlank(message="number cannot be empty")
#Pattern(regexp="[A-Za-z0-9]*",message="number can contain only alphabets and numbers")
#Column(name="ies_number")
private String number;
First of all, if I were you I will extract all the logic inside a Service Layer. To proceed, you could create an interface IncidentService and its own concrete implementation IncidentServiceImpl in which you can safely will handle your needs. Controllers are definitely not done to do everything.
Then, what are your needs?
"check Errors for each row and display only those rows containing validation errors and update the rows which don't have validation errors"
A method inside your Service Layer could be like that:
public void handleErrors(IncidentsForm incidentsForm, BindingResult bindingResult){
List<String> fieldsInErrorState = new ArrayList<String>(10);
if (bindingResult.hasErrors()) { //
Map<String, Object> bindingModel = bindingResult.getModel();
for (Map.Entry<String, Object> entry : bindingModel.entrySet()) {
String key = entry.getKey();
//Object value = entry.getValue(); you don't need to parse that unless you want specific domain model handlers to run
//you need to store the key as a form field which is in error state
fieldsInErrorState.add(key);
//you already have all the stuff to parse and display errors in your JSP
//thanksfully to bindingResult and JSTL tags.
}
ContactMessageForm cmForm2 = new ContactMessageForm();
// get the list of the fields inside your form
Field[] declaredFields = ContactMessageForm.class.getDeclaredFields();
for (Field field : declaredFields) {
if (!fieldsInErrorState.contains(field.getName())) {
if (field.getName().equalsIgnoreCase("firstname")) {
cmForm2.setFirstname(contactMessageForm.getFirstname());
}
if (field.getName().equalsIgnoreCase("lastname")) {
cmForm2.setLastname(contactMessageForm.getLastname());
}
//etc for each properties of your form object.
}
// then store your dbmodel object
// BUT i think you must be carefull to your data integrity... It is maybe not safe to save an object like that with bypassing some stuff...
// Your form was built like that maybe for a good reason looking at your objects graph.
// If your form is too big, then split it in small parts, it will be much easy to handle, to update, and to work with daily.
}
}
}
Of course you need to customize that code, don't forget to add the throws IntrospectionException to your service method, and you are on the good way.
Cheers!
As the logic is to display only those rows containing validation errors, a new List is created to store the rows which are having at least one validation error.
A new BindingResult is created to store the Errors against the index of the new List.(If this is not done then the error message wont be displayed against the displayed row).
The below logic is to check for field error against each field of the row and from the jsp you can see the filed name has "incidents[${status.index}].id".
- Counter i to get the row count
- Counter j is to set the index for BindingResult.
BeanPropertyBindingResult result2 = new BeanPropertyBindingResult(incidentsForm, bindingResult.getObjectName();
List<IncidentExtractStg> incidents= new ArrayList<IncidentExtractStg>();
int i=0;// to get the row count
int j=0;// to set the index
for(IncidentExtractStg ies : incidentsForm.getIncidents())
{
int count=0;
Field[] declaredFields = IncidentExtractStg.class.getDeclaredFields();
for (Field field : declaredFields)
{
if (bindingResult.hasFieldErrors("incidents["+i+"]."+field.getName()))
{
for (FieldError error: bindingResult.getFieldErrors("incidents["+i+"]."+field.getName()))
{
result2.addError(new FieldError(error.getObjectName(), "incidents["+j+"]."+field.getName(), error.getRejectedValue(), error.isBindingFailure(), error.getCodes(), error.getArguments(), error.getDefaultMessage()));
}
count++;
}
}
if(count>0)
{
j++;
incidents.add(ies);
}
else
{
ies.setValidated(1);
incidentExtractStgService.update(ies);
}
i++;
}
i=0;
j=0;
if (bindingResult.hasErrors()) {
incidentsForm.setIncidents(incidents);
System.out.println("error block");
for (FieldError error: result2.getFieldErrors()) {
System.out.println("field errors are "+error.getField());
System.out.println("field errors are "+error);
}
redirectAttributes.addFlashAttribute("org.springframework.validation.BindingResult.incidentsForm", result2);
redirectAttributes.addFlashAttribute("incidentsForm", incidentsForm);
return "redirect:/validateIncidentList";
}
E.g: If in row number 30 ,incidents[30].number field has validation error. Then i=30 and count>0 and j=0.
Hence the entire row incidents[30] will be saved at index 0 in the newly initialized list and the bindingresult will also be added at index 0. If the J is not being set to the result2 and bindingresult is used then it will still point to index 30 and error message will not be displayed against the field which is now stored at index 0.
Now this updated IncidentsForm and result2 will be send to jsp page which will display only those rows having validations errors and there respective error messages.

ASP.NET MVC 4 Want to populate dropdown list from database

I am new guy in ASP.NET MVC 4. I want to populate dropdownlist from database table BO where Column name is Id, Code, Name, OrgId. I want to bind two Code & Namecolumn's data to DataTextfield and Id column Data to DataValueField of dropdown. I have created code for this which are as follows BUT ITS NOT RETURNING DATA FROM TABLE and var BOList is remain empty :
my connectionstring is
<add name="iRegDBContext"
connectionString="Data Source=****;Initial Catalog=iReg;User ID=**;Password=****;Integrated Security=True"
providerName="System.Data.SqlClient"
/>
My Controller class :
public class iRegController : Controller
{
private iRegDBContext l_oDbBO = new iRegDBContext();
// GET: /iReg/
public ActionResult PopulatejQgrid()
{
var BOList = l_oDbBO
.BO
.ToList()
.Select(d => new SelectListItem
{
Value = d.Id.ToString(),
Text = d.Name + "[ " + d.Code + " ]"
});
ViewBag.BOData = new SelectList(BOList, "Value", "Text");
return View();
}
}
My Model class :
public class BO
{
public Guid Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
public class iRegDBContext : DbContext
{
public DbSet<BO> BO { get; set; }
}
My cshtml class :
#model MvciReg.Models.BO
#{
ViewBag.Title = "PopulatejQgrid";
}
#using (Html.BeginForm())
{
<fieldset>
BO :
#Html.DropDownList("BOData")
<p>
<input type="submit" value="Go" />
</p>
</fieldset>
}
I really don't know where I am going wrong. I developed my code from reference of following link Click here . Kindly suggest correction in code ...
UPDATE: I tried following Matt Bodily's code in my controller and what I see is code is not fetching data from database and that code is
public ActionResult populatejQgrid()
{
ViewBag.BOData = GetDropDown();
return View();
}
public static List<SelectListItem> GetDropDown()
{
List<SelectListItem> ls = new List<SelectListItem>();
var lm = from m in db.BOs //fetch data from database
select m;
foreach (var temp in lm)
{
ls.Add(new SelectListItem() { Text = temp.Name, Value = temp.Id.ToString() });
}
return ls;
}
In Controller :
#Html.DropDownList("BOData", (List<SelectListItem>)ViewBag.BOData)
But when I saw value of ls through watch it always show me Count = 0 but its not giving me any error.
I found something new this problem. When I kept mouse pointer over var lm; it shows me query and in query table name in FROM clause is not that one in my SQL database. My SQL table name is BO and in query it is taking BOes. I don't know from where this name is coming. I think this is the main cause of all this problem So How I overcome this??
First Create a BO list for Dropdownlist in VIEW
#{
var Bolst= Model.BO.Select(cl => new SelectListItem
{
Value = cl.Value.ToString(),
Text = cl.Text== null ? String.Empty : cl.Text
});
}
#(Html.DropDownList("sampleDropdown", BOlst, "-----Select-----"))
In Controller:
return View(BOlst); // why use Viewbag when directly pass it to view
from what I see in your code you are creating the select list and setting the ViewBag.BOData on the controller.
So in order to render it on the view you should do this
#Html.DropDownList(ViewBag.BOData)
instead of
#Html.DropDownList("BOData")
Regarding the access to the database are you trying to use "code first" in an existing database?
If you are you need to override the context constructor like this
public class iRegDBContext : DbContext
{
  public iRegDBContext()
     :base("Name= iRegDBContext")
   {
   }
}
see this link http://msdn.microsoft.com/en-us/data/jj200620.aspx
Hope it helps.
try building your dropdown this way
#Html.DropDownList(x => x.Selected, PathToController.GetDropDown())
and then in your controller
public static List<SelectListItem> GetDropDown()
{
List<SelectListItem> ls = new List<SelectListItem>();
lm = (call database);
foreach (var temp in lm)
{
ls.Add(new SelectListItem() { Text = temp.name, Value = temp.id });
}
return ls;
}
Hopefully this helps
I recently had this issue also and managed to get it working using Viewbag. You will need to make it fit your Db tables but it works and is quite simple.
Populating Drop Down Box with Db Data

MVC3 error when passing a model from controller to view while using a viewModel

I'm sorry, for I'm sure there's a way to do this with a viewModel, however I'm very inexperienced with this and don't even know if I'm doing it correctly.
What I'm trying to do is pass multiple blogs and the profile info of the user who posted each blog to a view.
I'm getting the following error.
The model item passed into the dictionary is of type
'ACapture.Models.ViewModels.BlogViewModel', but this dictionary
requires a model item of type
'System.Collections.Generic.IEnumerable`1[ACapture.Models.ViewModels.BlogViewModel]'.
I'm trying to pass the following query results to the view.
var results = (from r in db.Blog.AsEnumerable()
join a in db.Profile on r.AccountID equals a.AccountID
select new { r, a });
return View(new BlogViewModel(results.ToList()));
}
This is my viewModel
public class BlogViewModel
{
private object p;
public BlogViewModel(object p)
{
this.p = p;
}
}
And my view
#model IEnumerable<ACapture.Models.ViewModels.BlogViewModel>
#{
ViewBag.Title = "Home Page";
}
<div class="Forum">
<p>The Forum</p>
#foreach (var item in Model)
{
<div class="ForumChild">
<img src="#item.image.img_path" alt="Not Found" />
<br />
<table>
#foreach (var comment in item.comment)
{
<tr><td></td><td>#comment.Commentation</td></tr>
}
</table>
</div>
}
</div>
Thanks in advance.
I guess you need to change your view model a little to:
public class BlogViewModel
{
public Blog Blog { get; set; }
public Profile Profile{ get; set; }
}
and then return it as follow:
var results = (from r in db.Blog.AsEnumerable()
join a in db.Profile on r.AccountID equals a.AccountID
select new new BlogViewModel { Blog = r, Profile = a });
return View(results.ToList());
Then in your foreach loop inside of view, you will get an objects that will contain both - profile and blog info, so you can use it like f.e. #item.Profile.Username
I'm not entirely sure what you're trying to accomplish with the ViewModel in this case, but it seems like you are expecting for the page to represent a single blog with a collection of comments. In this case you should replace
IEnumerable<ACapture.Models.ViewModels.BlogViewModel>
With
ACapture.Models.ViewModels.BlogViewModel
Then Model represents a single BlogViewModel, that you can iterate over the comments by using Model.comments and access the image using Model.image.img_path.
If this not the case, and you intend to have multiple BlogViewModels per page, then you will have to actually construct a collection of BlogViewModels and pass that to the view instead.

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