Problem: "[action]" does not accept any expressions - struts2

I tried to use the following code :
<li>
<% for (int i=0; i<parentList.size(); i++) {
Role p = parentList.get(i);
%>
<li><%=p.getFuncname() %>
<ul>
<% for (int j=0; j<roleList.size(); j++) {
Role c = roleList.get(j);
if (!c.getFuncid().equalsIgnoreCase(c.getParentfunc()) && c.getParentfunc().equalsIgnoreCase(p.getFuncid()))
{
%>
<li><%=c.getFuncname() %>
<%
}
}
%>
</ul>
<% } %>
</li>
but it throw error:
JSPG0227E: Exception caught while translating /menu.jsp:
/menu.jsp(95,17) --> JSPG0124E: According to TLD or attribute directive in tag file, attribute "[action]" does not accept any expressions. Value of expression is: "[%=p.getFunclink() %]".
How can i fix it? Thanks!

The Struts tags cannot accept expressions, thus:
<s:url action="<%=p.getFunclink() %>"/>
is invalid.
First, avoid using scriptlets at all.
Second, look into using the <s:iterator/> tag to iterate over your collections.

Related

Passing SelectList through ViewData to editor templates - not displayed properly

It's a bit complicated so bear with me.
Let's say I've got an example of a controller edit action defined like:
Node nd = _repo.getNode(id);
List<Category> ac = new List<Category>();
ac.AddRange(_repo.getCategories());
SelectList acl = new SelectList(ac, "category_id", "category_name", ac.Where(cat => cat.category_id == nd.category_id).First());
ViewData["category_id"] = acl;
return View(nd);
The view is templated like so:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Myapp.Models.Node>" %>
<% if (ViewData.TemplateInfo.TemplateDepth > 1)
{ %>
<%= ViewData.ModelMetadata.SimpleDisplayText %>
<% }
else
{ %>
<table cellpadding="0" cellspacing="0" border="0">
<% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm)))
{ %>
<% if (prop.HideSurroundingHtml)
{ %>
<%= Html.Editor(prop.PropertyName) %>
<% }
else
{ %>
<tr>
<td>
<div class="editor-label" style="text-align: right;">
<%= prop.IsRequired ? "*" : ""%>
<%= Html.Label(prop.PropertyName)%>
</div>
</td>
<td>
<div class="editor-field">
<% if (ViewData.Keys.Contains(prop.PropertyName))
{
if ((ViewData[prop.PropertyName]).GetType().Name == "SelectList")
{ %>
<%= Html.DropDownList(prop.PropertyName, (SelectList)ViewData[prop.PropertyName])%>
<% }
else
{ %>
<%= Html.Editor(prop.PropertyName)%>
<% } %>
<% }
else
{ %>
<%= Html.Editor(prop.PropertyName)%>
<% } %>
<%= Html.ValidationMessage(prop.PropertyName, "*")%>
</div>
</td>
</tr>
<% } %>
<% } %>
</table>
<% } %>
So, what the template does is display a dropdown list for every property for which ViewData["property_name"] exists.
I've also defined DisplayName metadata attributes for every property of my Node class.
Now, the dropdown lists display fine and are being populated correctly, but:
The first value from a list is always selected, even though the SelectList selected value predicate is fine and does set a proper value (in the debugger at least).
Html.Label in the template returns a proper DisplayName for properties, but when I define a ViewData for them so as to display the dropdown list, the label resets to normal property name (ie. category_id instead of Category).
What gives? Can you think of any "neater" way to accomplish this functionality?
Allright, no one's answering so there's my answer, maybe it comes in handy for someone:
Do not use your property names for ViewData keys! It messes up with the view model, so your views get confused and start to behave strangely.
Actually, best avoid the magic strings mess entirely, but if you insist, just use something like ex.: ViewData[prop.PropertyName+"_list"]. Your views are going to be fine now.

How can I find all items beginning with a or â?

I have a list of items that are grouped by their first letter. By clicking a letter the user gets alle entries that begin with that letter.
This does not work for french. If I choose the letter a, items with â are not returned.
What is a good way to return items no matter if they have an accent or not?
<% char alphaStart = Char.Parse("A");
char alphaEnd = Char.Parse("Z"); %>
<% for (char i = alphaStart; i <= alphaEnd; i++) { %>
<% char c = i; %>
<% var abcList = Model.FaqList.Where(x => x.CmsHeader.StartsWith(c.ToString())).ToList(); %>
<% if (abcList.Count > 0 ) { %>
<div class="naviPkt">
<a id="<%= i.ToString().ToUpper() %>" class="naviPktLetter" href="#<%= i.ToString().ToLower() %>"><%= i.ToString().ToUpper() %></a>
</div>
<ul id="menuGroup<%= i.ToString().ToUpper() %>" class="contextMenu" style="display:none;">
<% foreach (var info in abcList) { %>
<li class="<%= info.CmsHeader%>">
<a id="infoId<%= info.CmsInfoId%>" href="#<%= info.CmsInfoId%>" class="abcEntry"><%= info.CmsHeader%></a>
</li>
<% } %>
</ul>
<% } %>
<% } %>
You can easily remove the diacritic signs (accents, umlaut, cedilla and so on) from the string to test only on the base character. This is done by normalizing the string to Unicode form D and removing the character of category "NonSpacingMark". The following extension method strips a string of its diacritic signs :
public static string RemoveDiacritics(this string s)
{
if (s == null)
throw new ArgumentNullException("s");
string formD = s.Normalize(NormalizationForm.FormD);
string noDiacriticsFormD = new string(
formD.Where(c => CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
.ToArray());
string noDiacritics = noDiacriticsFormD.Normalize(NormalizationForm.FormC);
return noDiacritics;
}
Using this method, your code becomes :
var abcList = Model.FaqList.Where(x => x.CmsHeader.RemoveDiacritics().StartsWith(c.ToString())).ToList();
Try using i.ToString().ToUpper(StringComparison.InvariantCultureIgnoreCase). It uses a culture neutral model for case conversion, which should eliminate accents.
In addition to LBushkin's answer (which I think will work), you can clean up your alphabet generation.
var A = Char.Parse("A");
var alphabet = Enumerable.Range(0, 26).Select(i => A + i);
Then you can use a foreach loop and forget the whole c = i thing. Also you don't need the .ToUpper() calls because you already have capital lettters.

Displaying ViewData in a View

What I need is to display Area_Name in various links within my view. Rather than repeat the same loop below each time, can I assign Area_Name to a variable and then display that variable in each link within my view?
ViewData
filterContext.Controller.ViewData["Categories"] = from m in _dataContext.Categories where m.Area_ID == SectionID select m;
View Page
Stylesheet
<%
foreach (var c in (IEnumerable<Categories>)ViewData["Categories"]) { %>
<link href="../../Content/<%= c.Area_Name %>/Site.css" rel="stylesheet" type="text/css" />
<% } %>
Image
<%
foreach (var c in (IEnumerable<Categories>)ViewData["Categories"]) { %>
<img src="../../Content/images/<%= c.Area_Name %>/slide1.jpg" />
<img src="../../Content/images/<%= c.Area_Name %>/slide2.jpg" />
<img src="../../Content/images/<%= c.Area_Name %>/slide3.jpg" />
<% } %>
perhaps create a strongly typed viewData file with a property for each AreaName that you have. The downside of this is that when you add a new category the viewData class and the view will not pick up those changes in the same way they will with your current code.
Use a second for-each or a for-loop?
Like this:
<% foreach (var c in (IEnumerable<Categories>)ViewData["Categories"]) {
for ( int i = 0 ; i < 100; i ++ )
{
%>
<img src="../../Content/images/<%= c.Area_Name %>/slide<%= i %>.jpg" />
<%
}
<% } %>
It's hard to know exactly what you mean though..

Modelbinding lists

I got a controller action like
public class Question {
public int Id { get;set; }
public string Question { get;set; }
public string Answer { get;set; }
}
public ActionResult Questions()
{
return View(GetQuestions());
}
public ActionResult SaveAnswers(List<Question> answers)
{
...
}
the view> looks like:
<% for (int i = 0; i < Model.Count; i++) { %>
<div>
<%= Html.Hidden(i.ToString() + ".Id") %>
<%= Model[i].Question %>
<%= Html.TextBox(i.ToString() + ".Answer") %>
</div>
<% } %>
Obviously this view doesn't work. I'm just not able access the list in the view.
The documentation for this also is outdated, it seem a lot of the functionality around modelbinding lists where changed in the beta.
I think that Scott Hanselman's post probably holds the answer. However it appears that you are trying to tie you view references to an anonymous object by returning in the post ...0.Answer=answer...
You should instead I believe be tying your fields to the `List answers refering to the answers[index].Answer.
Try the following:
<% for (int i = 0; i < Model.Count; i++) { %>
<div>
<%= Html.Hidden("answer["+i.ToString() + "].Id", Model["+i.ToString() + "].Id) %>
<%= Model[i].Question %>
<%= Html.TextBox("answer["+i.ToString() + "].Answer", Model["+i.ToString() + "].Answer) %>
</div>
<% } %>
Richard
Take a look at this and this question. Also this blog post.
Edit : As for accessing the model in the view. Are you sure you declared your with the following attribute?
<%# Page Language="C#"
Inherits="System.Web.Mvc.ViewPage<List<Namespace.Question>>" %>
//Assuming the GetQuestions() method returns a list of question objects.
the answer is not to use the html helpers.
<% for (int i = 0; i < Model.Count; i++) { %>
<div>
<input type="hidden" name="answers[<%= i %>].Id" id="answers_<%= i %>_Id" value="<%= Model[i].Id %>" />
<input type="text" name="answers[<%= i %>].Answer" id="answers_<%= i %>_Answer" value="<%= Model[i].Answer %>" />
</div>
<% } %>
Not very pretty, but works. The important thing is that Name and Id need to be different.
Name is allowed to have "[", "]" but id isn't.

In a View, What is the most efficient way to hide HTML related to a null value from the Model?

This is likely a very simple question with a straightforward answer but I'm something of a newbie when it comes to ASP.NET (MVC).
I am returning an address (in pieces) from my model. Some of components are null. Is there a simple or fluent-like way to check for that null value without a lot of extra code to determine whether or not to display the associated surrounding HTML (not just the value)?
Example:
<% foreach (var item in Model)
{ %>
<h3>
<%= Html.ActionLink(item.name, "Details", new { id = item.ID})%></h3>
<div>
<%= Html.Encode(item.address) %><br />
<%= Html.Encode(item.city) %>,
<%= Html.Encode(item.state) %>
<%= Html.Encode(item.zip) %>
</div>
<% } %>
In the above example, if there is a null value for item.address, I want the <br/> tag to be hidden as well so that only the city, state zip string is displayed.
I'm looking for something more elegant than just putting a <% if () { %> conditional out there. Thanks.
You could write an extension method for HtmlHelper that checked to see if it was null or not, and would output nothing if it was, or field + <br /> if it wasn't.
public static string FieldLine(this HtmlHelper helper, object value, bool addBr)
{
if (value == null)
{
return string.Empty;
}
else if (addBr)
{
return helper.Encode(value) + "<br />";
}
else
{
return helper.Encode(value);
}
}
Remember to import the namespace of your extension class into your View aspx. For this example, if my namespace was "MvcApplication1.Extensions", I would use
<%# Import Namespace="MvcApplication1.Extensions" %>
at the top of my View. Then to use it, it would simply be:
<%= Html.FieldLine(item.address, true) %>
<%= Html.FieldLine(item.city, true) %>
etc.
I'm adding another answer based on what womp described above.. I'd make the helper a bit more generic than he did, and still honor the origional Encode as well...
public static string EncodeWithHtml(this HtmlHelper helper, object value, string html)
{
if (value == null)
{
return string.Empty;
}
else
{
return helper.Encode(value) + html;
}
}
This would allow you to do something like:
<%= Html.EncodeWithHtml(item.address, "<br />") %>
or
<%= Html.EncodeWithHtml(item.address, "<img src=\"images\home.gif\"><br />") %>
Assuming item.address is a string...
<%= Html.Encode(item.address) %>
<% if (!string.IsNullOrEmpty(item.address)) { %>
<br />
<% } %>
of course, this is typed out in the little comment box, so be wary of spelling, case, etc, etc.

Resources