4

I have a loop in my XSL template that loops through each "event" and displays certain information. Each event has a form associated with it.

I'd like to store some of the event information in variables so that I can pass it along with the form submission.

First, I tried to give the variable a simple name (e.g. "event_id"):

<xsl:for-each select="event">
    <div class="event">
        <div class="event_id">Event ID: <xsl:value-of select="@id" /></div>
        <xsl:variable name="event_id"><xsl:value-of select="@id" /></xsl:variable>
    </div>
    <form action="submit_label" method="post">
        <input type="hidden" name="event_id" value="{$event_id}"></input>
    </form>
</xsl:for-each>

But I get this error when the page gets to the form:

Could not compile stylesheet: file: .../war/jstl:: line 81: Variable or parameter 'event_id' is undefined.

Pastebin XSL code: http://pastebin.com/CUgqxptb
Pastebin error message: http://pastebin.com/EuW0aV71

I'm assuming this is because the variable and form are in a for loop, and names need to be unique. (Is that right?! -- UPDATE: this is not right). Although, confusingly, this does not produce the same error:

<form>
    <xsl:variable name="foo">bar</xsl:variable>
    <input type="text" name="{$foo}" placeholder="{$foo}"></input>
</form>

So, what I'm trying now is this, with the aim that I can reference the event variable in the form input field using a counter (i):

<xsl:variable name="i" value="0"></xsl:variable> 
<xsl:for-each select="event">
    <div class="event_id">Event ID: <xsl:value-of select="@id" /></div>
    <xsl:variable name="event_{$i}"><xsl:value-of select="@id" /></xsl:variable>

    <form action="submit_label" method="post">
        <input type="hidden" name="event_id" value="{$event_i}"></input>
    </form>

    $i++
</xsl:for-each>

(I know value="{$event_i}" doesn't make any sense, but hopefully you get the idea)

This gives me the following error about <xsl:variable name="event_{$i}">:

An attribute whose value must be a QName or whitespace-separated list of QNames had the value 'event_{$i}'.

I tried using <xsl:variable name=<fn:resolve-QName("event",$i) />>, but then I get this error:

Open quote is expected for attribute associated with an element type "name".

But, of course, adding quotes (<xsl:variable name="<fn:resolve-QName('event',$i) />">), gives me this error:

The value of attribute "name" associated with an element type "null" must not contain the '<' character.

So, any suggestions about how to get a hold of the event_id information in order to send it along with my form? Thanks!!!

UPDATE:
Progress! Thanks to @Lukas comment below (referencing https://stackoverflow.com/a/9261566/1590763), I'm now trying to declare the variables outside of the for-loop. This is working OK, but I think I'll need to do the variable business 5 different times for EACH event-level variable, which seems pretty bloated... Example here: http://pastebin.com/EuW0aV71

UPDATE #2:
Don't need to do the variable business 5 times. Just needed to move some div tags around. Thanks to @TimC for talking me through it. Final working version of the code:

<xsl:for-each select="event">
    <xsl:variable name="event_id"><xsl:value-of select="@id" /></xsl:variable>        
    <div class="event">
        <div class="event_id">Event ID: <xsl:value-of select="@id" /></div>
    </div>
    <form action="submit_label" method="post">
        <input type="hidden" name="event_id" value="{$event_id}"></input>
    </form>
</xsl:for-each>

Full code here: http://pastebin.com/gP06gYEg

Community
  • 1
  • 1
ems
  • 722
  • 1
  • 9
  • 16
  • 2
    Can you add the desired output? What is it you're trying to achieve here? – Ian McLaird Oct 25 '13 at 19:50
  • The original XSLT snippet you have shown shouldn't get an error. Are you sure you are not trying to reference the 'event_id' variable outside the xsl:for-each loop at some point? Perhaps posting a complete XSLT sample, if it is not too big, would help. (Or perhaps use http://pastebin.com/ if it is) – Tim C Oct 26 '13 at 09:21
  • @TimC, I added pastebin links to the XSL code and the error that's being generated. I'm pretty sure that I'm not referencing event_id outside of the loop. Let me know if you see something else wrong. Thanks! – ems Oct 26 '13 at 12:59

3 Answers3

3

As mentioned in the other questions, variables are local in scope, and only exist in the block in which they are defined. The problem you have is in your actual XSLT, shown in paste bin, the xsl:variable declaration is inside a div element. Your example should really have looked like this:

<xsl:for-each select="event">
    <div class="event_info">
       <div class="event_id">Event ID: <xsl:value-of select="@id" /></div>
       <xsl:variable name="event_id"><xsl:value-of select="@id" /></xsl:variable>
    </div>
    <form action="submit_label" method="post">
       <input type="hidden" name="event_id" value="{$event_id}"></input>
    </form>
</xsl:for-each>

This means the variable is only in scope inside the div element, so when you try to access it later on, it no longer exists.

The solution in your case, is to move the declaration to where it is in scope later on, namely outside the div element (It will still be scope for any nested elements within the main xsl:for-each loop

In your example, this should work.

<xsl:for-each select="event">
    <xsl:variable name="event_id"><xsl:value-of select="@id" /></xsl:variable>
    <div class="event_info">
       <div class="event_id">Event ID: <xsl:value-of select="@id" /></div>
    </div>
    <form action="submit_label" method="post">
       <input type="hidden" name="event_id" value="{$event_id}"></input>
    </form>
</xsl:for-each>
Tim C
  • 70,053
  • 14
  • 74
  • 93
  • Thanks! I had been thinking that the non-xsl tags were irrelevant to the xsl scope. Now I see that I should be less discriminatory. – ems Oct 26 '13 at 14:03
0

I'm not quite sure why you want i to be part of the variable name. How about this, instead:

<input type="hidden" name="event{position()}" value="{$event_id}"></input>
<!-- you didn't really define this variable either:   ^^^^^^^^^        -->

See also this related Stack Overflow question: Counter inside xsl:for-each loop

Community
  • 1
  • 1
Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
  • He never defined a variable named `event_id`, but I suspect that this is *very close* to what he wants. – Ian McLaird Oct 25 '13 at 20:01
  • 1
    @IanMcLaird: I know. I suspect the OP somehow thought that the `
    ` would magically generate such a variable
    – Lukas Eder Oct 25 '13 at 20:06
  • Sorry for the sloppy question. I'd been trying to get this to work for too long. Some things got lost in translation from the full code to a readable version for the post. I want i to be a part of the variable name because there will be multiple events and forms on the page, and each form needs to be associated with information from just one particular event. The input's name, doesn't seem too important--that can just be "event_id". But, the value of that input will depend on which event the form is connected with. So, I was thinking I could use i to keep track of the appropriate event. – ems Oct 25 '13 at 23:05
  • @ems: No worries, I might've sounded a bit rude, sorry. Variables declared within the `` loop are locally scope. They don't "escape" that scope. So you do not need to assign a new name to the variable for each loop run. This answer explains it nicely: http://stackoverflow.com/a/9261566/521799 – Lukas Eder Oct 26 '13 at 07:54
  • @LukasEder, Thanks for the link, I'd seen that post before, but didn't fully grasp the variable scope issue. I think this is why my code isn't working. I think this means I'll need to do the variable assignments with counters (see updated pastebin example). The number of events I display on the page will vary, but will never be more than 5. This seems like it will create some pretty bloated code, but I don't see any way around it. Let me know if you have suggestions. – ems Oct 26 '13 at 13:24
0

I haven't tested this, but I think you probably want something more like this:

<xsl:for-each select="event">
    <xsl:variable name="event_id"><xsl:value-of select="@id" /></xsl:variable>
    <div class="event_id">Event ID: <xsl:value-of select="$event_id" /></div>

    <form action="submit_label" method="post">
        <input type="hidden" name="event{position()}" value="{$event_id}"></input>
    </form>
</xsl:for-each>

There's really no need for $i in this case.

To answer some of your additional questions:

"I'm assuming this is because the variable is in a for loop, and names need to be unique. (Is that right?!)"

Not quite. Variable names do have to be unique, but that's not the problem here. The problem is that you can't use a template to build itself. Just as in C, this wouldn't be legal:

int i = 0;
int abc[i] = 3;  // or whatever other made-up syntax...point is you can't do this.

You're not allowed to create a variable name on the fly.

I also tried using <xsl:variable name=<fn:resolve-QName("event",$i) />>...

This stems from a similar problem. The XSL document must itself be a valid XML document, which means, among other things, that you can't have a tag inside a tag.

<this <is<not<legal>>>syntax</this>

A lot of template languages like ASP, PHP, JSP, and ColdFusion will let that fly. XSL won't.

Finally, it needs to be pointed out that variables in XSL aren't variable. It's probably the worst-named tag in the language. <xsl:variable> defines a constant. So your line

$i++

Doesn't actually change the value of $i. See this related question for a very detailed discussion about it.

Community
  • 1
  • 1
Ian McLaird
  • 5,507
  • 2
  • 22
  • 31
  • Thanks for your response! Clearly, I am just learning. I added some text to the question to try to clarify a bit. Unfortunately, your solution isn't working for me. When I try it, I have the same problem as my first attempt mentioned in the question. I still get `Could not compile stylesheet: file: .../war/jstl:: line 81: Variable or parameter 'event_id' is undefined.`, which is a reference to ``. I wasn't thinking of trying to increment the `input`'s name, but rather the `event_variable`'s name. – ems Oct 25 '13 at 22:58