<c:forEach var="expData" items="${expenseDataList}">
<tr>
<td>
<div class="custom-control custom-checkbox">
<label><aui:input
cssClass="custom-control-input expense select-all"
type="checkbox" data-amount="${expData.expenseAmount}" data-expenseid="${expData.expenseId}"
id="expenseCheckbox" name="expenseCheckbox" label="" />
</label>
</div>
</td>
....
<td>
<c:forEach var="previewUrl1" items="${previewUrl}">
<aui:button icon=" icon-download-alt"
style="border:none; background-color: #1E47C2; color:white"
data-previewurl="${previewUrl1}" cssClass="download"
name="downloadButton" id="downloadButton" />
</c:forEach>
</td>
</tr>
</c:forEach>
I am using inside and It is creating 2 download button because I am iterating another near to download button.. I am getting the value inside previewUrl1 but it is creating 2 button as it is iterating twice because inside another
This is my portlet side code
long fileEntryId = 0L;
String previewURL = StringPool.BLANK;
List<String> s1=new ArrayList<String>();
try {
for (int i = 0; i < expenseDataList.size(); i++) {
fileEntryId = expenseDataList.get(i).getFileEntryId();
if (fileEntryId > 0) {
FileEntry fileEntry = DLAppLocalServiceUtil.getFileEntry(fileEntryId);
previewURL = DLUtil.getPreviewURL(fileEntry, fileEntry.getFileVersion(), themeDisplay, StringPool.BLANK);
}
renderRequest.setAttribute("previewUrl", previewURL);
s1.add(previewURL);
LOG.info("File Entries"+fileEntryId);
LOG.info("Preview URl " + previewURL);
}
LOG.info(s1);
renderRequest.setAttribute("previewUrl", s1);
} catch (Exception e) {
e.printStackTrace();
}
I don't fully understand your description of what happens. But in case there are multiple iterations of your inner loop, you're generating repeated id attributes in two cases: In your outer loop for the aui:input, in your inner loop for the aui:button. According to the HTML rules, the id must be unique, or you'll get undefined behavior (or every element but the first one being ignored)
Edit:
In your code, you use renderRequest.setAttribute("previewUrl", previewURL);, e.g. you set exactly one previewURL. Later, you change that attribute to a list, through renderRequest.setAttribute("previewUrl", s1); - don't do that: It makes your code unmaintainable and tricks readers (like me or others here) into thinking that previewURL is a single value when indeed it's list.
That being said: It seems that you have two lists with the same number of entries, and each element in one list refers to the element with the same index in the other list. And if you nest those lists, you'll naturally show all elements of list 2 for each element of list 1. Thus: Don't nest loops. You'll need only one loop, and access the corresponding element from the other loop directly by index.
I don't have JSTL in my muscle memory: Within the remaining (outer) forEach, keep track of the index (through varStatus, see this question on how to do it) and use this index to access the matching element in your second list.
Related
Given the following table, where every other row has it display value set to none:
<table>
<thead>
<tr><th>Expand</th><th>Some Id</th><th>Some Attribute</th><th>Another Attribute</th></tr>
</thead>
<tbody>
<tr><td>V</td><td>101</td><td>fast</td><td>1/1/1</td></tr>
<tr style="display: none"><td>1</td><td>0</td><td>1</td><td>A Hidden Value 1011</td></tr>
<tr><td>V</td><td>102</td><td>fast</td><td>2/2/2</td></tr>
<tr style="display: none"><td>1</td><td>0</td><td>2</td><td>A Hidden Value 1022</td></tr>
<tr><td>V</td><td>103</td><td>fast</td><td>3/3/3</td></tr>
<tr style="display: none"><td>1</td><td>0</td><td>3</td><td>A Hidden Value 1033</td></tr>
<tr><td>V</td><td>104</td><td>fast</td><td>4/4/4</td></tr>
<tr style="display: none"><td>1</td><td>0</td><td>4</td><td>A Hidden Value 1044</td></tr>
</tbody>
</table>
When I use HtmlUnit with an xPath expression to retrieve all the rows:
HtmlPage page = webClient.getPage("http://localhost:4567/Home.html");
List<HtmlTableRow> rowsByXPath = page.getByXPath("//tbody/tr");
all of the rows are retrieved, however even though the rows that have their display value set to none are retrieved, the value of their cells are not available when using asText.
As an example if I attempt to print the values for the final cell in each row using the following code:
cells.forEach(cell -> System.err.print("[" + cell.asText() + "]"));
The output is:
[1/1/1][][2/2/2][][3/3/3][][4/4/4][]
However when I use asXml:
cells.forEach(cell -> System.err.print("[" + cell.asXml() + "]"));
The following output is obtained (modified for readability):
[<td>1/1/1</td>][<td>A Hidden Value 1011</td>]
[<td>2/2/2</td>][<td>A Hidden Value 1022</td>]
[<td>3/3/3</td>][<td>A Hidden Value 1033</td>]
[<td>4/4/4</td>][<td>A Hidden Value 1044</td>]
Is this the expected behaviour?
I expected the output from using the asText on the cells to be:
[1/1/1][A Hidden Value 1011][2/2/2][A Hidden Value 1022][3/3/3][A Hidden Value 1033][4/4/4][A Hidden Value 1044]
XPath is an XML feature and works without any HTML semantics. This implies that style attributes are not hiding any element from the tree.
Or the other way around - it is correct that you find the (hidden) elements when using XPath.
On the other hand methods like DomNode.asText() are working on the DOM tree in an HTML context - asText will return an empty string if the node is not visible.
In a case where a same element could change for a different id or name depending on many factors, I would be able to do an assertion on this element with accuracy.
Doest nighwatchjs permit to do an assertion based on a relative position like can do SAHI ? (Left of this element ..., Under a div, etc.)
I want to avoid Xpath solutions, it's based on the element type (div, id, name, etc.) and if I set it to all types:
//*[contains(text(),'hello world')]
I will get many occurrences and couldn't be able to know which one I'm trying to assert.
e.g : Running the same test on the same page, I would be able to find this "hello world" even if the div id changes or another element.
<div id="homebutton">
<p>
<a href=#>
<span name="hm">Home</span>
<a>
</p>
</div>
<div id=[0-9]>
<p>
<a href=#>
<span name="hw">hello world</span>
<a>
</p>
</div>
[...]
<div id=[0-9]>
<p>
<a href=#>
<span name="hw">hello world</span>
<a>
</p>
</div>
<div id="logoutbutton">
<p>
<a href=#>
<span name="lo">Logout</span>
<a>
</p>
</div>
Test example : Assert element containing string "hello world", not the one which is near the logout button but the one which is near the home button.
Expanding on my previous answer, you have two options, if the Hello World you want is *always the 2nd to last, appearing just before the Logout button then you want the 2nd to last of a type, you could use an xPath selector like this:
"//*[.='hello world'][last()-1]"
That's right in the Rosetta doc I shared with you, so you should know that by now
Another option is to get a collection of all matches. For that, I'd write a helper function like so:
module.exports = {
getCountOfElementsUseXpath : function (client, selector, value) {
// set an empty variable to store the count of elements
var elementCount;
// get a collection of all elements that match the passed selector
client.getEls(selector, function(collection) {
// set the variable to be that collection's length
elementCount = collection.length;
// log the count of elements to the terminal
console.log("There were " + elementCount + " question types")
return elementCount;
});
},
};
Then you can use that with some formula for how far your selector is from the last element.
The xpath selector "//div[contains(text(), 'hello world')]"
would match on both of the elements you've shown. If the element itself can change, you would use a wildcard: "//*[contains(text(), 'hello world')]"
For a match, on any element with that exact text:
"//*[.='hello world']"
A great source, a "Rosetta stone", for selector construction
To use an xpath selector with nightwatch:
"some test": function(client){
client
.useXpath().waitForElementPresent("//div[contains(text(), 'hello world')]", this.timeout)
}
The Xpath solution is okay but here is the solution I needed, more generic and giving many more options :
Using elements and manage to return an array of childrend elements
I choosed to return an array of objects with data matching my needs :
[{ id: webElementId, size: {width: 18, height: 35}, ...}, {id: webElementId, ...}, etc.]
With those informations, I can do many things:
Find an element with a specific text, attribute or cssproperty and
perform any action on it, like assertions or click on the right of it through a calculation of his size.
Mouse hover each elements matched (if you want to browse tabs with
submenus ul li / ol li)
More data is filled, more you can perform assertions.
Here is how the html starts
BUSINESS DOCUMENTATION
<p>Some company</p>
<p>
<p>DEPARTMENT: Legal Process</p>
<p>FUNCTION: Computer Department</p>
<p>PROCESS: Process Server</p>
<p>PROCEDURE: ABC Process Server</p>
<p>OWNER: Some User</p>
<p>REVISION DATE: 06/10/2013</p>
<p>
<p>OBJECTIVE: To ensure that the process server receive their invoices the following day.</p>
<p>
<p>WHEN TO PERFORM: Daily</p>
<p>
<p>WHO WILL PERFORM? Computer Team</p>
<p>
<p>TIME TO COMPLETE: 5 minutes</p>
<p>
<p>TECHNOLOGY REQUIREMENT(S): </p>
<p>
<p>SOURCE DOCUMENT(S): N/A</p>
<p>
<p>CODES AND DEFINITIONS: N/A</p>
<p>
<table border="1">
<tr>
<td>
<p>KPI’s: </p>
</td>
</tr>
</table>
<p>
<table border="1">
<tr>
<td>
<p>RISKS: </p>
</td>
</tr>
</table>
After this there is a whole bunch of text. What I need to do is from the above I need to parse out specific data.
I need to parse out the Department, Function, Process, Procedure. Objective, When to Perform, Who Will Perform, Time To Complete, Technology Requirements, Source Documents, Codes and Definitions, Risks.
I then need to delete this information from the Html column while leaving everything else in-tact. Is this possible in LINQ?
Here is the LINQ query I am using:
var result = (from d in IPACS_Documents
join dp in IPACS_ProcedureDocs on d.DocumentID equals dp.DocumentID
join p in IPACS_Procedures on dp.ProcedureID equals p.ProcedureID
where d.DocumentID == 4
&& d.DateDeleted == null
select d.Html);
Console.WriteLine(result);
This regex worked just fine for me on your input data
(DEPARTMENT|FUNCTION|OBJECTIVE):\s*(?<value>.+)\<
The result is multiple Matches with 2 groups each - the first the key and the second the value. I have only handled two cases, but you can add the rest easily enough.
To remove the information thus parsed, you can do a Regex.Replace with this regex
(?\(DEPARTMENT|FUNCTION|OBJECTIVE):\s*)(?.+)(?\)
and replacement string as
${start}${end}
leaving out value.
In code, this looks kinda like this (quickly typed this out in Notepad++ - may have minor errors).
private static readonly ParseDocRegex = new Regex(#"(?<start>\<p\>(?<name>DEPARTMENT|FUNCTION|OBJECTIVE):\s*)(?<value>.+)(?<end>\</p\>)", RegexOptions.ExplicitCaptured | RegexOptions.Compiled);
...
from html in result
let matches = findValuesRegex.Match(html)
where matches.Success
select new
{
namesAndValues = from m in matches.AsType<Match>()
select new KeyValuePair<string, string>(m.Groups["name"].Value, m.Groups["value"].Value),
strippedHtml = ParseDocRegex.Replace(html, "${start}${end}")
};
This ought to give you the desired output.
It can be done with many LINQ statements but using regular expressions you need only a few lines of code.
For HTML, you need an HTML parser. Try HTML Agility Pack or CsQuery.
Regular expressions can handle simple matches against HTML but are not sufficient for hierarchical structures and queries would be less precise.
Any HTML extraction is going to be fragile as the structure of the HTML charges. HTML is a presentation format and creators seldom care about machine interpretation. At least with a parser, you'll get an accurate model for the presentation markup (assuming it is valid HTML). You'll also get translation of entities into characters and the ability to extract all the descendant text of an element without internal markup elements like bold or italics.
You can use arbitrary assemblies in LINQPad simply by adding a reference, and for expression-based script, you can import designated namespaces automatically.
I have this requirement to iterate over 3 lists at the same time in jstl. for iterating over a single list we use
<c:forEach var = "mfgn" items = "${requestScope.mfgNumber}" varStatus = "status">
do something;
</c:forEach>
I need to do some thing like
<c:forEach var = "mfgn" var = "issue" items = "${requestScope.mfgNumber}" items = "${requestScope.something" varStatus = "status">
mfgNumber;
</c:forEach>
is this possible or there an otherway to iterate over multiple lists at the same time.
If they have the same size, then there are two options, assuming that it are List<Integer> and List<String>:
Merge them in a single list with entities which in turn repesents the items of each other list in a single class like List<ManfacturerIssue> where the ManfacturerIssue is a javabean class which contains Integer number and String issue properties. This way you can end up doing:
<c:forEach items="${mfgIssues}" var="mfgIssue">
${mfgIssue.number}, ${mfgIssue.issue}
</c:forEach>
Iterate by index instead, this is however ugly and unmaintainable as (fill in):
<c:forEach begin="0" end="${fn:length(mfgNumbers) - 1}" varStatus="loop">
${mfgNumbers[loop.index]}, ${issues[loop.index]}
</c:forEach>
I am trying to create a form that allows the editing of multiple rows of data. I have no problem looping through and getting input boxes to render...I just cannot get the name attributes to output correctly.
I know that in order to submit a collection you need to post back an indexed name where the index is sequential starting at 0.
<input name="Books[0].Title" />
<input name="Books[1].Title" />
and so on...
Now, I can get the EditorFor function to output my proper name with given the following loop code
#For n = 0 To (Model.Books.Count - 1)
#Html.EditorFor(Function(m) Model.Books.Item(n).Title)
Next
giving me
<input name="Books[0].Title" />
<input name="Books[1].Title" />
and so on...
My problem is VS shows the following warning
Using the iteration variable in a lambda expression may have unexpected results. Instead, create a local variable within the loop and assign it the value of the iteration variable.
Yet when I change the loop to
#For n = 0 To (Model.Books.Count - 1)
Dim item = Mode.Books.Item(n)
#Html.EditorFor(Function(m) item.Title)
Next
I get
<input name="$VB$Local_item.Title" />
<input name="$VB$Local_item.Title" />
and so on...
Any thoughts? Should I just ignore the warning?
Thanks.
Jason
MVC works by actually breaking apart the lambda expression, and seeing what it's made of. It doesn't just execute the lambda and get the result. So you need to actually use the model parameter in the lambda for it to work. This should do it for you:
#For n = 0 To (Model.Books.Count - 1)
Dim index = n
#Html.EditorFor(Function(m) m(index).Title)
Next