Some HTML/CSS to get around a drawback in Struts2 - html

I am trying to get an HTML page like: http://jsbin.com/awoco.
This is a JSP page so it will include scriptlets. Final HTML output will be kind of like this (tags unclosed to save space):
<%
Iterator it = MyList.iterator()
While (it.hasNext())
SomeClass all = it.next();
SomeClass a = it.next();
SomeClass b = it.next();
%>
<tr>
<td rowspan=3 valign=top>Red<td><%=all.Name()%><td><%=all.price()%><td><%=all.originalPrice()%>
</tr>
<tr>
<td><%=a.Name()%><td><%=a.price()%><td><%=a.originalPrice()%>
</tr>
<tr >
<td><%=b.Name()%><td><%=b.price()%><td><%=b.originalPrice()%>
</tr>
As you can see, I have to call next() 3 times inside the while loop. This is because the source of the data is a List populated that way, and I have to show the data in the exact same manner as in the link provided above.
Is there a way to change the HTML output somehow so that I don't have to call next() more than once, but still get the same table structure?

Change your data structures. MyList should be list of (something like) AgregateClass which will contain 3 members of type SomeClass (all,a,b) and then simply iterate through MyList.
Just few tips:
If you are using Struts2 you can use <s:iterator> tag to iterate through collections. documentation
Since JSP 2.0 or so you can write ${b.price} instead of <%=b.price()%>
You should change tags you added to your question as it has nothing to do with html or css.

Related

Access filter result Angular 6

How can I access filteredArray in my .ts component? Because right now it is accessible only inside ng-container.
<ng-container *ngIf="(userList | filter: 'name' : value) as filteredArray">
<tr *ngFor="let user of filteredArray">
<td>{{user.name}}</td>
<td>{{user.group}}</td>
</tr>
<div>Count: {{ filteredArray.length }}</div>
</ng-container>
How can I modify the code in order to obtain what I want? Thank you for your time!
To answer your question directly: it's not possible the way you describe it. But read on.
Pipes (sometimes still called "filters") should be used only to format data, i.e. prepare it in a human-readable form. For example, the build-in date pipe can be used to transform an ISO string to a string such as "March 21st, 1995", which is how a human from the USA might expect to read the date.
The way you're using pipes is not recommended, precisely because of the question you have. You've essentially put application logic inside a template, which is an anti-pattern and beats the purpose of having easy-to-read declarative templates, which Angular uses in order to figure out how to update DOM.
You should move the filtering logic back to the class. For example, instead of setting this.userList = xxx, you could have a function which you call every time, such as this.changeUserList(xxx).
changeUserList (list) {
this.userList = list
this.filteredArray = list.filter(...)
}
You can put this logic in a setter as well, which allows you to run custom code when you write the usual this.userList = list, but you'll need a separate (usually prefixed private) property on the class where you'd actually store the value. It's not really a limitation since you can also have a trivial getter, so you can still us this.userList normally as a getter without having to remember to use this._userList, essentially tucking this away as the get/set pair's implementation detail.
private _userList
public set userList (list) {
this._userList = list
this.filteredArray = list.filter(...)
}
public get userList (list) { return this._userList }
Observables could really come in handy here as well, since you could just rx.map the userList$ to filteredArray$ with an Array#filter.
public userList$
public filteredArray$ = this.userList$.pipe(map(arr => arr.filter(...))
Then in the template, you can use the async pipe.
*ngIf="filteredArray$ | async as filteredArray"
Avoid doing the following.... but it works for demo purposes 😃
Create a component (e.g. demo-element.component.ts) that takes a single #Input() value:any
Add this new component as the first child of the <ng-container>, and give it a template reference #containerRef e.g.:
<ng-container *ngIf="(userList | filter: 'name' : value) as filteredArray">
<demo-element #containerRef [value]="filteredArray"></demo-element>
In your main component, add
#ViewChild('containerRef') ref;
ngAfterViewInit() {
this.filteredArray = this.ref.value; // Terrible pattern, but answers the question:-)
}
I hope this below code will help you.
<div class="rsrvtn_blk" *ngIf="(items | fSearch:firstname) as filteredItems">
<div class="col-md-3 pl-0" *ngFor="let item of filteredItems">
// you can display the filtered content here
</div>
</div>

Adding Substring in table loop in Html.DisplayFor

Im just asking how to implement Substring code in Razor Html DisplayFor
Actually It was working before then I added the Substring to get the first 5 number in the table then I encountered an error
An exception of type 'System.InvalidOperationException' occurred in System.Web.Mvc.dll but was not handled in user code
Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
Here are my set of Codes:
<tbody>
#for (var i = 0; i < Model.Cards.Count; ++i)
{
var counter = i + 1;
<tr>
<td valign="middle">
<p class="small">#counter</p>
</td>
<td valign="middle">
<p class="small">#Html.DisplayFor(x => x.Cards[i].Number.Substring(0,5))</p>
</td>
</tr>
}
</tbody>
Thanks in Advance !
As Stephen said in comments, using .Substring in the middle of your view doesn't really make sens.
Instead, in your ViewModel (the one which defines your Number property), add the following property :
public string DisplayableNumber => Number.Length > 5 ? Number.ToString().Substring(0, 5) : "WhatYouWantHere";
This will work for C# 5+, else you can use the old way and initialize it in the default constructor.
Then, use it in your view :
#Html.DisplayFor(x => x.Cards[i].DisplayableNumber)
Plus, this is good practice to use this type of property in ViewModel, as Views should't handle any logic.

pandas.to_html() for elements in a list show new line characters

I have a bunch of pandas dataframes in a list that I need to convert to html tables. The html code for each individual dataframe looks good, however when I append the html to a list I end up with a bunch of \n characters showing on my webpage. Can anyone tell me how to get rid of them?
python code:
dataframe_html = []
table_dic = {}
for df in dataframes:
frame = df.to_html(classes="table table-hover")
dataframe_html.append(frame) #this is the line where all the \n get added
table_dic.update({'dataframe_html':dataframe_html})
return render(request,'InterfaceApp/FileProcessor_results.html',table_dic)
html code:
<div class="table-responsive">
{{ dataframe_html | safe }}
</div>
Shows up like this:
'
Can anyone help me out with this??
To display 3 separate tables, join the list of HTML strings into a single string:
dataframe_html = u''.join(dataframe_html)
for df in dataframes:
frame = df.to_html(classes="table table-hover")
dataframe_html.append(frame)
dataframe_html = u''.join(dataframe_html)
table_dic = {'dataframe_html':dataframe_html}
return render(request,'InterfaceApp/FileProcessor_results.html',table_dic)
FWIW this late in the game:
I had originally had:
response = render_template('table_display.html', query_results=[df_html], query_name='Quality Item Query')
and was getting an entire row of \n characters. Changed to below and the newlines disappeared.
response = render_template('table_display.html', query_results=df_html, query_name='Quality Item Query')
Even later in the game...
I stumbled upon this thread when having the same problem. Hopefully, the below helps someone.
I assigned the results of df.to_html() to a (nested) list and got the new lines when rendering in my Jinja template. The solution inspired by #AlliDeacon was to index the result again when rendering
Python code:
result[0][0] = df.to_html()
Jinja template:
<div>Table: {{ result[0][0][0] }}</div>
Refer output of the following code for illustration of the difference between a (sub-)list and a list element:
df = pd.DataFrame([['a','b']],
columns=['col_A', 'col_B'])
tmp = []
tmp.append(df.to_html(index=False))
print(tmp)
print(tmp[0])
Result:
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th>col_A</th>
<th>col_B</th>
</tr>
</thead>
<tbody>
<tr>
<td>a</td>
<td>b</td>
</tr>
</tbody>
</table>
Even more than late than late to the game. The issue with the newline characters is that the frame is by default in JSON when passed from your app to Jinja. The \n's are being read literally, but the table gets constructed from what follows inside the JSON string. There are two cases to deal with this, depending on the method of passing your dataframe:
Case 1: Passing a dataframe with render_template:
Pass the frame in your app without the "df.to_html". Within your html use the Jinja2 syntax to obtain a clean frame without any newline characters:
{{ df_html.to_html(classes="your-fancy-table-class") | safe }}
Case 2: Passing with json.dumps to be retrieved in JS
In case you send your frame via a response, e.g. post Ajax request, pass the frame with df.to_html() as you did:
#app.route('/foo', methods=['POST']) #load and return the frame route
def foo():
# df = pd.DataFrame...
return json.dumps({"response": df.to_html}
Then in your JS, load the clean frame without the newline characters in HTML from your response with:
JSON.parse(data).response;
$("#your-table-wrapper").html(response);

Using Perl LibXML to read textContent that contains html tags

If I have the following XML:
<File id="MyTestApp/app/src/main/res/values/strings.xml">
<Identifier id="page_title" isArray="0" isPlural="0">
<EngTranslation eng_indx="0" goesWith="-1" index="0">My First App</EngTranslation>
<Description index="0">Home page title</Description>
<LangTranslation index="0">My First App</LangTranslation>
</Identifier>
<Identifier id="count" isArray="0" isPlural="0">
<EngTranslation eng_indx="0" goesWith="-1" index="0">You have <b>%1$d</b> view(s)</EngTranslation>
<Description index="0">Number of page views</Description>
<LangTranslation index="0">You have <b>%1$d</b> view(s)</LangTranslation>
</Identifier>
</File>
I'm trying to read the 'EngTranslation' text value, and want to return the full value including any HTML tags. For example, I have the following:
my $parser = XML::LibXML->new;
my $dom = $parser->parse_file("test.xml") or die;
foreach my $file ($dom->findnodes('/File')) {
print $file->getAttribute("id")."\n";
foreach my $identifier ($file->findnodes('./Identifier')) {
print $identifier->getAttribute("id")."\n";
print encode('UTF-8',$identifier->findnodes('./EngTranslation')->get_node(1)->textContent."\n");
print encode('UTF-8',$identifier->findnodes('./Description')->get_node(1)->textContent."\n");
print encode('UTF-8',$identifier->findnodes('./LangTranslation')->get_node(1)->textContent."\n");
}
}
The output I get is:
MyTestApp/app/src/main/res/values/strings.xml
page_title
My First App
Home page title
My First App
count
You have %1$d view(s)
Number of page views
You have %1$d views
What I'm hoping to get is:
MyTestApp/app/src/main/res/values/strings.xml
page_title
My First App
Home page title
My First App
count
You have <b>%1$d</b> view(s)
Number of page views
You have <b>%1$d</b> views
I'm just using this as an example for a more complicated situation, hopefully it makes sense.
Thanks!
Here's a rather monkey patching solution, but it works:
sub XML::LibXML::Node::innerXML{
my ($self) = shift;
join '', $self->childNodes();
}
…
say $identifier->findnodes('./Description')->get_node(1)->innerXML;
Oh, and if the encoding becomes a problem, use the toString method, it's first argument handles encoding. (I did use open, but there were no out of range characters in the xml).
If you don't like the monkey patching. you can change the sub to a normal one and supply the argument, like this:
sub myInnerXML{
my ($self) = shift;
join '', map{$_->toString(1)} $self->childNodes();
}
…
say myInnerXML($identifier->findnodes('./Description')->get_node(1));
In your source XML, you either need to encode the tags as entities or wrap that content in a CDATA section.
One problem with embedding HTML in XML is that HTML is not necessarily 'well formed'. For example the <br> tag and the <img> tag are not usually followed by matching closing tags and without the closing tags, it would not be valid in an XML document unless you XML-escape the whole string of HTML, e.g.:
<EngTranslation eng_indx="0" goesWith="-1" index="0">You have <b>%1$d</b> view(s)</EngTranslation>
Or use a CDATA section:
<EngTranslation eng_indx="0" goesWith="-1" index="0"><![CDATA[You have <b>%1$d</b> view(s)]]></EngTranslation>
However, if you restrict your HTML to always be well-formed, you can achieve what you want with the toString() method.
If you called toString() on the <EngTranslation> element node, the output would include the <EngTranslation>...</EngTranslation> wrapper tags. So instead, you would need to call toString() on each of the child nodes and concatenate the results together:
binmode(STDOUT, ':utf8');
foreach my $file ($dom->findnodes('/File')) {
print $file->getAttribute("id")."\n";
foreach my $identifier ($file->findnodes('./Identifier')) {
print $identifier->getAttribute("id")."\n";
my $html = join '', map { $_->toString }
$identifier->findnodes('./EngTranslation')->get_node(1)->childNodes;
print $html."\n";
print $identifier->findnodes('./Description')->get_node(1)->textContent."\n";
print $identifier->findnodes('./LangTranslation')->get_node(1)->textContent."\n";
}
}
Note I took the liberty of using binmode to set UTF8 encoding on the output filehandle so it was not necessary to call encode for every print.

Concordion - how can i set a value into a table?

At some stage in a concordion test I have, I set a value to some random number.
This value is passed along, and later on , I have some output. The output is "checked" using the concordion "table" setup, where i compare an output value to what I expect it to be.
The problem I have is that I would like to check against the random number mentioned above. When I get the random number, I can set it to something Concordion can store -
<span c:set="#prefixValue">Bob</span>
Well, you get the idea - I replace "Bob" with a getter for my value.
But when I try to use it:
<table c:execute="#actual = getValue(#report, #xpath)">
<tr>
<th>Field name</th>
<th c:assertEquals="#actual">Value</th>
<th c:set="#xpath">Xpath</th>
</tr>
<tr>
<td>UnrelatedValue</td>
<td>SomeDeterminateValue</td>
<td>theXpathForThisToCompareTo</td>
</tr>
<tr>
<td>prefix</td>
<td><span c:echo="#prefixValue" /></td>
<td>differentXpathForThisToCompareTo</td>
</tr>
The whole shebang grinds to a halt, complaining that
"
Commands must be placed on elements when using 'execute' or 'verifyRows' commands on a <table>.
"
How can I use a pre-determined value in a table in Concordion?
The specs aren't supposed to have random elements in them. They're supposed to contain a specific example or set of examples. Rather than using a random number, hardcode a particular value and then you can use it later on.
To tell the fixture about the hardcoded value you can do this:
<span c:execute="setPrefixValue(#TEXT)">Bob</span>
Then in the fixture:
public void setPrefixValue(String prefixValue) {
// TODO: Whatever system configuration you need to do
}
If it isn't actually possible to set the value in your system under test, then use your fixture code to map between the hardcoded value and the actual random value.
public String getValue(String report, String xpath) {
String value = report(report).get(xpath);
if (value.equals(knownRandomValue)) {
return hardcodedValue;
}
return value;
}
Oh Lord! I didn't realise Concordion was going to get all rigid on me.
To fix, I had to alter the Concordion method I was using to be less stupidly rigid.
Following the general ideas here:
http://automatingsoftwaretesting.wordpress.com/2011/05/27/write-your-own-concordion-command-an-example-for-asserting/
I wrote this:
#Override
public void verify(CommandCall commandCall, Evaluator evaluator, ResultRecorder resultRecorder) {
Object expected = evaluator.evaluate(commandCall.getChildren().get(0).getExpression());
Object actual = evaluator.evaluate(commandCall.getExpression());
Element element = setupTheGoshDarnElement(commandCall, expected);
if (actual.equals(expected)) {
resultRecorder.record(Result.SUCCESS);
announceSuccess(element);
} else {
resultRecorder.record(Result.FAILURE);
announceFailure(element, expected.toString(), actual);
}
}
private Element setupTheGoshDarnElement(CommandCall commandCall, Object expected) {
Element e = commandCall.getElement();
e.appendText(expected.toString());
return e;
}
private void announceFailure(Element element, String expected, Object actual) {
listeners.announce().failureReported(new AssertFailureEvent(element, expected, actual));
}
private void announceSuccess(Element element) {
listeners.announce().successReported(new AssertSuccessEvent(element));
}
And so made a new "evaluator" ~ the rest of the evaluator is identical to the AssertEqualsCommand, if you're wondering.
This neatly checks the element and evaluates. Why concordion didn't do this ~ it isn't rocket science at all ~ is beyond me!
NOTE that I still need to use the "expanded table" in my question to get this to work, the short-form table has it's own issues with evaluating inner expressions.