I'm learning Struts 2 and stucking on converting a list of object to string.
I have class Movie which has a list of Genre (List<Genre>) as its property. I want to show genre names of a movie as a string (each genre is separated by a comma). I have converted other classes to String successfully but none of them are arrays or collections.
My first attempt was writing a custom converter class with method convertToString(Map map, Object obj) but it didn't work:
if (obj instanceof List) {
System.out.println("Found a list!");
StringBuilder results = null;
List list = (List) obj;
if (list != null && list.size() > 0) {
results = new StringBuilder();
// Genre List
if (list.get(0) instanceof Genre) {
System.out.println("Found a genre list!");
for (Object genre : list) {
results.append(((Genre) genre).getName() + ", ");
}
}
}
On JSP page:
<s:iterator value="movieList" status="movieStatus">
...
<td>
<s:property value="genres" />
</td>
...
</s:iterator>
Then I tried another way:
if (obj instanceof Genre) {
System.out.println("Found a genre!");
return ((Genre) obj).getName();
}
JSP:
<td>
<s:iterator value="genres" status="genreStatus">
<s:text name="genres[#genreStatus.index]" />,
</s:iterator>
</td>
or just this without converter:
<td>
<s:iterator value="genres" status="genreStatus">
<s:text name="genres[#genreStatus.index].name" />,
</s:iterator>
</td>
And it didn't work either.
In both cases, Struts didn't jump into any if clauses or report any errors (dev mode was on). It just showed Genre objects' memory addresses (the first case) or "genres[#genreStatus.index].name" (the second) on the browser.
What did I miss here? Any responses would be appreciated!
You don't need converter for that neither <s:iterator> tag. Just use OGNL projections.
<s:property value="genres.{name}" />
Related
I know this seems stupid but I can't find a way to make it work...
I generate a table from a db with this loop (simplified)
#foreach (var item in Model.Approvals)
{
<tr>
<td>#item.ApprovalReviewer</td>
.
.
.
.
<td>
<button asp-action="Approve" asp-route-id="#item.Key" asp-controller="Dashboard">Approve
</button>
</td>
<td><input asp-for="Approval.ChangeReviewer" value="#item.ChangeReviewer"/></td>
}
it looks like this, I can add a row and delete one, no issue there everything is working
so basically you enter a name in the reviewer input and when you press approve then that name is updated for the correct key. The issue I have is that I can't find a way to bind that input to the button.
[HttpPost]
public IActionResult Approve(int? key, Global obj)
{
var objToApprove = _db.ApprovalTable.Find(key);
objToApprove.ChangeApproved = true;
objToApprove.ChangeApprovalDate = DateTime.Now;
objToApprove.ChangeReviewer = obj.Approval.ChangeReviewer;
objToApprove.ChangeSigned = obj.Approval.ChangeReviewer;
_db.ApprovalTable.Update(objToApprove);
_db.SaveChanges();
return Redirect(Url.Action("ECR", new { id = obj.Approval.ECRNumber }) + "#2"); //TAB #2 FORM APPROVAL
}
what I need is the value of the input on the same row as the button... sounds easy...I guess not for me..
I don't know if this is the right way of doing it but I was able to solve my problem like this:
I replaced the Foreach loop to a regular loop so that each control would have its own index. Then I passed to the controller the index i so I could get the correct value in the controller.
So it looks like this:
#for (int i = 0; i < Model.Approvals.Count() - 1; i++)
{
The button where I pass the index i to the controller
<button class="---" asp-action="Approve" asp-route-key="#Model.Approvals[i].Key" asp-route-index="#i" asp-route-ecr="#Model.Approvals[i].ECRNumber" asp-controller="Dashboard">Approve</button>
and finally the controller where I can retrieve the variable
[HttpPost]
public IActionResult Approve(int? key, Global obj, int? ecr, int index)
{
var objToApprove = _db.ApprovalTable.Find(key);
objToApprove.ChangeApproved = true;
objToApprove.ChangeApprovalDate = DateTime.Now;
objToApprove.ChangeReviewer = obj.Approvals[index].ChangeReviewer;
objToApprove.ChangeSigned = obj.Approvals[index].ChangeReviewer;
_db.ApprovalTable.Update(objToApprove);
_db.SaveChanges();
return Redirect(Url.Action("ECR", new { id = ecr }) + "#2");
}
I have 2 weeks of experience in asp.net so it is hard to know if there is a much better and elegant way of doing this but it works. So it seems that after all I was not there for a "code writing service"...
I have a small Spring 4 application that receives data from a form, persists it in MariaDB, displays it on a webpage and if asked returns the data written into an xls (MS Excel) document using Apache POI.
The application works fine for English text.
Here are the commands I ran to create and populate the db table:
CREATE DATABASE testdb COLLATE 'utf16_general_ci';
use testdb;
create table testtable( id INTEGER PRIMARY KEY AUTO_INCREMENT, text1 VARCHAR(100), text2 VARCHAR(100), text3 VARCHAR(200));
INSERT INTO testtable (text1,text2,text3) VALUES ('אבג','דהו','זחט');
Here is the JSP page for displaying and making requests:
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-16">
</head>
<body>
<table border=1>
<tr><th>TEXT1</th><th>TEXT2</th><th>TEXT3</th></tr>
<c:forEach items="${rowList}" var="row">
<tr>
<td>${row.getText1()}</td>
<td>${row.getText2()}</td>
<td>${row.getText3()}</td>
</tr>
</c:forEach>
<tr><td><br><form method="GET" action="/clear"><input type="submit" value="CLEAR TABLE" /></form></td>
<td><br><form method="GET" action="/getxls"><input type="submit" value="download XLS file" /></form></td>
<td></td>
</tr>
</table>
<br>
<table border=1>
<tr><td>
<form method="POST" action="/add" accept-charset="UTF-16" >
<label>TEXT1</label>
<input type="text" name="text1" /><br>
<label>TEXT2</label>
<input type="text" " name="text2" /><br>
<label>TEXT3</label>
<input type="text" name="text3" />
<br>
<input type="submit" />
</td></tr>
</table>
</form>
</body>
</html>
And here is the single controller for the application:
#Controller
public class HomeController {
#Autowired
UserDao userDao;
#RequestMapping(value = "/", method = RequestMethod.GET)
public String index(ModelMap model){
model.addAttribute("rowList", userDao.getUserList());
return "home";
}
#RequestMapping(value = "/clear", method = RequestMethod.GET)
public String clearTable(){
userDao.deleteAllUsers();
return "home";
}
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addCoding(
#RequestParam("text1") String t1,
#RequestParam("text2") String t2,
#RequestParam("text3") String t3,
ModelMap model
) throws Exception{
User b = new User(java.net.URLDecoder.decode(t1,"UTF-16"), t2, t3);
userDao.addUser(b);
model.addAttribute("rowList", userDao.getUserList());
return "home";
}
#RequestMapping(value = "/getxls", method = RequestMethod.GET )
public void export1( HttpServletResponse response ) throws IOException {
List<User> users = userDao.getUserList();
Workbook wb = new HSSFWorkbook();
CreationHelper createHelper = wb.getCreationHelper();
Sheet sheet = wb.createSheet("new sheet");
// creating headers for data columns
Row row = sheet.createRow(0);
row.createCell(0).setCellValue("TEXT1");
row.createCell(1).setCellValue("TEXT2");
row.createCell(2).setCellValue("TEXT3");
int rowIndex = 1;
for(User user : users){
row = sheet.createRow(rowIndex);
row.createCell(0).setCellValue(createHelper.createRichTextString(user.getText1()));
row.createCell(1).setCellValue(createHelper.createRichTextString(user.getText2()));
row.createCell(2).setCellValue(user.getText3());
rowIndex++;
}
response.setHeader("Content-Disposition","attachment; filename=data.xls");
ServletOutputStream out = response.getOutputStream();
wb.write(out);
out.flush();
out.close();
}
}
The data object in the controller is called User but it really is just and int for db id and three Strings
Filling the form with the same Hebrew input as in the MariaDB console submitting it results in the following:
In all of the three result displays, contents of line 1 and 3 are the same. (Or at least the input was)
Line 1 was entered using MariaDB console.
Line 3 was entered in the web page.
How can I process the web request so in the xls file lines 1 and 3 are the same?
Also how can I properly display data of line 1 on the web page?
Correction to the JPS page:
1) adding
<%# page language="java" contentType="text/html; charset=UTF-8" pageEncoding="ISO-8859-1"%>
to the top of the document, this will properly display valid data pulled from the DB.
2) in the <form> rag, changing the value of attribute accept-charset from UTF-16 to ISO-8859-1. This will return Arabic and Hebrew characters in the form of &#xxxx; where each x is a decimal digit.
Correction to logic - turning the said &#xxxx; characters into proper Arabic/Hebrew characters:
The hebrew 'Aleph' = 'א' passed as א.
In java String.valueOf(Character.toChars(Integer.parseInt("1488", 10))); will return the proper 'א' character; so I added the following funcion to run on the input first thing in the controller:
public static String y(String txt){
String formatedString = "";
char[] charArr = txt.toCharArray();
for (int i=0; i<charArr.length; i++){
if(charArr[i] != '&')
formatedString += charArr[i];
else {
if (i+5 <= charArr.length && charArr[i+1] == '#'){
String temp = "";
for (int j=i+2;j<=i+5;j++)
temp += charArr[j];
if (temp.matches("-?\\d+(\\.\\d+)?")){
formatedString += String.valueOf(Character.toChars(Integer.parseInt(temp, 10)));
i=i+6;
}
} else
formatedString += charArr[i];
}
}
return formatedString;
}
Do not use utf-16. Use UTF-8, which is called utf8 or utf8mb4 inside MySQL.
The
On connecting you must specify the encoding in use by the client.
Columns in tables must be declared CHARACTER SET utf8 (or utf8mb4).
(Arabic and Hebrew are both handled.)
If you actually do have UTF-16 encoding at the client, then you can connect with utf16. But I recommend using utf8 or utf8mb4 in the tables. MySQL will convert on the fly.
How I can clean a string leaving only the plain text and the <a> elements?
Example:
<table><tr><td>Hello my web is Myweb, <span>Follow my blog!</span></td></tr></table>
Results:
Hello my web is Myweb, Follow my blog!
Thanks,
VERY VERY hacky (and really shouldn't be used productionally) but:
C#
Regex.Replace(input, #"<[^>]+?\/?>", m => {
// here you can exclude specific tags such as `<a>` or maybe `<b>`, etc.
return Regex.IsMatch(m.Value, #"^<a\b|\/a>$") ? m.Value : String.Empty;
});
Basically, it just takes out every HTML code with the exception of <a ...>...</a>.
Note: this DOES NOT:
Validate if a tag was opened/closed/nested correctly.
Validate if the <> are actually HTML tags (maybe your input has < or > in the text itself?)
Handle "nested" <> tags. (e.g. <img src="http://placeholde.it/100" alt="foo<Bar>"/> will leave a remainder of "/> in the output string)
Here's the same thing turned in to a helper method:
// Mocks http://www.php.net/strip_tags
/// <summary>
/// Removed all HTML tags from the string and returned the purified result.
/// If supplied, tags matching <paramref name="allowedTags"/> will be left untouched.
/// </summary>
/// <param name="input">The input string.</param>
/// <param name="allowedTags">Tags to remain in the original input.</param>
/// <returns>Transformed input string.</returns>
static String StripTags(String input, params String[] allowedTags)
{
if (String.IsNullOrEmpty(input)) return input;
MatchEvaluator evaluator = m => String.Empty;
if (allowedTags != null && allowedTags.Length > 0)
{
Regex reAllowed = new Regex(String.Format(#"^<(?:{0})\b|\/(?:{0})>$", String.Join("|", allowedTags.Select(x => Regex.Escape(x)).ToArray())));
evaluator = m => reAllowed.IsMatch(m.Value) ? m.Value : String.Empty;
}
return Regex.Replace(input, #"<[^>]+?\/?>", evaluator);
}
// StripTags(input) -- all tags are removed
// StripTags(input, "a") -- all tags but <a> are removed
// StripTags(input, new[]{ "a" }) -- same as above
This code will remove all tags but <a> tag.
Regex r = new Regex(#"(?!</a>)(<\w+>|</\w+>)");
var removedTags = r.Replace(inputString, "");
First off you can't use regex's to parse html
just do a global replace on something like </?table>|</?tr>|</?td> with any other tags you don't want and replace them with the empty string "".
Can someone please help me with this problem? At the bottom of my view just before the page loads I create an array of checkboxes like this:
foreach (var course in courses.Select((x, i) => new { Data = x, Index = i }))
{
int currentIndex = course.Index;
String selectedday = "";
String selectedteacher = "";
if (cnt++ % 4 == 0)
{
#: </tr> <tr>
}
#: <td >
<br /><br /><br />
<input type="checkbox" id="checkbox"
name="selectedCourses"
value="#course.Data.CourseID"
#(Html.Raw(course.Data.Assigned ? "checked=\"checked\"" : ""))
/>
I use the same loop to add the assigned state to a list like this:
bool theNewString=course.Data.Assigned ;
String a=theNewString.ToString();
assignedCourses.Add(a);
I defined a list variable at the top of the page so that it can be accessed by the form-wide like this:
#{List<String> assignedCourses =new List<String>(); }
Now I want to send that variable to the controller and this is where things get muddy. If I send a string like this it works fine:
Razor markup
String postedData = "literalString";
#using (Html.BeginForm("Action", "Controller", new { assigned = # postedData }))
Action
[HttpPost]
public ActionResult Edit(int id,List<String> assigned){}
Now if I try this:
#{List<String> assignedCourses =new List<String>(); }
#using (Html.BeginForm("Action", "Controller", new { assigned = #assignedCourses }))
And nothing comes through to the controller. It’s like the list is emptied just before posting. How can send my list to the controller?
I think the way you are trying to create the query string/postback url for the form using the list is wrong and will not produce a properly formatted string.
Maybe try something like this:
#{
List<string> ExampleList = new List<string>();
ExampleList.Add("True");
ExampleList.Add("False");
string param = string.Join("&", ExampleList.ToArray());
}
#using (Html.BeginForm("index", "home", new { #someparam = (new HtmlString(param)) })) { }
This would require you to split the single string up into an array when the action method receives it.
alternatively you can try using RouteValueDictionary instead of list, but this does not support duplicate keys.
Also for checkbox creation try using #Html.CheckBox() as this will automatically create a second hidden input set to false. HTML Forms don't post back checkbox values when they are unchecked, so the second hidden input with the same name will be posted back and you will know it has been unchecked or not checked. MVC will interpret values of "True, False" for the same form key/input name as True and False when just the hidden input with the "False" value is posted back.
For example, here in StackOverflow you can se a top menu with the options: Questions, Tags, Users, Badges, Unanswered and Ask Question. When you are in one of those sections, it is highlighted in orange.
What is the best way to achieve that in ASP.NET MVC?
So far, and as proof of concept, I have done this helper:
public static String IsCurrentUrl(this UrlHelper url, String generatedUrl, String output)
{
var requestedUrl = url.RequestContext.HttpContext.Request.Url;
if (generatedUrl.EndsWith("/") && !requestedUrl.AbsolutePath.EndsWith("/"))
generatedUrl=generatedUrl.Substring(0, generatedUrl.Length - 1);
if (requestedUrl.AbsolutePath.EndsWith(generatedUrl))
return output;
return String.Empty;
}
That method add the output string to the element if the current request match that link. So it can be used like this:
<li>
About Us</span>
</li>
First problem, I am basically calling twice to Url.Action, first for the "href" attribute, and after in the helper, and I think there has to be a better way to do this. Second problem, that is not the best way to compare two links. I think I could create a new Html.ActionLink overload so I don't need to call the Url.Action twice, but is there any buil-in way to do this?
Bonus: if I add "class=\"on\"", MVC renders class=""on"". Why?
Regards.
For a project that i'm working on we've had the exact same problem. How to highlight the current tab? This is the approach that was taken at the time:
In the master page view:
<%
var requestActionName =
ViewContext.RouteData.Values["action"].ToString();
var requestControllerName =
ViewContext.RouteData.Values["controller"].ToString();
%>
<li class="<%= requestActionName.Equals("Index",
StringComparison.OrdinalIgnoreCase)
&& requestControllerName.Equals("Home",
StringComparison.OrdinalIgnoreCase) ?
"current" : string.Empty %>">
<%: Html.ActionLink("Home", "Index", "Home") %>
</li>
Basically what's happening is that we're just string comparing the action and controller values with values associated with a link. If they match, then we're calling that the current link, and we assign a 'current' class to the menu item.
Now so far, this works, but as we've gotten bigger in size, this setup starts to get pretty large with a whole lot of 'or' this 'or' that. So keep that mind if you decide to try this.
Good luck, and hope this helps you out some.
Do it using CSS. On the server, create a function to identify the section of the site that should be highlighted and output that in your body tag as a css class:
This articles explains it:
http://hicksdesign.co.uk/journal/highlighting-current-page-with-css
Another way is to use an extension method like this (Razor and C# in example):
#Html.MenuItem("MainPage","Index", "Home")
method:
public static MvcHtmlString MenuItem(
this HtmlHelper htmlHelper,
string linkText,
string actionName,
string controllerName
)
{
string currentAction = htmlHelper.ViewContext.RouteData.GetRequiredString("action");
string currentController = htmlHelper.ViewContext.RouteData.GetRequiredString("controller");
if (actionName == currentAction && controllerName == currentController)
{
return htmlHelper.ActionLink(
linkText,
actionName,
controllerName,
null,
new
{
#class = "current"
});
}
return htmlHelper.ActionLink(linkText, actionName, controllerName);
}
Not sure about the first bit, but for the bonus:
\ is an escape character in C# (and most languages, for that matter), and it will cause the next character to be interpreted as a string, rather than a C# operator.