I'm trying to include the view service which already retrieve data from my database to my index.html page. I'm using Spring and Thymeleaf.
This is my service controller
#Controller
public class MyServicesController{
#Autowired
private ServicesForOurServices servicesForOurServices;
#RequestMapping(value = "myservices",method = RequestMethod.GET)
public String myServices(Model model){
model.addAttribute("services",servicesForOurServices.listAll());
return "services";
}
#RequestMapping(value = "services",method = RequestMethod.GET)
public ModelAndView listAll() {
ModelAndView modelAndView = new ModelAndView("services");
modelAndView.addObject("services",servicesForOurServices.listAll());
return modelAndView;
}
#ModelAttribute("services")
public Iterable<MyServices> services() {
System.out.println("Inside service without model");
return servicesForOurServices.listAll();
}
}
And my service template view
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head lang="en">
<title>Services</title>
<!--/*/ <th:block th:include="fragments/headerinc :: head"></th:block> /*/-->
</head>
<body th:fragment="services">
<!--/*/ <th:block th:include="fragments/header :: header"></th:block> /*/-->
<div th:if="${not #lists.isEmpty(services)}" class="container-fluid text-center" id="services">
<h2>SERVICES</h2>
<h4>What we offer</h4>
<div th:each="service : ${services}">
<div class="col-sm-4">
<span th:class="${service.getIcon()}"></span>
<h4 th:text="${service.getName()}"></h4>
<p th:text="${service.getDescription()}"></p>
</div>
</div>
</div>
</body>
</html>
Then I include the fragment to my index.html
<!--/*/ <th:block th:include="services :: #services"></th:block> /*/-->
But the index.html only render the h2 and h4 tag and not the div containing the services
and if I access the services.html everything renders fine
Thanks in advance
Have you inspect the elements from your browser to see whether there is any error fetching pictures? Suspecting the URL is incorrect after rendering.
Related
How do you get dynamically loaded tabs to work in ASP.Net Core MVC?
I have a simple Index.cshtml that uses bootstrap tabs to create two tabs from the a tags on the page. (To test out options, I first copied from https://qawithexperts.com/article/asp.net/bootstrap-tabs-with-dynamic-content-loading-in-aspnet-mvc/176)
There is a click event on each tab that uses $.ajax() to call the controller and then set the html of the appropriate div.
I have a model with one field, a string that is required.
I have the create view that Visual Studio created.
When I run it and click the first tab, the controller returns PartialView("FirstTabCreate") and loads into the div and everything looks great.
The problem is when clicking the "Create" button.
The controller method checks if IsValid on the ModelState. If not, here is where I run into a problem. If I return the partial view and the model that was passed in I see my validation errors as expected but because I returned the partial view, I lose my tabs. If I return the main view (Index) then the javascript reloads my partial view and has lost the ModelState at that point.
I am not sure what to return so that this works. I have seen lots of examples online that use dynamically loaded tabs but none of them have models or validation.
Code below:
Index Page
#model FirstTab
<!-- Tab Buttons -->
<ul id="tabstrip" class="nav nav-tabs" role="tablist">
<li class="active">
Submission
</li>
<li>
Search
</li>
</ul>
<!-- Tab Content Containers -->
<div class="tab-content">
<div class="tab-pane active" id="FirstTab">
</div>
<div class="tab-pane fade" id="SecondTab">
</div>
</div>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script>
$('#tabstrip a').click(function (e) {
e.preventDefault();
var tabID = $(this).attr("href").substr(1);
$(".tab-pane").each(function () {
console.log("clearing " + $(this).attr("id") + " tab");
$(this).empty();
});
$.ajax({
url: "/#ViewContext.RouteData.Values["controller"]/" + tabID,
cache: false,
type: "get",
dataType: "html",
success: function (result) {
$("#" + tabID).html(result);
}
});
$(this).tab('show');
});
$(document).ready(function () {
$('#tabstrip a')[0].click();
});
</script>
FirstTabCreate View
#model WebApplication1.Models.FirstTab
<h4>FirstTab</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="FirstTabCreate">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="FirstName" class="control-label"></label>
<input asp-for="FirstName" class="form-control" />
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
Model
using System.ComponentModel.DataAnnotations;
namespace WebApplication1.Models
{
public class FirstTab
{
[Required()]
public string FirstName { get; set; }
}
}
Controller
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using WebApplication1.Models;
namespace WebApplication1.Controllers
{
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public ActionResult FirstTab()
{
return PartialView("FirstTabCreate");
}
public ActionResult FirstTabCreate(FirstTab model)
{
if (!ModelState.IsValid)
{
return View("FirstTabCreate", model);
}
return Content("Success");
}
public ActionResult SecondTab()
{
return PartialView("_SecondTab");
}
}
}
I don't like it but to get it to work, when I click Save, in the Controller method I check if the ModelState is valid. If not, I put the keys and values into a list of custom class and then put that list in the cache. When the child partial view loads it checks to see if there is anything in the cache and if so, parses it back out and uses ModelState.AddModelError().
It's not pretty but it does allow the validation to work.
try to add jquery validation scripts in your code
delete this
<script src="~/lib/jquery/dist/jquery.min.js"></script>
and use this instead
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Add below code to your #section Scripts
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
<script>
$.validator.setDefaults({
ignore: []
});
</script>
}
Note: do not add the above inside $(document).ready()
I have a problem about removing attributes when I navigate pages.
I used PartialView in HomeController to define index.cshtml page.
The line which I pass the data to the destination is showed orderly.
HomeController -> Contract.cshtml -> _Layout.cshtml -> NavbarPartial.cshtml
Here is HomeController.cs file shown below.
public ActionResult ContractUs()
{
ViewBag.Attribute = ""; // header-transparent
return View();
}
Here is my Contract.cshtml shown below.
#{
ViewBag.Title = "Contract Us";
Layout = "~/Views/Shared/_Layout.cshtml";
}
Here is my _Layout.cshtml shown below.
Html.RenderAction("NavbarPartial", "Home");
Here is my NavbarPartial.cshtml shown below.
<header id="header" class="fixed-top d-flex align-items-center header-transparent">
</header>
What I want to do is to show this header code snippet in the index page and show another one on another page like this shown as below without changing NavbarPartial.cshtml.
Index.cshtml
<header id="header" class="fixed-top d-flex align-items-center header-transparent">
</header>
Contract.cshtml
<header id="header" class="fixed-top d-flex align-items-center">
</header>
How can I do that?
If you render your partial view from another view, then you can send some parameters to decide what class should be applied. So let's look at the example.
We call partial view and send additional data:
#{Html.RenderPartial("_Header", new ViewDataDictionary { { "ApplyStyle", true } });}
and then in partial view we can apply style conditionally:
#{
var yourClasses = "";
if ((bool)ViewData["ApplyStyle"])
{
yourClasses = "fixed-top d-flex align-items-center header-transparent";
}
else
{
yourClasses = "fixed-top d-flex align-items-center";
}
}
<header id="header" class="#yourClasses">
This is header!
</header>
UPDATE:
This is a fiddle with complete example. However, this fiddle does not support PartialView, but I believe basic idea is shown.
Try using conditions in class value.
For example:
<header id="header" class="fixed-top d-flex align-items-center #(#Model.name === 'index' ? 'header-transparent' : '')">
If you dont have any kind of variable name for current component, you can use Javascript's location object.
window.location.pathname will provide you the pathname of current page.
Using this value you can add conditional class values
I am trying to call child action from view as below
#{
Html.RenderAction("Render", "ProgressBar", new { total = 10, completed = 3 });
};
and my controller code is as below
public class ProgressBarController : Controller
{
// GET: ProgressBar
[ChildActionOnly]
public ActionResult Render(int total, int completed)
{
ViewBag.Total = total;
ViewBag.Completed = completed;
ViewBag.Percent = ((completed * 100) / total).ToString("0.#####");
return PartialView();
}
}
and partial view for Render
<div class="section-progress-wrapper">
<div class="section-progress-info clearfix">
<div class="section-progress-label float-l">
<span class="section-progress-output">#ViewBag.Completed</span>
<span class="section-progress-desc">of</span>
<span class="section-progress-total">#ViewBag.Total</span> modules completed
</div>
<div class="section-progress-icon float-r"></div>
</div>
<div class="section-progress-box">
<div id="progressBar" class="section-progress-range" style="width:#string.Format("{0}%",ViewBag.Percent)"></div>
</div>
</div>
but for some reason I am getting exception saying
System.ArgumentException: Illegal characters in path.
I am not able to figure out what I am doing wrong. Can someone please help?
So, I'm trying to create comments on a post using spring mvc, spring boot, spring data, jpa, and thymeleaf, and so far I can get to the specific page I want, using the controller and pathvariables, and I can load up the page just how I want, but when I go to submit the comment I get the error
There was an unexpected error (type=Bad Request, status=400).
Failed to convert value of type 'java.lang.String' to required type 'com.example.domain.Comment'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.lang.String to type java.lang.Long for value 'comment 1'; nested exception is java.lang.NumberFormatException: For input string: "comment1"
This error is only in my browser, nothing comes up in the console in my IDE. Also I can access the page just fine, so there I don't think there's an issue in my get method in my controller, but I'm not really sure where the problem is, so I'll show you guys some of my code.
Here's my controller.
private PostRepository postRepo;
#RequestMapping(value="viewCourse/post/{postId}", method=RequestMethod.GET)
public String postViewGet (#PathVariable Long postId, ModelMap model)
{
Post post = postRepo.findOne(postId);
model.put("post", post);
Comment comment = new Comment();
model.put("comment", comment);
return "post";
}
#RequestMapping(value="viewCourse/post/{postId}", method=RequestMethod.POST)
public String postViewPost (#ModelAttribute Comment comment, #PathVariable Long postId, ModelMap model)
{
Post post = postRepo.findOne(postId);
comment.setPost(post);
post.getComments().add(comment);
postRepo.save(post);
return "redirect:/viewCourse/{postId}";
}
#Autowired
public void setPostRepo(PostRepository postRepo) {
this.postRepo = postRepo;
}
Here's my thymeleaf html page
<div class="PostContent">
<h2 th:text = "${post.title}"></h2>
<p th:text = "${post.content}"></p>
</div>
<br/>
<div class="CommentPost">
<form th:action="${post.id}" method="post" th:object="${comment}" id="comment">
<div class="form-group">
<textarea rows="2" th:field="${comment.comment}" class="form-control" placeholder="comment" id="comment"></textarea>
</div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<input type="submit" value="Comment" class="btn btn-success"/>
</form>
</div>
<br/>
<div class="Comments">
<div th:each = "comment : ${comments}" th:object="${comment}">
<span th:text="${comment.comment}"></span>
</div>
<div th:if = "${#lists.isEmpty(comments)}">
There are no comments to display
</div>
</div>
</div>
Also on this page the message comes up "There are no comments to display", just like I tell it to in the code, but it still says "There are no comments to display" even if I manually insert a comment into the database.
Here's my comment object, although I'm pretty sure that's fine.
#Entity
public class Comment {
public Long id;
public String comment;
public Post post;
public User user;
#Id
#GeneratedValue
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
#ManyToOne
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
#ManyToOne
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
And my postRepo, although this should be fine, just thought I'd include it
public interface PostRepository extends JpaRepository <Post, Long>{
}
If anyone can see my issue, and let me know, that would be awesome, thanks.
When you use th:object don't have to reference to object, you access directly atributes of object. Try with this code:
<div class="PostContent">
<h2 th:text = "${post.title}"></h2>
<p th:text = "${post.content}"></p>
</div>
<br/>
<div class="CommentPost">
<form th:action="${post.id}" method="post" th:object="${comment}" id="comment">
<div class="form-group">
<textarea rows="2" th:field="*{comment}" class="form-control" placeholder="comment" id="comment"></textarea>
</div>
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
<input type="submit" value="Comment" class="btn btn-success"/>
</form>
</div>
<br/>
I don't see in the controller where you put the comments in the model. I suppose that comments there are inside the post so modify the refereces of comments to post.comments
<div th:each = "comment : ${post.comments}" th:object="${comment}">
<span th:text="*{comment}"></span>
</div>
<div th:if = "${#lists.isEmpty(post.comments)}">
There are no comments to display
</div>
</div>
</div>
The problem is that the name of Class - Comment - and the field - comment - are the same, regarding to insensitive way, causing problem due to Java Reflection use to read the field and its class.
The solution was to rename the field, like "comment" to "commentary", and to avoid to change again in database, if there is some, just put the annotation #Column(name="comment") above the field.
Perhaps, reference from main template on thymeleaf template in question look like:
th:href="#{/post/{${post.getId()}}",
but it should look like:
th:href="#{/post/{postId}(postId=${post.getId()})}"
In my occasion, it helped me
I want to know what is the current url of my template. I have read here that the "request" is an implicit object present in all templates which returns the url of my template.
So I have tried this:
Controllers:
public class Application extends Controller {
public static Result index() {
return redirect("/home");
}
public static Result home() {
return ok(homePage.render());
}
public static Result aboutUs() {
return ok(aboutUs.render());
}
}
HTML:
leftbar.scala.html file:
<aside id="left-panel">
<nav>
<ul class="animated fadeInLeft">
<li class="#if(request.uri.contains("/aboutus")){active}">(some code here)</li>
</ul>
</nav>
</aside>
homePage.scala.html file:
#scripts = { (some scripts here) }
#views.html.main("Beta Project", scripts) {
#views.html.leftbar()
<div id="main" role="main">
<div id="content">
<span><i class="fa fa-bell"></i>SOMETHING</span>
</div>
</div>
}
Where I import the leftbar.scala.html file I get this error:
not found: value request
What should I do to solve this error? Thanks in advance
A bit more code from your template would be useful, but you can probably fix it by adding
(implicit request: Request[AnyContent])
at the end of the first line on your template.