If I remove the div tag from the source code below my application runs with no error. But it displays an empty cell (which is correct). I just want to hide this if the cells are empty.
Thymeleaf html
<div th:object="${AppPortModel.Status}" th:if="${AppPortModel.Status} == 'CRITICAL'">
<h3>
MONITORING
</h3>
<table id ="apCritTable">
<thead>
<tr>
<th> Status </th>
<th> HostName </th>
<th> Port Name</th>
<th> Port Listening Count </th>
</tr>
</thead>
<tbody>
<tr th:each="AppPortModel, iterStat : ${showap}" th:if="${AppPortModel.Status == 'CRITICAL'}">
<td th:text ="${AppPortModel.Status}"></td>
<td th:text="${AppPortModel.host}">${AppPortModel.host}</td>
<td th:text="${AppPortModel.portOwner}"></td>
<td th:text="${AppPortModel.count}"></td>
</tr>
</tbody>
</table>
</div>
AppPortModel
public class AppPortModel implements Comparable {
private String Status;
private String host;
private String portName;
private String plCount;
//getters and setters
#Override int compareTo(Object o) {
return //stuff
}
Controller
#Controller
public class IndexController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView getdata() throws IOException {
ModelAndView model = new ModelAndView("index");
model.addObject("showap", apList);
return model;
}
AppPortList
#Component
public class AppPortList {
#Value("#{'$APP_SERVERS_PORT}'.split('#!')}")
private String[] apServerArray;
#Value("#{'${APP_SERVER_MONITORING_LIST}'.split('#!')}")
private String[] appServerPortsList;
#PostConstruct
public List<AppPortModel> getAppPortList() {
final int MYTHREADS = 80;
ExecutorService executor = Executors.newFixedThreadPool(MYTHREADS);
ApplicationPort.resetData();
try {
for (int z = 0; z < apServerArray.length; z++) {
String apServer = apServerArray[z];
String[] portListArray=appServerPortsList[z].split(",");
ApplicationPort apWorker = new ApplicationPort(apServer, portListArray);
executor.execute(apWorker);
}
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println("ArrayIndexOutOfBoundsException in AppPortList");
}
finally {
executor.shutdown();
while (!executor.isTerminated()) {
}
logger.info("\nFinished all threads in App Port. ");
}
return ApplicationPort.getData();
}
Snippet of Class App
static List<AppPortModel> apData = new ArrayList<AppPortModel>();
public static List<AppPortModel> getData() {
return apData;
}
public static void setData(List<AppPortModel> apData) {
ApplicationPort.apData = apData;
}
public static void resetData(){
apData = new ArrayList<AppPortModel>();
}
public ApplicationPort(String apServer, String[] portListArray) {
this.apServer = apServer;
this.portListArray = portListArray;
}
This table will populate if AppPortModel.Status is CRITICAL. I am trying to hide this table if there is no values in this table. If I just have a regular div tag my code will run but I will have a awkward head and table row heads on my page with empty cells.
Attempt
I tried adding some th logic into my div tag, but I receive an null error.
<div th:object="${AppPortModel.Status}" th:if="${AppPortModel.Status == 'CRITICAL'}">
Attempt 2
<div th:if="${Status} == 'CRITICAL'">
This script would hide my div tag. Even if I have Status = to CRITICAL it would still hide the table.
You can check whether the list is empty using the following condition expression. Assuming the object showap is a List type,
<div th:if="${not #lists.isEmpty(showap)}">
--content--
</div>
Your h3 tag and table goes inside this div.
#lists is a thymeleaf utility class. Refer http://www.thymeleaf.org/apidocs/thymeleaf/2.0.2/org/thymeleaf/expression/Lists.html for more options. You can alternatively use size() method and check for list length.
Related
I have a Blazor server app. Some variables on a specific razor page (main.razor) are defined as static because I want that these variables keep their values when the client navigates to other pages in the same project and comes back again to main.razor. So far it is working good.
But when I refresh the complete page, or even close the tab and reopen my app (login again), I see that the static variables still keep their values. How can prevent this? Of course I want that the values return to their default values (like 0 or ""), when the client makes a login or refreshes the page with F5. How can I do that?
I have defined the related variables in the following way:
private static StringBuilder log = new StringBuilder();
public static string testvar1= "";
public static int testvar2= 0;
Statics exist for the lifetime of the application instance which explains the behaviour you see.
You need to be maintaining state. At one end of the spectrum you can implement a State Management system such as Fluxor. At the other just create a user class, set it up as a service and inject it as a Scoped Service. Or you can build a middle-of-the-road solution.
This is mine.
A generic UIStateService that maintains a Dictionary of (state)objects against a Guid.
public class UIStateService
{
private Dictionary<Guid, object> _stateItems = new Dictionary<Guid, object>();
public void AddStateData(Guid Id, object value)
{
if (_stateItems.ContainsKey(Id))
_stateItems[Id] = value;
else
_stateItems.Add(Id, value);
}
public void ClearStateData(Guid Id)
{
if (_stateItems.ContainsKey(Id))
_stateItems.Remove(Id);
}
public bool TryGetStateData<T>(Guid Id, out T? value)
{
value = default;
if (Id == Guid.Empty)
return false;
var isdata = _stateItems.ContainsKey(Id);
var val = isdata
? _stateItems[Id]
: default;
if (val is T)
{
value = (T)val;
return true;
}
return false;
}
}
Set it up as a service:
builder.Services.AddScoped<UIStateService>();
Next define a simple template ComponentBase page that contains the common page code:
using Blazr.UI;
using Microsoft.AspNetCore.Components;
namespace BlazorApp2.Pages
{
public class StatePage : ComponentBase
{
// this provides a guid for this specific page during the lifetime of the application runtime
// we use this as the reference to store the state data against
private static Guid RouteId = Guid.NewGuid();
[Inject] protected UIStateService UIStateService { get; set; } = default!;
protected void SaveState<T>(T state) where T : class, new()
{
if (RouteId != Guid.Empty)
this.UIStateService.AddStateData(RouteId, state);
}
protected bool GetState<T>( out T value) where T : class, new()
{
value = new T();
if (RouteId != Guid.Empty && this.UIStateService.TryGetStateData<T>(RouteId, out T? returnedState))
{
value = returnedState ?? new T();
return true;
}
else
return false;
}
}
}
And use it in a page:
#page "/"
#inherits StatePage
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
<div class="p-2">
<button class="btn btn-primary" #onclick=SetData>Set Data</button>
</div>
<div class="p-3 text-primary">
State Time : #stateData.StateTime;
</div>
#code {
private MyStateData stateData = new MyStateData();
protected override void OnInitialized()
{
if (this.GetState<MyStateData>(out MyStateData value))
this.stateData = value;
else
this.SaveState<MyStateData>(this.stateData);
}
private void SetData()
{
this.stateData.StateTime = DateTime.Now.ToLongTimeString();
SaveState<MyStateData>(this.stateData);
}
public class MyStateData
{
public string StateTime { get; set; } = DateTime.Now.ToLongTimeString();
}
}
You can now navigate around the application and the state will be maintained for the page.
You can apply an observer/notification pattern to the state object to trigger automatic state updates if you wish.
I have a API Json data that contains a lot of information, but I only need to map one attribute to my model class, say Programname, I don't care of other information. I am using Json converter to DeserializeObject the API data. Is it possible to only map partial Json data to my model class? Right now, I receive error message as below if I only map one attribute of the Json data. Thanks.
InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List`1[WebApplication2.Program]', but this ViewDataDictionary instance requires a model item of type 'System.Collections.Generic.IEnumerable`1[WebApplication2.Models.Program]'.
public class Program
{
public int Prog_id { get; set; }
public string Programname { get; set; }
}
Controller:
public async Task<IActionResult> Index()
{
var Client = new HttpClient();
Client.DefaultRequestHeaders.Accept.Clear();
Client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("appllication/json"));
HttpResponseMessage res = await Client.GetAsync("my url");
if (res.IsSuccessStatusCode)
{
var result = res.Content.ReadAsStringAsync().Result;
List<Program> programs = JsonConvert.DeserializeObject<List<Program>>(result);
return View(programs);
}
else
{
throw new Exception(res.ReasonPhrase);
}
}
View
#model IEnumerable<WebApplication2.Models.Program>
<h1>Index</h1>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
Program
</th>
<th></th>
</tr>
</thead>
<tbody>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Programname)
</td>
</tr>
}
</tbody>
</table>
change view model to list and fix a namespace
#model List<WebApplication2.Program>
and IMHO use await instead of .Result
var result = await res.Content.ReadAsStringAsync();
List<Program> programs = JsonConvert.DeserializeObject<List<Program>>(result);
return View(programs);
I figured it out what the problem was now. The word "Program" is a reserved word in asp.net core. After I changed the Program to something else, it works. :/
I am a newbie at Spring and I am writing a rental system for movies. I have a Controller where I can get a list of all movies which are still rented (marked by digit "1" as a status in my Database) and which are already have been returned(marked as "0").
Now currently, when I call the page I see all the rented movies with the current status "1" or "0" as well as already returned movies which can still be returned and have a checkbox (which shouldn't be possible.
My question is, how can I change the HTML in the following way:
The status "1" or "0" changes to "rented" and "returned"
I want to remove the checkbox on all movies which already have been returned.
My code:
#Controller
public class MovieController {
#Autowired
private MovieService movieService;
#GetMapping("/home")
public String home(Model model) {
return "index";
}
#GetMapping("/movieList")
public String listAllMovies(Model model) {
model.addAttribute("listMovies", movieService.findNotRented());
return "movies";
}
#GetMapping("/search")
public String findByOption(Model model, #RequestParam(value = "search") String search,
#RequestParam("options") String options) {
if (options.equals("movie")) {
model.addAttribute("listMovies", movieService.findByName(search));
} else if (options.equals("actor")) {
model.addAttribute("listMovies", movieService.findMovieByActor(search));
} else if (options.equals("genre")) {
model.addAttribute("listMovies", movieService.findMovieByGenre(search));
}
return "movies";
}
#GetMapping("/rentedList")
public String findRentedMovies(Model model) {
model.addAttribute("listMovies", movieService.findRentedMovies());
return "rented";
}
#GetMapping("/rentMovie")
public String rentMovie(Model model, #RequestParam int id) {
model.addAttribute("listMovies", movieService.rentMovie(id));
return "index";
}
#GetMapping("/deleteRentedMovie")
public String deleterentedMovie(Model model, #RequestParam int id) {
model.addAttribute("listMovies", movieService.deleteRentedMovie(id));
return "index";
}
#GetMapping("/rentMovie2")
public String rentMovie2(Model model, #RequestParam("idChecked") List<Integer> id) {
if (id != null) {
for (Integer idInt : id) {
model.addAttribute("listMovies", movieService.rentMovie(idInt));
}
}
return "index";
}
#GetMapping("/deleteRentedMovie2")
public String deleterentedMovie(Model model, #RequestParam("idChecked") List<Integer> id) {
if (id != null) {
for (Integer idInt : id) {
model.addAttribute("listMovies", movieService.deleteRentedMovie(idInt));
}
}
return "index";
}
}
Thymeleaf
<h1>Movie List</h1>
<form action="/deleteRentedMovie2">
<table>
<tr>
<th>Title</th>
<th>Time rented</th>
<th>Status</th>
<th>Select</th>
</tr>
<tr th:each="movie : ${listMovies}">
<td th:text="${movie.title}"></td>
<td th:text="${movie.date}"></td>
<td th:text="${movie.status}"></td>
<td><input type="checkbox" th:name="idChecked" th:value="${movie.id}"></td>
</tr>
</table>
<input type="submit" value="Return Movie">
</form>
Thank you in advance and sorry for my bad English
For the status I would something like:
<td>
<span th:if="${movie.status == 1}">rented</span>
<span th:if="${movie.status == 0}">returned</span>
</td>
You could also use the Elvis operator but it is maybe less readable.
For the checkbox:
<td><input th:unless="${movie.status == 0}" type="checkbox" th:name="idChecked" th:value="${movie.id}"></td>
I am converting HTML to a PDF using iText 7. I need the PDF to be accessible (508 compliant with appropriate tags, etc), but, no matter what markup I put on a table, accessibility checkers give the same error: "Table header cell has no associated subcells". I've tried setting scope, headers, etc... nothing seems to work. Here is an example of one of the tables but all of them have the same issue:
<table class="problems" summary="Patient's diagnosed problems and associated ICD codes.">
<thead>
<tr>
<th scope="col" id="problem-header">
Problem
</th>
<th scope="col" id="icd-code-header">
Code
</th>
</tr>
</thead>
<tbody>
<tr>
<td headers="problem-header">Some Problem</td>
<td headers="icd-code-header">Some ICD Code</td>
</tr>
</tbody>
</table>
Any help would be appreciated. Thank you so much.
EDIT: I forgot to mention, I am using the .NET version of iText 7.
EDIT 2: Here is the code that converts the HTML to PDF:
public class AccessiblePdfService : IAccessiblePdfService
{
private static readonly string[] FontPaths = ConfigurationManager.AppSettings["FontPaths"].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
public void createPdf(string html, string dest, PdfTypes type = PdfTypes.RefDoc) //string resources
{
FileStream outputStream = new FileStream(dest, FileMode.Create, FileAccess.Write);
WriterProperties writerProperties = new WriterProperties();
//Add metadata
writerProperties.AddXmpMetadata();
PdfWriter pdfWriter = new PdfWriter(outputStream, writerProperties);
PdfDocument pdfDoc = new PdfDocument(pdfWriter);
pdfDoc.GetCatalog().SetLang(new PdfString("en-US"));
//Set the document to be tagged
pdfDoc.SetTagged();
pdfDoc.GetCatalog().SetViewerPreferences(new PdfViewerPreferences().SetDisplayDocTitle(true));
//Set meta tags
PdfDocumentInfo pdfMetaData = pdfDoc.GetDocumentInfo();
pdfMetaData.SetAuthor("SOME STRING");
pdfMetaData.AddCreationDate();
pdfMetaData.GetProducer();
pdfMetaData.SetCreator("SOME STRING");
switch (type)
{
case PdfTypes.RefDoc:
pdfMetaData.SetKeywords("SOME STRING");
pdfMetaData.SetSubject("SOME STRING");
break;
case PdfTypes.PatientRoi:
pdfMetaData.SetKeywords("SOME STRING");
pdfMetaData.SetSubject("SOME STRING");
break;
case PdfTypes.RoiAdmin:
pdfMetaData.SetKeywords("SOME STRING");
pdfMetaData.SetSubject("SOME STRING");
break;
default:
break;
}
//Title is derived from html
// pdf conversion
ConverterProperties props = new ConverterProperties();
FontProvider fp = new FontProvider();
fp.AddStandardPdfFonts();
foreach (var path in FontPaths)
{
fp.AddFont(path);
}
props.SetFontProvider(fp);
DefaultTagWorkerFactory tagWorkerFactory = new AccessibilityTagWorkerFactory();
props.SetTagWorkerFactory(tagWorkerFactory);
HtmlConverter.ConvertToPdf(html, pdfDoc, props);
pdfDoc.Close();
}
}
EDIT 3:
Here is the AccessibilityTagWorkerFactory (keep in mind, the tables that I want to act like tables are not marked with the class, "make-table-div" and there shouldn't be affected by the customizations in this class:
public class AccessibilityTagWorkerFactory : DefaultTagWorkerFactory
{
public override ITagWorker GetCustomTagWorker(IElementNode tag, ProcessorContext context)
{
bool hasClass = false;
foreach (var attribute in tag.GetAttributes())
{
if (attribute.GetKey() == "class")
{
hasClass = true;
}
}
if (hasClass && tag.GetAttribute(AttributeConstants.CLASS).Contains("make-h1"))
{
return new HRoleSpanTagWorker(tag, context, StandardRoles.H1);
}
if (hasClass && tag.GetAttribute(AttributeConstants.CLASS).Contains("make-h2"))
{
return new HRoleSpanTagWorker(tag, context, StandardRoles.H2);
}
if (hasClass && tag.GetAttribute(AttributeConstants.CLASS).Contains("make-table-div"))
{
return new DivRoleTableTagWorker(tag, context);
}
return base.GetCustomTagWorker(tag, context);
}
}
After working with Jon Reilly on the iText team, this was the final solution that worked for me (column IDs and associated headers aren't needed... just Scope)
public class ThWithScopeTagWorker : ThTagWorker
{
public ThWithScopeTagWorker(IElementNode element, ProcessorContext context) : base(element, context)
{
}
public override void ProcessEnd(IElementNode element, ProcessorContext context)
{
base.ProcessEnd(element, context);
IPropertyContainer elementResult = base.GetElementResult();
if (elementResult is IAccessibleElement)
{
((IAccessibleElement)elementResult).GetAccessibilityProperties().SetRole(StandardRoles.TH);
//Can use this in the future in case we have th elements with different scope than "col"
string htmlScope = element.GetAttribute("scope"); //This is the scope="XXX" in your HTML
AccessibilityProperties properties = ((IAccessibleElement)elementResult).GetAccessibilityProperties();
//Could add "Row" if needed based on htmlScope string above.
//For my purposes, all th elements were scope="col"
properties.AddAttributes(new PdfStructureAttributes("Table").AddEnumAttribute("Scope", "Column"));
}
}
}
and this:
public class AccessibilityTagWorkerFactory : DefaultTagWorkerFactory
{
public override ITagWorker GetCustomTagWorker(IElementNode tag, ProcessorContext context)
{
//...
if (tag.Name() == "th")
{
return new ThWithScopeTagWorker(tag, context);
}
return base.GetCustomTagWorker(tag, context);
}
}
I need to add an attribute display-name to <td> with values as wicket id.
<tbody>
<tr wicket:id="result" class="bgalternate1">
<td wicket:id="values">
<span wicket:id="value">Value</span>
</td>
</tr>
</tbody>
Expected result is
<tbody>
<tr class="bgalternate1">
<td display-name="<attribute_name>"> <!--attribute is column header -->
<span wicket:id="value">Value</span>
</td>
</tr>
</tbody>
I have tried using below java code, however unable to achieve the desired result, i.e unable to append the attribute "display-name" to <td>. I have initially used SimpleAttributeModifier as it is deprecated, used AttributeModifier.
public ReportResultPanel(final String id, final IModel<AdHocReportSetup> model)
{
super(id, model);
setOutputMarkupId(true);
final IModel<Paging> pagingModel = new Model<Paging>(new Paging());
resultModel = createResultModel(model, pagingModel);
addResults(this, "result", resultModel);
}
private ListView<AdHocReportResult> addResults(final MarkupContainer parent,
final String id,
final IModel<ReportResultModel> model)
{
final IModel<List<AdHocReportResult>> resultsModel = new PropertyModel<List<AdHocReportResult>>(model, "results");
final ListView<AdHocReportResult> result = new ListView<AdHocReportResult>(id, resultsModel) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem(final ListItem<AdHocReportResult> item)
{
addResultValues(item, "values", item.getModel());
AdHocPage.applyParityClass(item.getIndex(), item);
final Behavior behavior = AttributeModifier.append("display-name", "red");
for (final Component next : item)
{
next.add(behavior);
}
}
};
parent.add(result);
return result;
}
private ListView<AdHocReportResultAttribute> addResultValues(final
MarkupContainer parent, final String id,
final IModel<AdHocReportResult> model)
{
final IModel<List<AdHocReportResultAttribute>> attributesModel = new PropertyModel<List<AdHocReportResultAttribute>>(
model, "attributes");
final ListView<AdHocReportResultAttribute> result = new ListView<AdHocReportResultAttribute>(id, attributesModel) {
private static final long serialVersionUID = 1L;
#Override
protected void populateItem(final ListItem<AdHocReportResultAttribute> item)
{
addValueLabel(item, "value", item.getModel());
}
};
parent.add(result);
return result;
}
You are adding the AttributeModifier to the ListView but you really need to add it to its ListItems.
...
item.add(AttributeModifier.append("display-name", "red"));
addValueLabel(item, "value", item.getModel());
...