help with custom tags

tan kian

Ranch Hand

Posts: 40

posted 11 years ago

hi all, my custom tag only prints out the last data from a collection, it never prints out the first few data. i did not code any doAfterBody(), everything is done in doStartTag(). what am i doing wrong?

You are saving all data items (i.e. the Iterator's elements) using the same attribute name (this.module). Each iteration of the while loop is saving the new data point to the same name, and thus overwriting the previous value of the attribute with the new data. As a result, only the last item of the Iterator will be available in the attribute with the name indicated by this.module().

You indicate that only the last item is printed out, but I am not seeing where you actually write the value out via the PageContext's JspWriter. Is your JSP referencing the attribute (via another tag, such as <cut>, perhaps) and writing it out?

Originally posted by tan kian: i am printing it out in the jsp page, which is as follows: <h2> Core Modules </h2> <table border="1"> <ass2:testtag module="core" /> <tr> <td>Name</td> <td>${pageScope.core}</td> </tr> </table>

That's why all you are getting is your last data item. As I mentioned in my previous reply, your tag is lopping through the collection you retrieved and storing each data item in the Page scoped Attribute named by the module attribute ("core" in the case of your example). However, pageContext.setAttribute(String, Object) (which is actually inherited from its super class JspContext) is a pure setter; it does not append new values to previously set values. So each iteration through your loop is overwriting the previous value. The end result is that only the last data item is stored in the attribute.

Instead, as you iterator through your iteration, you could write out that value. You can do that via the PageContext's JspWriter:

Note that I am using a simple Collection here (an ArrayList saved as "sampleData") for illustration purposes. You can substitute in your code to get the collection and then the iterator you need.

If we use the tag in a JSP page as follows:

The output is as follows:

Data Element OneData Element TwoData Element Three

Notice it is not formatted in a desirable way. If we had used the JspWriter.printLn(String) method instead of the JspWriter.write(String) method, each data element in the HMTL sent to the browser would be on a separate line, and the browser would render the line break as a single space, and we would have:

Data Element One Data Element Two Data Element Three

(See the API for the other differences between a write() method and a print()/println() method in a Writer.) But what if we want each Data Element on a separate line? We could add the necessary HTML markup in the tag's call to JspWriter.write()

This would gives us the output:

Data Element One Data Element Two Data Element Three

But this is a bad design. We now have HTML markup hard-coded into our custom tag. What if we want different markup, such as bold, or table cells? We have to change and then recompile out Tag class. And this still wouldn't allow us to use the tag in different places with different markup. So if in one JSP page we wanted the data elements in a line, on a second page on separate lines, and a third page bolded inside table cells, we couldn't do it. So the better way is to develop an iteration tag.

It is with an iteration tag that we would want to store the value in an attribute, like you were doing, so we can output its value, but we want to write each value before we place the next value in the attribute. Therefore, it is in the body of our tag that we want to write the value. Again, the below sample uses the sampleData list. I also use the tag attribute "id" (rather then the "module" you used) since id is an attribute defined in the TagSupport class that we are extending.

In our TLD, we need to define the body as a JSP body:

And in out JSP we use the tag as follows:

The resultant out put would look like this:Data Element OneData Element TwoData Element Three

To enhance our tag, instead of hard-coding the data set (i.e. the collection) to use (sampleData in my case, and the code you have to retrieve an iterator in your case), we could use an attribute to set that collection, and iterate through that collection. We could even define the scope of the attribute (either page, request, session, or application). But the funny thing is we are starting to reinvent the wheel here. The JSTL core taglib has a tag, <c:forEach>, that iterates through things for us.

Instead of storing the last data item in the attribute, we could store your collection:

Then use a <c:forEach> and a <cut> tag to iterate through the collection and output the results:

(I added a simple line break (<br>) after each item; you can of course do whatever formatting is necessary such as the table you had.)

Note that the "items" attribute of the <c:forEach> tag can take the following Java types: java.util.Collection, java.util.Iterator, java.util.Enumeration, java.util.Map, array of objects or primitive types, or a comma-separated String

And you don't necessary need to use a custom tag to set the collection as an attribute. Other server side code could do that.