Re: Nested Java directives - best practice on how to make values available to inner directive?
Jonathan Revusky <[email protected]>
| Newsgroups | gmane.comp.web.freemarker.user |
|---|---|
| Message-ID | <[email protected]> |
Alistair Israel wrote:
> I've searched the mailing list archives up to a several months back
> but couldn't find anything on this.
FreeMarker 2.3.x has some weaknesses in the macro system that you're
hitting in your usage. If we ever get a stable 2.4.x release out, there
will be much better solutions to this.
>
> What I'm basically trying to do is write a custom "@form" directive in
> Java. Within the @form directive's body, I'd like to have various
> "@input" directives (also written in Java). As to why I would want to
> do this in Java rather than as a regular macro - I want the directives
> to insert Javascript validation code based on annotations on the form
> bean object.
>
> So, conceptually, I'd like to be able to say:
> <@form name="someForm" action="someAction">
> <@input type="text" property="name"/>
> </@form>
>
> I'd like to make the value of @form name ("someForm") available to the
> @input directive (so the @input directive can lookup the name from the
> template model, inspect the class, find annotations and so on and so
> forth.
>
> At first I tried adding a local variable via
> Environment.setLocalVariable() in my directive's execute(). This fails
> with "Not executing macro body".
Yes, well, the problem is that, within the nested body associated with a
macro, you are not actually within the macro -- at least from a variable
namespace resolution POV. The macro has yielded control back to the
context it was called from, so the above exception and message is
correct, as far as that goes.
>
> Delving through the code, it's apparent that while a custom macro
> directive has currentMacroContext (that lets us define/access local
> variables through the ".locals" special variable), a custom Java
> directive doesn't. Why is that so?
Well, mainly it's due to oversights in the design/implementation that is
mostly resolved in the bleeding edge 2.4.x code.
Talking at the level of writing the macros in FTL (though I understand
you are creating the directives in java code) there is the basic problem
that your name parameter passed into the form macro is not visible. This
has been addressed by a new built-in called scope. You could get at it
in FM 2.4 via form?scope["name"]. And you could get at this via java
code as so:
Macro form = (Macro) env.getCurrentScope().get("form");
Scope formScope = env.getMacroNamespace(form);
TemplateModel name = formScope.get("name");
or if you like to write dense, obfuscated code, you could do it one line:
env.getMacroNamespace((Macro)env.getCurrentScope().get("form")).get("name");
But anyway, none of this is available in the current stable 2.3.x release.
>
> So, I was able to make it work by using a global variable instead
> (Environment.setGlobalVariable()).
Well, I don't think you need this to be a global variable, i.e. visible
from all namespaces. Probably, you could use:
env.getCurrentNamespace().put("name", name);
and then
env.getCurrentNamespace().get("name")
to retrieve it.
Then you'd only be putting the variable in at the current template
namespace level, not global. And actually, if the form and input macros
are in a separate library that is imported, this solution is not so
terrible.
> The only thing that bothers me is
> that it really doesn't need to be a global variable - the scope of the
> value of the current form name is just that: the current form. I could
> conceptually overwrite or define a global variable that other
> templates depend upon for other purposes.
Yes, that's right. You're better just doing, as above, at the namespace
level. OTOH, it would be even better to be able to do it at the macro
level, but you can't really do that in 2.3.
>
> [ Aside: perhaps if there was some way to use namespaces to
> distinguish my directives (as well as any 'local' variables they need)
> it wouldn't bother me as much.
Well, if the macros are all defined in a separate library and you use
that namespace, as above, via env.getCurrentNamespace() that is
definitely better than using a global variable.
HTH,
JR
> However, using
> Configuration.setSharedVariable (via Spring's FreeMarkerConfigurer)
> only lets me provide a simple string name - not sure if any prefixes
> (e.g., "my.form") will be interpreted as a namespace. This, in fact,
> was a separate question I meant to ask the list. ]
>
> How would the members of the list suggest I do this - make a value
> local to my parent directive available to nested directives - short of
> explicitly passing them?
>
> - Alistair A. Israel
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/